diff options
Diffstat (limited to 'src')
585 files changed, 23909 insertions, 14841 deletions
diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index 1bf2b16ed6..3c8ca8ab45 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -585,7 +585,7 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer, "class Service : public ::grpc::SynchronousService {\n" " public:\n"); printer->Indent(); - printer->Print("Service() : service_(nullptr) {}\n"); + printer->Print("Service();\n"); printer->Print("virtual ~Service();\n"); for (int i = 0; i < service->method_count(); ++i) { PrintHeaderServerMethodSync(printer, service->method(i), vars); @@ -594,7 +594,7 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer, printer->Outdent(); printer->Print( " private:\n" - " ::grpc::RpcService* service_;\n"); + " std::unique_ptr< ::grpc::RpcService> service_;\n"); printer->Print("};\n"); // Server side - Asynchronous @@ -1014,8 +1014,10 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, "{}\n\n"); printer->Print(*vars, + "$ns$$Service$::Service::Service() {\n" + "}\n\n"); + printer->Print(*vars, "$ns$$Service$::Service::~Service() {\n" - " delete service_;\n" "}\n\n"); for (int i = 0; i < service->method_count(); ++i) { (*vars)["Idx"] = as_string(i); @@ -1026,10 +1028,10 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, "::grpc::RpcService* $ns$$Service$::Service::service() {\n"); printer->Indent(); printer->Print( - "if (service_ != nullptr) {\n" - " return service_;\n" + "if (service_) {\n" + " return service_.get();\n" "}\n"); - printer->Print("service_ = new ::grpc::RpcService();\n"); + printer->Print("service_ = std::unique_ptr< ::grpc::RpcService>(new ::grpc::RpcService());\n"); for (int i = 0; i < service->method_count(); ++i) { const grpc::protobuf::MethodDescriptor *method = service->method(i); (*vars)["Idx"] = as_string(i); @@ -1077,7 +1079,7 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); } } - printer->Print("return service_;\n"); + printer->Print("return service_.get();\n"); printer->Outdent(); printer->Print("}\n\n"); } diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc index a3157db0fb..ff092053ad 100644 --- a/src/compiler/objective_c_generator.cc +++ b/src/compiler/objective_c_generator.cc @@ -203,6 +203,7 @@ void PrintMethodImplementations(Printer *printer, printer.Print( "- (instancetype)initWithHost:(NSString *)host" " NS_DESIGNATED_INITIALIZER;\n"); + printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n"); printer.Print("@end\n"); } return output; @@ -239,6 +240,9 @@ void PrintMethodImplementations(Printer *printer, printer.Print(" packageName:(NSString *)packageName\n"); printer.Print(" serviceName:(NSString *)serviceName {\n"); printer.Print(" return [self initWithHost:host];\n"); + printer.Print("}\n\n"); + printer.Print("+ (instancetype)serviceWithHost:(NSString *)host {\n"); + printer.Print(" return [[self alloc] initWithHost:host];\n"); printer.Print("}\n\n\n"); for (int i = 0; i < service->method_count(); i++) { diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc index fe2b9fad99..83133f2b6e 100644 --- a/src/compiler/python_generator.cc +++ b/src/compiler/python_generator.cc @@ -339,7 +339,7 @@ bool PrintAlphaServerFactory(const grpc::string& package_qualified_service_name, } out->Print("}\n"); out->Print( - "return implementations.server(" + "return early_adopter_implementations.server(" "\"$PackageQualifiedServiceName$\"," " method_service_descriptions, port, private_key=private_key," " certificate_chain=certificate_chain)\n", @@ -422,7 +422,7 @@ bool PrintAlphaStubFactory(const grpc::string& package_qualified_service_name, } out->Print("}\n"); out->Print( - "return implementations.stub(" + "return early_adopter_implementations.stub(" "\"$PackageQualifiedServiceName$\"," " method_invocation_descriptions, host, port," " metadata_transformer=metadata_transformer, secure=secure," @@ -586,13 +586,13 @@ bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name, "Constructor", name_and_implementation_constructor->second); } out->Print("}\n"); - out->Print("server_options = beta.server_options(" + out->Print("server_options = beta_implementations.server_options(" "request_deserializers=request_deserializers, " "response_serializers=response_serializers, " "thread_pool=pool, thread_pool_size=pool_size, " "default_timeout=default_timeout, " "maximum_timeout=maximum_timeout)\n"); - out->Print("return beta.server(method_implementations, " + out->Print("return beta_implementations.server(method_implementations, " "options=server_options)\n"); } return true; @@ -685,13 +685,13 @@ bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name, "Cardinality", name_and_cardinality->second); } out->Print("}\n"); - out->Print("stub_options = beta.stub_options(" + out->Print("stub_options = beta_implementations.stub_options(" "host=host, metadata_transformer=metadata_transformer, " "request_serializers=request_serializers, " "response_deserializers=response_deserializers, " "thread_pool=pool, thread_pool_size=pool_size)\n"); out->Print( - "return beta.dynamic_stub(channel, \'$PackageQualifiedServiceName$\', " + "return beta_implementations.dynamic_stub(channel, \'$PackageQualifiedServiceName$\', " "cardinalities, options=stub_options)\n", "PackageQualifiedServiceName", package_qualified_service_name); } @@ -701,9 +701,9 @@ bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name, bool PrintPreamble(const FileDescriptor* file, const GeneratorConfiguration& config, Printer* out) { out->Print("import abc\n"); - out->Print("from $Package$ import beta\n", + out->Print("from $Package$ import implementations as beta_implementations\n", "Package", config.beta_package_root); - out->Print("from $Package$ import implementations\n", + out->Print("from $Package$ import implementations as early_adopter_implementations\n", "Package", config.early_adopter_package_root); out->Print("from grpc.framework.alpha import utilities as alpha_utilities\n"); out->Print("from grpc.framework.common import cardinality\n"); diff --git a/src/core/README.md b/src/core/README.md index 407dc4f701..0d8c0d5bd9 100644 --- a/src/core/README.md +++ b/src/core/README.md @@ -5,5 +5,4 @@ Python, PHP, NodeJS, Objective-C) are layered on top of this library. #Status -Alpha : Ready for early adopters - +Beta diff --git a/src/core/census/context.h b/src/core/census/context.h index d43a69f7e5..d9907d4da7 100644 --- a/src/core/census/context.h +++ b/src/core/census/context.h @@ -41,9 +41,9 @@ struct census_context { gpr_uint64 op_id; /* Operation identifier - unique per-context */ gpr_uint64 trace_id; /* Globally unique trace identifier */ - /* TODO(aveitch) Add census tags: - const census_tag_set *tags; - */ + /* TODO(aveitch) Add census tags: + const census_tag_set *tags; + */ }; #endif /* GRPC_INTERNAL_CORE_CENSUS_CONTEXT_H */ diff --git a/src/core/census/grpc_context.c b/src/core/census/grpc_context.c index 429f3ec9db..4b61382a2c 100644 --- a/src/core/census/grpc_context.c +++ b/src/core/census/grpc_context.c @@ -33,9 +33,12 @@ #include <grpc/census.h> #include <grpc/grpc.h> +#include "src/core/surface/api_trace.h" #include "src/core/surface/call.h" void grpc_census_call_set_context(grpc_call *call, census_context *context) { + GRPC_API_TRACE("grpc_census_call_set_context(call=%p, census_context=%p)", 2, + (call, context)); if (census_enabled() == CENSUS_FEATURE_NONE) { return; } @@ -45,5 +48,6 @@ void grpc_census_call_set_context(grpc_call *call, census_context *context) { } census_context *grpc_census_call_get_context(grpc_call *call) { + GRPC_API_TRACE("grpc_census_call_get_context(call=%p)", 1, (call)); return (census_context *)grpc_call_context_get(call, GRPC_CONTEXT_TRACING); } diff --git a/src/core/census/grpc_filter.c b/src/core/census/grpc_filter.c index e01c9a2ad4..872543057e 100644 --- a/src/core/census/grpc_filter.c +++ b/src/core/census/grpc_filter.c @@ -36,7 +36,6 @@ #include <stdio.h> #include <string.h> -#include "include/grpc/census.h" #include "src/core/channel/channel_stack.h" #include "src/core/channel/noop_filter.h" #include "src/core/statistics/census_interface.h" @@ -49,65 +48,67 @@ typedef struct call_data { census_op_id op_id; - census_context* ctxt; + census_context *ctxt; gpr_timespec start_ts; int error; /* recv callback */ - grpc_stream_op_buffer* recv_ops; - grpc_iomgr_closure* on_done_recv; + grpc_stream_op_buffer *recv_ops; + grpc_closure *on_done_recv; } call_data; typedef struct channel_data { - grpc_mdstr* path_str; /* pointer to meta data str with key == ":path" */ + grpc_mdstr *path_str; /* pointer to meta data str with key == ":path" */ } channel_data; -static void extract_and_annotate_method_tag(grpc_stream_op_buffer* sopb, - call_data* calld, - channel_data* chand) { - grpc_linked_mdelem* m; +static void extract_and_annotate_method_tag(grpc_stream_op_buffer *sopb, + call_data *calld, + channel_data *chand) { + grpc_linked_mdelem *m; size_t i; for (i = 0; i < sopb->nops; i++) { - grpc_stream_op* op = &sopb->ops[i]; + grpc_stream_op *op = &sopb->ops[i]; if (op->type != GRPC_OP_METADATA) continue; for (m = op->data.metadata.list.head; m != NULL; m = m->next) { if (m->md->key == chand->path_str) { gpr_log(GPR_DEBUG, "%s", - (const char*)GPR_SLICE_START_PTR(m->md->value->slice)); + (const char *)GPR_SLICE_START_PTR(m->md->value->slice)); /* Add method tag here */ } } } } -static void client_mutate_op(grpc_call_element* elem, - grpc_transport_stream_op* op) { - call_data* calld = elem->call_data; - channel_data* chand = elem->channel_data; +static void client_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; if (op->send_ops) { extract_and_annotate_method_tag(op->send_ops, calld, chand); } } -static void client_start_transport_op(grpc_call_element* elem, - grpc_transport_stream_op* op) { +static void client_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op *op) { client_mutate_op(elem, op); - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); } -static void server_on_done_recv(void* ptr, int success) { - grpc_call_element* elem = ptr; - call_data* calld = elem->call_data; - channel_data* chand = elem->channel_data; +static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr, + int success) { + grpc_call_element *elem = ptr; + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; if (success) { extract_and_annotate_method_tag(calld->recv_ops, calld, chand); } - calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); + calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success); } -static void server_mutate_op(grpc_call_element* elem, - grpc_transport_stream_op* op) { - call_data* calld = elem->call_data; +static void server_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; if (op->recv_ops) { /* substitute our callback for the op callback */ calld->recv_ops = op->recv_ops; @@ -116,56 +117,65 @@ static void server_mutate_op(grpc_call_element* elem, } } -static void server_start_transport_op(grpc_call_element* elem, - grpc_transport_stream_op* op) { - call_data* calld = elem->call_data; +static void server_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; GPR_ASSERT((calld->op_id.upper != 0) || (calld->op_id.lower != 0)); server_mutate_op(elem, op); - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); } -static void client_init_call_elem(grpc_call_element* elem, - const void* server_transport_data, - grpc_transport_stream_op* initial_op) { - call_data* d = elem->call_data; +static void client_init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + call_data *d = elem->call_data; GPR_ASSERT(d != NULL); + memset(d, 0, sizeof(*d)); d->start_ts = gpr_now(GPR_CLOCK_REALTIME); if (initial_op) client_mutate_op(elem, initial_op); } -static void client_destroy_call_elem(grpc_call_element* elem) { - call_data* d = elem->call_data; +static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + call_data *d = elem->call_data; GPR_ASSERT(d != NULL); /* TODO(hongyu): record rpc client stats and census_rpc_end_op here */ } -static void server_init_call_elem(grpc_call_element* elem, - const void* server_transport_data, - grpc_transport_stream_op* initial_op) { - call_data* d = elem->call_data; +static void server_init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + call_data *d = elem->call_data; GPR_ASSERT(d != NULL); + memset(d, 0, sizeof(*d)); d->start_ts = gpr_now(GPR_CLOCK_REALTIME); /* TODO(hongyu): call census_tracing_start_op here. */ - grpc_iomgr_closure_init(d->on_done_recv, server_on_done_recv, elem); + grpc_closure_init(d->on_done_recv, server_on_done_recv, elem); if (initial_op) server_mutate_op(elem, initial_op); } -static void server_destroy_call_elem(grpc_call_element* elem) { - call_data* d = elem->call_data; +static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + call_data *d = elem->call_data; GPR_ASSERT(d != NULL); /* TODO(hongyu): record rpc server stats and census_tracing_end_op here */ } -static void init_channel_elem(grpc_channel_element* elem, grpc_channel* master, - const grpc_channel_args* args, grpc_mdctx* mdctx, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { - channel_data* chand = elem->channel_data; + channel_data *chand = elem->channel_data; GPR_ASSERT(chand != NULL); - chand->path_str = grpc_mdstr_from_string(mdctx, ":path", 0); + chand->path_str = grpc_mdstr_from_string(mdctx, ":path"); } -static void destroy_channel_elem(grpc_channel_element* elem) { - channel_data* chand = elem->channel_data; +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + channel_data *chand = elem->channel_data; GPR_ASSERT(chand != NULL); if (chand->path_str != NULL) { GRPC_MDSTR_UNREF(chand->path_str); diff --git a/src/core/channel/channel_args.c b/src/core/channel/channel_args.c index 54ee75af28..487db1119a 100644 --- a/src/core/channel/channel_args.c +++ b/src/core/channel/channel_args.c @@ -132,7 +132,7 @@ grpc_compression_algorithm grpc_channel_args_get_compression_algorithm( for (i = 0; i < a->num_args; ++i) { if (a->args[i].type == GRPC_ARG_INTEGER && !strcmp(GRPC_COMPRESSION_ALGORITHM_ARG, a->args[i].key)) { - return a->args[i].value.integer; + return (grpc_compression_algorithm)a->args[i].value.integer; break; } } @@ -151,8 +151,8 @@ grpc_channel_args *grpc_channel_args_set_compression_algorithm( /** Returns 1 if the argument for compression algorithm's enabled states bitset * was found in \a a, returning the arg's value in \a states. Otherwise, returns * 0. */ -static int find_compression_algorithm_states_bitset( - const grpc_channel_args *a, int **states_arg) { +static int find_compression_algorithm_states_bitset(const grpc_channel_args *a, + int **states_arg) { if (a != NULL) { size_t i; for (i = 0; i < a->num_args; ++i) { @@ -167,9 +167,7 @@ static int find_compression_algorithm_states_bitset( } grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( - grpc_channel_args **a, - grpc_compression_algorithm algorithm, - int state) { + grpc_channel_args **a, grpc_compression_algorithm algorithm, int state) { int *states_arg; grpc_channel_args *result = *a; const int states_arg_found = @@ -177,9 +175,9 @@ grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( if (states_arg_found) { if (state != 0) { - GPR_BITSET(states_arg, algorithm); + GPR_BITSET((unsigned *)states_arg, algorithm); } else { - GPR_BITCLEAR(states_arg, algorithm); + GPR_BITCLEAR((unsigned *)states_arg, algorithm); } } else { /* create a new arg */ @@ -189,9 +187,9 @@ grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( /* all enabled by default */ tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; if (state != 0) { - GPR_BITSET(&tmp.value.integer, algorithm); + GPR_BITSET((unsigned *)&tmp.value.integer, algorithm); } else { - GPR_BITCLEAR(&tmp.value.integer, algorithm); + GPR_BITCLEAR((unsigned *)&tmp.value.integer, algorithm); } result = grpc_channel_args_copy_and_add(*a, &tmp, 1); grpc_channel_args_destroy(*a); diff --git a/src/core/channel/channel_args.h b/src/core/channel/channel_args.h index 06a6012dee..480cc9aec2 100644 --- a/src/core/channel/channel_args.h +++ b/src/core/channel/channel_args.h @@ -71,13 +71,11 @@ grpc_channel_args *grpc_channel_args_set_compression_algorithm( * compression algorithms are enabled. It's an error to disable an algorithm set * by grpc_channel_args_set_compression_algorithm. * - * Returns an instance will the updated algorithm states. The \a a pointer is + * Returns an instance with the updated algorithm states. The \a a pointer is * modified to point to the returned instance (which may be different from the * input value of \a a). */ grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( - grpc_channel_args **a, - grpc_compression_algorithm algorithm, - int enabled); + grpc_channel_args **a, grpc_compression_algorithm algorithm, int enabled); /** Returns the bitset representing the support state (true for enabled, false * for disabled) for compression algorithms. diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c index cd7c182ef2..abd7f719e7 100644 --- a/src/core/channel/channel_stack.c +++ b/src/core/channel/channel_stack.c @@ -57,7 +57,7 @@ int grpc_trace_channel = 0; /* Given a size, round up to the next multiple of sizeof(void*) */ #define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ - (((x) + GPR_MAX_ALIGNMENT - 1) & ~(GPR_MAX_ALIGNMENT - 1)) + (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) size_t grpc_channel_stack_size(const grpc_channel_filter **filters, size_t filter_count) { @@ -101,7 +101,8 @@ grpc_call_element *grpc_call_stack_element(grpc_call_stack *call_stack, return CALL_ELEMS_FROM_STACK(call_stack) + index; } -void grpc_channel_stack_init(const grpc_channel_filter **filters, +void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, + const grpc_channel_filter **filters, size_t filter_count, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, @@ -123,7 +124,7 @@ void grpc_channel_stack_init(const grpc_channel_filter **filters, for (i = 0; i < filter_count; i++) { elems[i].filter = filters[i]; elems[i].channel_data = user_data; - elems[i].filter->init_channel_elem(&elems[i], master, args, + elems[i].filter->init_channel_elem(exec_ctx, &elems[i], master, args, metadata_context, i == 0, i == (filter_count - 1)); user_data += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); @@ -137,18 +138,20 @@ void grpc_channel_stack_init(const grpc_channel_filter **filters, stack->call_stack_size = call_size; } -void grpc_channel_stack_destroy(grpc_channel_stack *stack) { +void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx, + grpc_channel_stack *stack) { grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(stack); size_t count = stack->count; size_t i; /* destroy per-filter data */ for (i = 0; i < count; i++) { - channel_elems[i].filter->destroy_channel_elem(&channel_elems[i]); + channel_elems[i].filter->destroy_channel_elem(exec_ctx, &channel_elems[i]); } } -void grpc_call_stack_init(grpc_channel_stack *channel_stack, +void grpc_call_stack_init(grpc_exec_ctx *exec_ctx, + grpc_channel_stack *channel_stack, const void *transport_server_data, grpc_transport_stream_op *initial_op, grpc_call_stack *call_stack) { @@ -168,37 +171,40 @@ void grpc_call_stack_init(grpc_channel_stack *channel_stack, call_elems[i].filter = channel_elems[i].filter; call_elems[i].channel_data = channel_elems[i].channel_data; call_elems[i].call_data = user_data; - call_elems[i].filter->init_call_elem(&call_elems[i], transport_server_data, - initial_op); + call_elems[i].filter->init_call_elem(exec_ctx, &call_elems[i], + transport_server_data, initial_op); user_data += ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); } } -void grpc_call_stack_destroy(grpc_call_stack *stack) { +void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack) { grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack); size_t count = stack->count; size_t i; /* destroy per-filter data */ for (i = 0; i < count; i++) { - elems[i].filter->destroy_call_elem(&elems[i]); + elems[i].filter->destroy_call_elem(exec_ctx, &elems[i]); } } -void grpc_call_next_op(grpc_call_element *elem, grpc_transport_stream_op *op) { +void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op *op) { grpc_call_element *next_elem = elem + 1; - next_elem->filter->start_transport_stream_op(next_elem, op); + next_elem->filter->start_transport_stream_op(exec_ctx, next_elem, op); } -char *grpc_call_next_get_peer(grpc_call_element *elem) { +char *grpc_call_next_get_peer(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { grpc_call_element *next_elem = elem + 1; - return next_elem->filter->get_peer(next_elem); + return next_elem->filter->get_peer(exec_ctx, next_elem); } -void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op) { +void grpc_channel_next_op(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_transport_op *op) { grpc_channel_element *next_elem = elem + 1; - next_elem->filter->start_transport_op(next_elem, op); + next_elem->filter->start_transport_op(exec_ctx, next_elem, op); } grpc_channel_stack *grpc_channel_stack_from_top_element( @@ -212,9 +218,10 @@ grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) { sizeof(grpc_call_stack))); } -void grpc_call_element_send_cancel(grpc_call_element *cur_elem) { +void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx, + grpc_call_element *cur_elem) { grpc_transport_stream_op op; memset(&op, 0, sizeof(op)); op.cancel_with_status = GRPC_STATUS_CANCELLED; - grpc_call_next_op(cur_elem, &op); + grpc_call_next_op(exec_ctx, cur_elem, &op); } diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h index 4a608b956e..6732cc3018 100644 --- a/src/core/channel/channel_stack.h +++ b/src/core/channel/channel_stack.h @@ -64,12 +64,14 @@ typedef struct grpc_call_element grpc_call_element; typedef struct { /* Called to eg. send/receive data on a call. See grpc_call_next_op on how to call the next element in the stack */ - void (*start_transport_stream_op)(grpc_call_element *elem, + void (*start_transport_stream_op)(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op); /* Called to handle channel level operations - e.g. new calls, or transport closure. See grpc_channel_next_op on how to call the next element in the stack */ - void (*start_transport_op)(grpc_channel_element *elem, grpc_transport_op *op); + void (*start_transport_op)(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_transport_op *op); /* sizeof(per call data) */ size_t sizeof_call_data; @@ -80,13 +82,13 @@ typedef struct { server_transport_data is an opaque pointer. If it is NULL, this call is on a client; if it is non-NULL, then it points to memory owned by the transport and is on the server. Most filters want to ignore this - argument.*/ - void (*init_call_elem)(grpc_call_element *elem, + argument. */ + void (*init_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op); /* Destroy per call data. The filter does not need to do any chaining */ - void (*destroy_call_elem)(grpc_call_element *elem); + void (*destroy_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem); /* sizeof(per channel data) */ size_t sizeof_channel_data; @@ -96,16 +98,17 @@ typedef struct { is_first, is_last designate this elements position in the stack, and are useful for asserting correct configuration by upper layer code. The filter does not need to do any chaining */ - void (*init_channel_elem)(grpc_channel_element *elem, grpc_channel *master, - const grpc_channel_args *args, + void (*init_channel_elem)(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, int is_first, int is_last); /* Destroy per channel data. The filter does not need to do any chaining */ - void (*destroy_channel_elem)(grpc_channel_element *elem); + void (*destroy_channel_elem)(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem); /* Implement grpc_call_get_peer() */ - char *(*get_peer)(grpc_call_element *elem); + char *(*get_peer)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem); /* The name of this filter */ const char *name; @@ -153,31 +156,36 @@ grpc_call_element *grpc_call_stack_element(grpc_call_stack *stack, size_t i); size_t grpc_channel_stack_size(const grpc_channel_filter **filters, size_t filter_count); /* Initialize a channel stack given some filters */ -void grpc_channel_stack_init(const grpc_channel_filter **filters, +void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, + const grpc_channel_filter **filters, size_t filter_count, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, grpc_channel_stack *stack); /* Destroy a channel stack */ -void grpc_channel_stack_destroy(grpc_channel_stack *stack); +void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx, + grpc_channel_stack *stack); /* Initialize a call stack given a channel stack. transport_server_data is expected to be NULL on a client, or an opaque transport owned pointer on the server. */ -void grpc_call_stack_init(grpc_channel_stack *channel_stack, +void grpc_call_stack_init(grpc_exec_ctx *exec_ctx, + grpc_channel_stack *channel_stack, const void *transport_server_data, grpc_transport_stream_op *initial_op, grpc_call_stack *call_stack); /* Destroy a call stack */ -void grpc_call_stack_destroy(grpc_call_stack *stack); +void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack); /* Call the next operation in a call stack */ -void grpc_call_next_op(grpc_call_element *elem, grpc_transport_stream_op *op); +void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op *op); /* Call the next operation (depending on call directionality) in a channel stack */ -void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op); +void grpc_channel_next_op(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_transport_op *op); /* Pass through a request to get_peer to the next child element */ -char *grpc_call_next_get_peer(grpc_call_element *elem); +char *grpc_call_next_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem); /* Given the top element of a channel stack, get the channel stack itself */ grpc_channel_stack *grpc_channel_stack_from_top_element( @@ -188,7 +196,8 @@ grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem); void grpc_call_log_op(char *file, int line, gpr_log_severity severity, grpc_call_element *elem, grpc_transport_stream_op *op); -void grpc_call_element_send_cancel(grpc_call_element *cur_elem); +void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx, + grpc_call_element *cur_elem); extern int grpc_trace_channel; diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index 2e25033813..9f85557ea1 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -36,22 +36,24 @@ #include <stdio.h> #include <string.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/useful.h> + #include "src/core/channel/channel_args.h" #include "src/core/channel/connected_channel.h" -#include "src/core/surface/channel.h" #include "src/core/iomgr/iomgr.h" +#include "src/core/profiling/timers.h" #include "src/core/support/string.h" +#include "src/core/surface/channel.h" #include "src/core/transport/connectivity_state.h" -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/sync.h> -#include <grpc/support/useful.h> /* Client channel implementation */ typedef struct call_data call_data; -typedef struct { +typedef struct client_channel_channel_data { /** metadata context for this channel */ grpc_mdctx *mdctx; /** resolver for this channel */ @@ -73,9 +75,9 @@ typedef struct { guarded by mu_config */ grpc_client_config *incoming_configuration; /** a list of closures that are all waiting for config to come in */ - grpc_iomgr_closure *waiting_for_config_closures; + grpc_closure_list waiting_for_config_closures; /** resolver callback */ - grpc_iomgr_closure on_config_changed; + grpc_closure on_config_changed; /** connectivity state being tracked */ grpc_connectivity_state_tracker state_tracker; /** when an lb_policy arrives, should we try to exit idle */ @@ -91,7 +93,7 @@ typedef struct { update the channel, and create a new watcher */ typedef struct { channel_data *chand; - grpc_iomgr_closure on_changed; + grpc_closure on_changed; grpc_connectivity_state state; grpc_lb_policy *lb_policy; } lb_policy_connectivity_watcher; @@ -115,7 +117,7 @@ struct call_data { call_state state; gpr_timespec deadline; grpc_subchannel *picked_channel; - grpc_iomgr_closure async_setup_task; + grpc_closure async_setup_task; grpc_transport_stream_op waiting_op; /* our child call stack */ grpc_subchannel_call *subchannel_call; @@ -123,17 +125,18 @@ struct call_data { grpc_linked_mdelem details; }; -static grpc_iomgr_closure *merge_into_waiting_op( - grpc_call_element *elem, - grpc_transport_stream_op *new_op) GRPC_MUST_USE_RESULT; +static grpc_closure *merge_into_waiting_op(grpc_call_element *elem, + grpc_transport_stream_op *new_op) + GRPC_MUST_USE_RESULT; -static void handle_op_after_cancellation(grpc_call_element *elem, +static void handle_op_after_cancellation(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; if (op->send_ops) { grpc_stream_ops_unref_owned_objects(op->send_ops->ops, op->send_ops->nops); - op->on_done_send->cb(op->on_done_send->cb_arg, 0); + op->on_done_send->cb(exec_ctx, op->on_done_send->cb_arg, 0); } if (op->recv_ops) { char status[GPR_LTOA_MIN_BUFSIZE]; @@ -152,26 +155,28 @@ static void handle_op_after_cancellation(grpc_call_element *elem, mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); grpc_sopb_add_metadata(op->recv_ops, mdb); *op->recv_state = GRPC_STREAM_CLOSED; - op->on_done_recv->cb(op->on_done_recv->cb_arg, 1); + op->on_done_recv->cb(exec_ctx, op->on_done_recv->cb_arg, 1); } if (op->on_consumed) { - op->on_consumed->cb(op->on_consumed->cb_arg, 0); + op->on_consumed->cb(exec_ctx, op->on_consumed->cb_arg, 0); } } typedef struct { - grpc_iomgr_closure closure; + grpc_closure closure; grpc_call_element *elem; } waiting_call; -static void perform_transport_stream_op(grpc_call_element *elem, +static void perform_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op, int continuation); -static void continue_with_pick(void *arg, int iomgr_success) { +static void continue_with_pick(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { waiting_call *wc = arg; call_data *calld = wc->elem->call_data; - perform_transport_stream_op(wc->elem, &calld->waiting_op, 1); + perform_transport_stream_op(exec_ctx, wc->elem, &calld->waiting_op, 1); gpr_free(wc); } @@ -179,10 +184,9 @@ static void add_to_lb_policy_wait_queue_locked_state_config( grpc_call_element *elem) { channel_data *chand = elem->channel_data; waiting_call *wc = gpr_malloc(sizeof(*wc)); - grpc_iomgr_closure_init(&wc->closure, continue_with_pick, wc); + grpc_closure_init(&wc->closure, continue_with_pick, wc); wc->elem = elem; - wc->closure.next = chand->waiting_for_config_closures; - chand->waiting_for_config_closures = &wc->closure; + grpc_closure_list_add(&chand->waiting_for_config_closures, &wc->closure, 1); } static int is_empty(void *p, int len) { @@ -194,31 +198,31 @@ static int is_empty(void *p, int len) { return 1; } -static void started_call(void *arg, int iomgr_success) { +static void started_call_locked(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { call_data *calld = arg; grpc_transport_stream_op op; int have_waiting; - gpr_mu_lock(&calld->mu_state); if (calld->state == CALL_CANCELLED && calld->subchannel_call != NULL) { memset(&op, 0, sizeof(op)); op.cancel_with_status = GRPC_STATUS_CANCELLED; gpr_mu_unlock(&calld->mu_state); - grpc_subchannel_call_process_op(calld->subchannel_call, &op); + grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, &op); } else if (calld->state == CALL_WAITING_FOR_CALL) { have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op)); if (calld->subchannel_call != NULL) { calld->state = CALL_ACTIVE; gpr_mu_unlock(&calld->mu_state); if (have_waiting) { - grpc_subchannel_call_process_op(calld->subchannel_call, + grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, &calld->waiting_op); } } else { calld->state = CALL_CANCELLED; gpr_mu_unlock(&calld->mu_state); if (have_waiting) { - handle_op_after_cancellation(calld->elem, &calld->waiting_op); + handle_op_after_cancellation(exec_ctx, calld->elem, &calld->waiting_op); } } } else { @@ -227,36 +231,53 @@ static void started_call(void *arg, int iomgr_success) { } } -static void picked_target(void *arg, int iomgr_success) { +static void started_call(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + call_data *calld = arg; + gpr_mu_lock(&calld->mu_state); + started_call_locked(exec_ctx, arg, iomgr_success); +} + +static void picked_target(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { call_data *calld = arg; grpc_pollset *pollset; + grpc_subchannel_call_create_status call_creation_status; + + GPR_TIMER_BEGIN("picked_target", 0); if (calld->picked_channel == NULL) { /* treat this like a cancellation */ calld->waiting_op.cancel_with_status = GRPC_STATUS_UNAVAILABLE; - perform_transport_stream_op(calld->elem, &calld->waiting_op, 1); + perform_transport_stream_op(exec_ctx, calld->elem, &calld->waiting_op, 1); } else { gpr_mu_lock(&calld->mu_state); if (calld->state == CALL_CANCELLED) { gpr_mu_unlock(&calld->mu_state); - handle_op_after_cancellation(calld->elem, &calld->waiting_op); + handle_op_after_cancellation(exec_ctx, calld->elem, &calld->waiting_op); } else { GPR_ASSERT(calld->state == CALL_WAITING_FOR_PICK); calld->state = CALL_WAITING_FOR_CALL; pollset = calld->waiting_op.bind_pollset; - gpr_mu_unlock(&calld->mu_state); - grpc_iomgr_closure_init(&calld->async_setup_task, started_call, calld); - grpc_subchannel_create_call(calld->picked_channel, pollset, - &calld->subchannel_call, - &calld->async_setup_task); + grpc_closure_init(&calld->async_setup_task, started_call, calld); + call_creation_status = grpc_subchannel_create_call( + exec_ctx, calld->picked_channel, pollset, &calld->subchannel_call, + &calld->async_setup_task); + if (call_creation_status == GRPC_SUBCHANNEL_CALL_CREATE_READY) { + started_call_locked(exec_ctx, calld, iomgr_success); + } else { + gpr_mu_unlock(&calld->mu_state); + } } } + + GPR_TIMER_END("picked_target", 0); } -static grpc_iomgr_closure *merge_into_waiting_op( - grpc_call_element *elem, grpc_transport_stream_op *new_op) { +static grpc_closure *merge_into_waiting_op(grpc_call_element *elem, + grpc_transport_stream_op *new_op) { call_data *calld = elem->call_data; - grpc_iomgr_closure *consumed_op = NULL; + grpc_closure *consumed_op = NULL; grpc_transport_stream_op *waiting_op = &calld->waiting_op; GPR_ASSERT((waiting_op->send_ops != NULL) + (new_op->send_ops != NULL) <= 1); GPR_ASSERT((waiting_op->recv_ops != NULL) + (new_op->recv_ops != NULL) <= 1); @@ -282,7 +303,7 @@ static grpc_iomgr_closure *merge_into_waiting_op( return consumed_op; } -static char *cc_get_peer(grpc_call_element *elem) { +static char *cc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; grpc_subchannel_call *subchannel_call; @@ -293,8 +314,8 @@ static char *cc_get_peer(grpc_call_element *elem) { subchannel_call = calld->subchannel_call; GRPC_SUBCHANNEL_CALL_REF(subchannel_call, "get_peer"); gpr_mu_unlock(&calld->mu_state); - result = grpc_subchannel_call_get_peer(subchannel_call); - GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "get_peer"); + result = grpc_subchannel_call_get_peer(exec_ctx, subchannel_call); + GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, subchannel_call, "get_peer"); return result; } else { gpr_mu_unlock(&calld->mu_state); @@ -302,7 +323,8 @@ static char *cc_get_peer(grpc_call_element *elem) { } } -static void perform_transport_stream_op(grpc_call_element *elem, +static void perform_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op, int continuation) { call_data *calld = elem->call_data; @@ -310,7 +332,7 @@ static void perform_transport_stream_op(grpc_call_element *elem, grpc_subchannel_call *subchannel_call; grpc_lb_policy *lb_policy; grpc_transport_stream_op op2; - grpc_iomgr_closure *consumed_op = NULL; + GPR_TIMER_BEGIN("perform_transport_stream_op", 0); GPR_ASSERT(elem->filter == &grpc_client_channel_filter); GRPC_CALL_LOG_OP(GPR_INFO, elem, op); @@ -320,15 +342,15 @@ static void perform_transport_stream_op(grpc_call_element *elem, GPR_ASSERT(!continuation); subchannel_call = calld->subchannel_call; gpr_mu_unlock(&calld->mu_state); - grpc_subchannel_call_process_op(subchannel_call, op); + grpc_subchannel_call_process_op(exec_ctx, subchannel_call, op); break; case CALL_CANCELLED: gpr_mu_unlock(&calld->mu_state); - handle_op_after_cancellation(elem, op); + handle_op_after_cancellation(exec_ctx, elem, op); break; case CALL_WAITING_FOR_SEND: GPR_ASSERT(!continuation); - consumed_op = merge_into_waiting_op(elem, op); + grpc_exec_ctx_enqueue(exec_ctx, merge_into_waiting_op(elem, op), 1); if (!calld->waiting_op.send_ops && calld->waiting_op.cancel_with_status == GRPC_STATUS_OK) { gpr_mu_unlock(&calld->mu_state); @@ -354,10 +376,10 @@ static void perform_transport_stream_op(grpc_call_element *elem, op2.on_consumed = NULL; } gpr_mu_unlock(&calld->mu_state); - handle_op_after_cancellation(elem, op); - handle_op_after_cancellation(elem, &op2); + handle_op_after_cancellation(exec_ctx, elem, op); + handle_op_after_cancellation(exec_ctx, elem, &op2); } else { - consumed_op = merge_into_waiting_op(elem, op); + grpc_exec_ctx_enqueue(exec_ctx, merge_into_waiting_op(elem, op), 1); gpr_mu_unlock(&calld->mu_state); } break; @@ -367,7 +389,7 @@ static void perform_transport_stream_op(grpc_call_element *elem, if (op->cancel_with_status != GRPC_STATUS_OK) { calld->state = CALL_CANCELLED; gpr_mu_unlock(&calld->mu_state); - handle_op_after_cancellation(elem, op); + handle_op_after_cancellation(exec_ctx, elem, op); } else { calld->waiting_op = *op; @@ -380,34 +402,33 @@ static void perform_transport_stream_op(grpc_call_element *elem, gpr_mu_lock(&chand->mu_config); lb_policy = chand->lb_policy; if (lb_policy) { - grpc_transport_stream_op *op = &calld->waiting_op; - grpc_pollset *bind_pollset = op->bind_pollset; + grpc_transport_stream_op *waiting_op = &calld->waiting_op; + grpc_pollset *bind_pollset = waiting_op->bind_pollset; grpc_metadata_batch *initial_metadata = - &op->send_ops->ops[0].data.metadata; + &waiting_op->send_ops->ops[0].data.metadata; GRPC_LB_POLICY_REF(lb_policy, "pick"); gpr_mu_unlock(&chand->mu_config); calld->state = CALL_WAITING_FOR_PICK; - GPR_ASSERT(op->bind_pollset); - GPR_ASSERT(op->send_ops); - GPR_ASSERT(op->send_ops->nops >= 1); - GPR_ASSERT(op->send_ops->ops[0].type == GRPC_OP_METADATA); + GPR_ASSERT(waiting_op->bind_pollset); + GPR_ASSERT(waiting_op->send_ops); + GPR_ASSERT(waiting_op->send_ops->nops >= 1); + GPR_ASSERT(waiting_op->send_ops->ops[0].type == GRPC_OP_METADATA); gpr_mu_unlock(&calld->mu_state); - grpc_iomgr_closure_init(&calld->async_setup_task, picked_target, - calld); - grpc_lb_policy_pick(lb_policy, bind_pollset, initial_metadata, - &calld->picked_channel, + grpc_closure_init(&calld->async_setup_task, picked_target, calld); + grpc_lb_policy_pick(exec_ctx, lb_policy, bind_pollset, + initial_metadata, &calld->picked_channel, &calld->async_setup_task); - GRPC_LB_POLICY_UNREF(lb_policy, "pick"); + GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "pick"); } else if (chand->resolver != NULL) { calld->state = CALL_WAITING_FOR_CONFIG; add_to_lb_policy_wait_queue_locked_state_config(elem); if (!chand->started_resolving && chand->resolver != NULL) { GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); chand->started_resolving = 1; - grpc_resolver_next(chand->resolver, + grpc_resolver_next(exec_ctx, chand->resolver, &chand->incoming_configuration, &chand->on_config_changed); } @@ -417,62 +438,70 @@ static void perform_transport_stream_op(grpc_call_element *elem, calld->state = CALL_CANCELLED; gpr_mu_unlock(&chand->mu_config); gpr_mu_unlock(&calld->mu_state); - handle_op_after_cancellation(elem, op); + handle_op_after_cancellation(exec_ctx, elem, op); } } } break; } - if (consumed_op != NULL) { - consumed_op->cb(consumed_op->cb_arg, 1); - } + GPR_TIMER_END("perform_transport_stream_op", 0); } -static void cc_start_transport_stream_op(grpc_call_element *elem, +static void cc_start_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { - perform_transport_stream_op(elem, op, 0); + perform_transport_stream_op(exec_ctx, elem, op, 0); } -static void watch_lb_policy(channel_data *chand, grpc_lb_policy *lb_policy, +static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand, + grpc_lb_policy *lb_policy, grpc_connectivity_state current_state); -static void on_lb_policy_state_changed(void *arg, int iomgr_success) { +static void on_lb_policy_state_changed_locked( + grpc_exec_ctx *exec_ctx, lb_policy_connectivity_watcher *w) { + /* check if the notification is for a stale policy */ + if (w->lb_policy != w->chand->lb_policy) return; + + grpc_connectivity_state_set(exec_ctx, &w->chand->state_tracker, w->state, + "lb_changed"); + if (w->state != GRPC_CHANNEL_FATAL_FAILURE) { + watch_lb_policy(exec_ctx, w->chand, w->lb_policy, w->state); + } +} + +static void on_lb_policy_state_changed(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { lb_policy_connectivity_watcher *w = arg; gpr_mu_lock(&w->chand->mu_config); - /* check if the notification is for a stale policy */ - if (w->lb_policy == w->chand->lb_policy) { - grpc_connectivity_state_set(&w->chand->state_tracker, w->state, - "lb_changed"); - if (w->state != GRPC_CHANNEL_FATAL_FAILURE) { - watch_lb_policy(w->chand, w->lb_policy, w->state); - } - } + on_lb_policy_state_changed_locked(exec_ctx, w); gpr_mu_unlock(&w->chand->mu_config); - GRPC_CHANNEL_INTERNAL_UNREF(w->chand->master, "watch_lb_policy"); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->chand->master, "watch_lb_policy"); gpr_free(w); } -static void watch_lb_policy(channel_data *chand, grpc_lb_policy *lb_policy, +static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand, + grpc_lb_policy *lb_policy, grpc_connectivity_state current_state) { lb_policy_connectivity_watcher *w = gpr_malloc(sizeof(*w)); GRPC_CHANNEL_INTERNAL_REF(chand->master, "watch_lb_policy"); w->chand = chand; - grpc_iomgr_closure_init(&w->on_changed, on_lb_policy_state_changed, w); + grpc_closure_init(&w->on_changed, on_lb_policy_state_changed, w); w->state = current_state; w->lb_policy = lb_policy; - grpc_lb_policy_notify_on_state_change(lb_policy, &w->state, &w->on_changed); + grpc_lb_policy_notify_on_state_change(exec_ctx, lb_policy, &w->state, + &w->on_changed); } -static void cc_on_config_changed(void *arg, int iomgr_success) { +static void cc_on_config_changed(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { channel_data *chand = arg; grpc_lb_policy *lb_policy = NULL; grpc_lb_policy *old_lb_policy; grpc_resolver *old_resolver; - grpc_iomgr_closure *wakeup_closures = NULL; grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE; int exit_idle = 0; @@ -481,10 +510,10 @@ static void cc_on_config_changed(void *arg, int iomgr_success) { if (lb_policy != NULL) { GRPC_LB_POLICY_REF(lb_policy, "channel"); GRPC_LB_POLICY_REF(lb_policy, "config_change"); - state = grpc_lb_policy_check_connectivity(lb_policy); + state = grpc_lb_policy_check_connectivity(exec_ctx, lb_policy); } - grpc_client_config_unref(chand->incoming_configuration); + grpc_client_config_unref(exec_ctx, chand->incoming_configuration); } chand->incoming_configuration = NULL; @@ -493,8 +522,7 @@ static void cc_on_config_changed(void *arg, int iomgr_success) { old_lb_policy = chand->lb_policy; chand->lb_policy = lb_policy; if (lb_policy != NULL || chand->resolver == NULL /* disconnected */) { - wakeup_closures = chand->waiting_for_config_closures; - chand->waiting_for_config_closures = NULL; + grpc_exec_ctx_enqueue_list(exec_ctx, &chand->waiting_for_config_closures); } if (lb_policy != NULL && chand->exit_idle_when_lb_policy_arrives) { GRPC_LB_POLICY_REF(lb_policy, "exit_idle"); @@ -505,57 +533,53 @@ static void cc_on_config_changed(void *arg, int iomgr_success) { if (iomgr_success && chand->resolver) { grpc_resolver *resolver = chand->resolver; GRPC_RESOLVER_REF(resolver, "channel-next"); - grpc_connectivity_state_set(&chand->state_tracker, state, + grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, state, "new_lb+resolver"); + if (lb_policy != NULL) { + watch_lb_policy(exec_ctx, chand, lb_policy, state); + } gpr_mu_unlock(&chand->mu_config); GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); - grpc_resolver_next(resolver, &chand->incoming_configuration, + grpc_resolver_next(exec_ctx, resolver, &chand->incoming_configuration, &chand->on_config_changed); - GRPC_RESOLVER_UNREF(resolver, "channel-next"); - if (lb_policy != NULL) { - watch_lb_policy(chand, lb_policy, state); - } + GRPC_RESOLVER_UNREF(exec_ctx, resolver, "channel-next"); } else { old_resolver = chand->resolver; chand->resolver = NULL; - grpc_connectivity_state_set(&chand->state_tracker, + grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, "resolver_gone"); gpr_mu_unlock(&chand->mu_config); if (old_resolver != NULL) { - grpc_resolver_shutdown(old_resolver); - GRPC_RESOLVER_UNREF(old_resolver, "channel"); + grpc_resolver_shutdown(exec_ctx, old_resolver); + GRPC_RESOLVER_UNREF(exec_ctx, old_resolver, "channel"); } } if (exit_idle) { - grpc_lb_policy_exit_idle(lb_policy); - GRPC_LB_POLICY_UNREF(lb_policy, "exit_idle"); + grpc_lb_policy_exit_idle(exec_ctx, lb_policy); + GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "exit_idle"); } if (old_lb_policy != NULL) { - grpc_lb_policy_shutdown(old_lb_policy); - GRPC_LB_POLICY_UNREF(old_lb_policy, "channel"); - } - - while (wakeup_closures) { - grpc_iomgr_closure *next = wakeup_closures->next; - wakeup_closures->cb(wakeup_closures->cb_arg, 1); - wakeup_closures = next; + grpc_lb_policy_shutdown(exec_ctx, old_lb_policy); + GRPC_LB_POLICY_UNREF(exec_ctx, old_lb_policy, "channel"); } if (lb_policy != NULL) { - GRPC_LB_POLICY_UNREF(lb_policy, "config_change"); + GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "config_change"); } - GRPC_CHANNEL_INTERNAL_UNREF(chand->master, "resolver"); + + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->master, "resolver"); } -static void cc_start_transport_op(grpc_channel_element *elem, +static void cc_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_transport_op *op) { grpc_lb_policy *lb_policy = NULL; channel_data *chand = elem->channel_data; grpc_resolver *destroy_resolver = NULL; - grpc_iomgr_closure *on_consumed = op->on_consumed; - op->on_consumed = NULL; + + grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 1); GPR_ASSERT(op->set_accept_stream == NULL); GPR_ASSERT(op->bind_pollset == NULL); @@ -563,7 +587,7 @@ static void cc_start_transport_op(grpc_channel_element *elem, gpr_mu_lock(&chand->mu_config); if (op->on_connectivity_state_change != NULL) { grpc_connectivity_state_notify_on_state_change( - &chand->state_tracker, op->connectivity_state, + exec_ctx, &chand->state_tracker, op->connectivity_state, op->on_connectivity_state_change); op->on_connectivity_state_change = NULL; op->connectivity_state = NULL; @@ -577,35 +601,31 @@ static void cc_start_transport_op(grpc_channel_element *elem, } if (op->disconnect && chand->resolver != NULL) { - grpc_connectivity_state_set(&chand->state_tracker, + grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, "disconnect"); destroy_resolver = chand->resolver; chand->resolver = NULL; if (chand->lb_policy != NULL) { - grpc_lb_policy_shutdown(chand->lb_policy); - GRPC_LB_POLICY_UNREF(chand->lb_policy, "channel"); + grpc_lb_policy_shutdown(exec_ctx, chand->lb_policy); + GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); chand->lb_policy = NULL; } } gpr_mu_unlock(&chand->mu_config); if (destroy_resolver) { - grpc_resolver_shutdown(destroy_resolver); - GRPC_RESOLVER_UNREF(destroy_resolver, "channel"); + grpc_resolver_shutdown(exec_ctx, destroy_resolver); + GRPC_RESOLVER_UNREF(exec_ctx, destroy_resolver, "channel"); } if (lb_policy) { - grpc_lb_policy_broadcast(lb_policy, op); - GRPC_LB_POLICY_UNREF(lb_policy, "broadcast"); - } - - if (on_consumed) { - grpc_iomgr_add_callback(on_consumed); + grpc_lb_policy_broadcast(exec_ctx, lb_policy, op); + GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "broadcast"); } } /* Constructor for call_data */ -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; @@ -622,7 +642,8 @@ static void init_call_elem(grpc_call_element *elem, } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) { +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { call_data *calld = elem->call_data; grpc_subchannel_call *subchannel_call; @@ -634,7 +655,7 @@ static void destroy_call_elem(grpc_call_element *elem) { case CALL_ACTIVE: subchannel_call = calld->subchannel_call; gpr_mu_unlock(&calld->mu_state); - GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "client_channel"); + GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, subchannel_call, "client_channel"); break; case CALL_CREATED: case CALL_CANCELLED: @@ -644,14 +665,13 @@ static void destroy_call_elem(grpc_call_element *elem) { case CALL_WAITING_FOR_CONFIG: case CALL_WAITING_FOR_CALL: case CALL_WAITING_FOR_SEND: - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - break; + GPR_UNREACHABLE_CODE(return ); } } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, int is_first, int is_last) { @@ -666,43 +686,37 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, chand->mdctx = metadata_context; chand->master = master; grpc_pollset_set_init(&chand->pollset_set); - grpc_iomgr_closure_init(&chand->on_config_changed, cc_on_config_changed, - chand); + grpc_closure_init(&chand->on_config_changed, cc_on_config_changed, chand); grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, "client_channel"); } /* Destructor for channel_data */ -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { channel_data *chand = elem->channel_data; if (chand->resolver != NULL) { - grpc_resolver_shutdown(chand->resolver); - GRPC_RESOLVER_UNREF(chand->resolver, "channel"); + grpc_resolver_shutdown(exec_ctx, chand->resolver); + GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel"); } if (chand->lb_policy != NULL) { - GRPC_LB_POLICY_UNREF(chand->lb_policy, "channel"); + GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); } - grpc_connectivity_state_destroy(&chand->state_tracker); + grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); grpc_pollset_set_destroy(&chand->pollset_set); gpr_mu_destroy(&chand->mu_config); } const grpc_channel_filter grpc_client_channel_filter = { - cc_start_transport_stream_op, - cc_start_transport_op, - sizeof(call_data), - init_call_elem, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - cc_get_peer, - "client-channel", + cc_start_transport_stream_op, cc_start_transport_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, + destroy_channel_elem, cc_get_peer, "client-channel", }; -void grpc_client_channel_set_resolver(grpc_channel_stack *channel_stack, +void grpc_client_channel_set_resolver(grpc_exec_ctx *exec_ctx, + grpc_channel_stack *channel_stack, grpc_resolver *resolver) { /* post construction initialization: set the transport setup pointer */ grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack); @@ -711,31 +725,32 @@ void grpc_client_channel_set_resolver(grpc_channel_stack *channel_stack, GPR_ASSERT(!chand->resolver); chand->resolver = resolver; GRPC_RESOLVER_REF(resolver, "channel"); - if (chand->waiting_for_config_closures != NULL || + if (!grpc_closure_list_empty(chand->waiting_for_config_closures) || chand->exit_idle_when_lb_policy_arrives) { chand->started_resolving = 1; GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); - grpc_resolver_next(resolver, &chand->incoming_configuration, + grpc_resolver_next(exec_ctx, resolver, &chand->incoming_configuration, &chand->on_config_changed); } gpr_mu_unlock(&chand->mu_config); } grpc_connectivity_state grpc_client_channel_check_connectivity_state( - grpc_channel_element *elem, int try_to_connect) { + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) { channel_data *chand = elem->channel_data; grpc_connectivity_state out; gpr_mu_lock(&chand->mu_config); out = grpc_connectivity_state_check(&chand->state_tracker); if (out == GRPC_CHANNEL_IDLE && try_to_connect) { if (chand->lb_policy != NULL) { - grpc_lb_policy_exit_idle(chand->lb_policy); + grpc_lb_policy_exit_idle(exec_ctx, chand->lb_policy); } else { chand->exit_idle_when_lb_policy_arrives = 1; if (!chand->started_resolving && chand->resolver != NULL) { GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); chand->started_resolving = 1; - grpc_resolver_next(chand->resolver, &chand->incoming_configuration, + grpc_resolver_next(exec_ctx, chand->resolver, + &chand->incoming_configuration, &chand->on_config_changed); } } @@ -745,12 +760,12 @@ grpc_connectivity_state grpc_client_channel_check_connectivity_state( } void grpc_client_channel_watch_connectivity_state( - grpc_channel_element *elem, grpc_connectivity_state *state, - grpc_iomgr_closure *on_complete) { + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_connectivity_state *state, grpc_closure *on_complete) { channel_data *chand = elem->channel_data; gpr_mu_lock(&chand->mu_config); - grpc_connectivity_state_notify_on_state_change(&chand->state_tracker, state, - on_complete); + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &chand->state_tracker, state, on_complete); gpr_mu_unlock(&chand->mu_config); } @@ -760,14 +775,16 @@ grpc_pollset_set *grpc_client_channel_get_connecting_pollset_set( return &chand->pollset_set; } -void grpc_client_channel_add_interested_party(grpc_channel_element *elem, +void grpc_client_channel_add_interested_party(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_pollset *pollset) { channel_data *chand = elem->channel_data; - grpc_pollset_set_add_pollset(&chand->pollset_set, pollset); + grpc_pollset_set_add_pollset(exec_ctx, &chand->pollset_set, pollset); } -void grpc_client_channel_del_interested_party(grpc_channel_element *elem, +void grpc_client_channel_del_interested_party(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_pollset *pollset) { channel_data *chand = elem->channel_data; - grpc_pollset_set_del_pollset(&chand->pollset_set, pollset); + grpc_pollset_set_del_pollset(exec_ctx, &chand->pollset_set, pollset); } diff --git a/src/core/channel/client_channel.h b/src/core/channel/client_channel.h index 13681e3956..5103f07a43 100644 --- a/src/core/channel/client_channel.h +++ b/src/core/channel/client_channel.h @@ -49,22 +49,25 @@ extern const grpc_channel_filter grpc_client_channel_filter; /* post-construction initializer to let the client channel know which transport setup it should cancel upon destruction, or initiate when it needs a connection */ -void grpc_client_channel_set_resolver(grpc_channel_stack *channel_stack, +void grpc_client_channel_set_resolver(grpc_exec_ctx *exec_ctx, + grpc_channel_stack *channel_stack, grpc_resolver *resolver); grpc_connectivity_state grpc_client_channel_check_connectivity_state( - grpc_channel_element *elem, int try_to_connect); + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect); void grpc_client_channel_watch_connectivity_state( - grpc_channel_element *elem, grpc_connectivity_state *state, - grpc_iomgr_closure *on_complete); + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_connectivity_state *state, grpc_closure *on_complete); grpc_pollset_set *grpc_client_channel_get_connecting_pollset_set( grpc_channel_element *elem); -void grpc_client_channel_add_interested_party(grpc_channel_element *channel, +void grpc_client_channel_add_interested_party(grpc_exec_ctx *exec_ctx, + grpc_channel_element *channel, grpc_pollset *pollset); -void grpc_client_channel_del_interested_party(grpc_channel_element *channel, +void grpc_client_channel_del_interested_party(grpc_exec_ctx *exec_ctx, + grpc_channel_element *channel, grpc_pollset *pollset); #endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H */ diff --git a/src/core/channel/client_uchannel.c b/src/core/channel/client_uchannel.c new file mode 100644 index 0000000000..510677a844 --- /dev/null +++ b/src/core/channel/client_uchannel.c @@ -0,0 +1,572 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/channel/client_uchannel.h" + +#include <string.h> + +#include "src/core/census/grpc_filter.h" +#include "src/core/channel/channel_args.h" +#include "src/core/channel/client_channel.h" +#include "src/core/channel/compress_filter.h" +#include "src/core/iomgr/iomgr.h" +#include "src/core/support/string.h" +#include "src/core/surface/channel.h" +#include "src/core/transport/connectivity_state.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/useful.h> + +/** Microchannel (uchannel) implementation: a lightweight channel without any + * load-balancing mechanisms meant for communication from within the core. */ + +typedef struct call_data call_data; + +typedef struct client_uchannel_channel_data { + /** metadata context for this channel */ + grpc_mdctx *mdctx; + + /** master channel - the grpc_channel instance that ultimately owns + this channel_data via its channel stack. + We occasionally use this to bump the refcount on the master channel + to keep ourselves alive through an asynchronous operation. */ + grpc_channel *master; + + /** connectivity state being tracked */ + grpc_connectivity_state_tracker state_tracker; + + /** the subchannel wrapped by the microchannel */ + grpc_subchannel *subchannel; + + /** the callback used to stay subscribed to subchannel connectivity + * notifications */ + grpc_closure connectivity_cb; + + /** the current connectivity state of the wrapped subchannel */ + grpc_connectivity_state subchannel_connectivity; + + gpr_mu mu_state; +} channel_data; + +typedef enum { + CALL_CREATED, + CALL_WAITING_FOR_SEND, + CALL_WAITING_FOR_CALL, + CALL_ACTIVE, + CALL_CANCELLED +} call_state; + +struct call_data { + /* owning element */ + grpc_call_element *elem; + + gpr_mu mu_state; + + call_state state; + gpr_timespec deadline; + grpc_closure async_setup_task; + grpc_transport_stream_op waiting_op; + /* our child call stack */ + grpc_subchannel_call *subchannel_call; + grpc_linked_mdelem status; + grpc_linked_mdelem details; +}; + +static grpc_closure *merge_into_waiting_op(grpc_call_element *elem, + grpc_transport_stream_op *new_op) + GRPC_MUST_USE_RESULT; + +static void handle_op_after_cancellation(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + if (op->send_ops) { + grpc_stream_ops_unref_owned_objects(op->send_ops->ops, op->send_ops->nops); + op->on_done_send->cb(exec_ctx, op->on_done_send->cb_arg, 0); + } + if (op->recv_ops) { + char status[GPR_LTOA_MIN_BUFSIZE]; + grpc_metadata_batch mdb; + gpr_ltoa(GRPC_STATUS_CANCELLED, status); + calld->status.md = + grpc_mdelem_from_strings(chand->mdctx, "grpc-status", status); + calld->details.md = + grpc_mdelem_from_strings(chand->mdctx, "grpc-message", "Cancelled"); + calld->status.prev = calld->details.next = NULL; + calld->status.next = &calld->details; + calld->details.prev = &calld->status; + mdb.list.head = &calld->status; + mdb.list.tail = &calld->details; + mdb.garbage.head = mdb.garbage.tail = NULL; + mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + grpc_sopb_add_metadata(op->recv_ops, mdb); + *op->recv_state = GRPC_STREAM_CLOSED; + op->on_done_recv->cb(exec_ctx, op->on_done_recv->cb_arg, 1); + } + if (op->on_consumed) { + op->on_consumed->cb(exec_ctx, op->on_consumed->cb_arg, 0); + } +} + +typedef struct { + grpc_closure closure; + grpc_call_element *elem; +} waiting_call; + +static void perform_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op *op, + int continuation); + +static int is_empty(void *p, int len) { + char *ptr = p; + int i; + for (i = 0; i < len; i++) { + if (ptr[i] != 0) return 0; + } + return 1; +} + +static void monitor_subchannel(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + channel_data *chand = arg; + grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, + chand->subchannel_connectivity, + "uchannel_monitor_subchannel"); + grpc_subchannel_notify_on_state_change(exec_ctx, chand->subchannel, + &chand->subchannel_connectivity, + &chand->connectivity_cb); +} + +static void started_call_locked(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + call_data *calld = arg; + grpc_transport_stream_op op; + int have_waiting; + + if (calld->state == CALL_CANCELLED && iomgr_success == 0) { + have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op)); + gpr_mu_unlock(&calld->mu_state); + if (have_waiting) { + handle_op_after_cancellation(exec_ctx, calld->elem, &calld->waiting_op); + } + } else if (calld->state == CALL_CANCELLED && calld->subchannel_call != NULL) { + memset(&op, 0, sizeof(op)); + op.cancel_with_status = GRPC_STATUS_CANCELLED; + gpr_mu_unlock(&calld->mu_state); + grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, &op); + } else if (calld->state == CALL_WAITING_FOR_CALL) { + have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op)); + if (calld->subchannel_call != NULL) { + calld->state = CALL_ACTIVE; + gpr_mu_unlock(&calld->mu_state); + if (have_waiting) { + grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, + &calld->waiting_op); + } + } else { + calld->state = CALL_CANCELLED; + gpr_mu_unlock(&calld->mu_state); + if (have_waiting) { + handle_op_after_cancellation(exec_ctx, calld->elem, &calld->waiting_op); + } + } + } else { + GPR_ASSERT(calld->state == CALL_CANCELLED); + gpr_mu_unlock(&calld->mu_state); + have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op)); + if (have_waiting) { + handle_op_after_cancellation(exec_ctx, calld->elem, &calld->waiting_op); + } + } +} + +static void started_call(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + call_data *calld = arg; + gpr_mu_lock(&calld->mu_state); + started_call_locked(exec_ctx, arg, iomgr_success); +} + +static grpc_closure *merge_into_waiting_op(grpc_call_element *elem, + grpc_transport_stream_op *new_op) { + call_data *calld = elem->call_data; + grpc_closure *consumed_op = NULL; + grpc_transport_stream_op *waiting_op = &calld->waiting_op; + GPR_ASSERT((waiting_op->send_ops != NULL) + (new_op->send_ops != NULL) <= 1); + GPR_ASSERT((waiting_op->recv_ops != NULL) + (new_op->recv_ops != NULL) <= 1); + if (new_op->send_ops != NULL) { + waiting_op->send_ops = new_op->send_ops; + waiting_op->is_last_send = new_op->is_last_send; + waiting_op->on_done_send = new_op->on_done_send; + } + if (new_op->recv_ops != NULL) { + waiting_op->recv_ops = new_op->recv_ops; + waiting_op->recv_state = new_op->recv_state; + waiting_op->on_done_recv = new_op->on_done_recv; + } + if (new_op->on_consumed != NULL) { + if (waiting_op->on_consumed != NULL) { + consumed_op = waiting_op->on_consumed; + } + waiting_op->on_consumed = new_op->on_consumed; + } + if (new_op->cancel_with_status != GRPC_STATUS_OK) { + waiting_op->cancel_with_status = new_op->cancel_with_status; + } + return consumed_op; +} + +static char *cuc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_subchannel_call *subchannel_call; + char *result; + + gpr_mu_lock(&calld->mu_state); + if (calld->state == CALL_ACTIVE) { + subchannel_call = calld->subchannel_call; + GRPC_SUBCHANNEL_CALL_REF(subchannel_call, "get_peer"); + gpr_mu_unlock(&calld->mu_state); + result = grpc_subchannel_call_get_peer(exec_ctx, subchannel_call); + GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, subchannel_call, "get_peer"); + return result; + } else { + gpr_mu_unlock(&calld->mu_state); + return grpc_channel_get_target(chand->master); + } +} + +static void perform_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op *op, + int continuation) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_subchannel_call *subchannel_call; + grpc_transport_stream_op op2; + GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter); + GRPC_CALL_LOG_OP(GPR_INFO, elem, op); + + gpr_mu_lock(&calld->mu_state); + /* make sure the wrapped subchannel has been set (see + * grpc_client_uchannel_set_subchannel) */ + GPR_ASSERT(chand->subchannel != NULL); + + switch (calld->state) { + case CALL_ACTIVE: + GPR_ASSERT(!continuation); + subchannel_call = calld->subchannel_call; + gpr_mu_unlock(&calld->mu_state); + grpc_subchannel_call_process_op(exec_ctx, subchannel_call, op); + break; + case CALL_CANCELLED: + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(exec_ctx, elem, op); + break; + case CALL_WAITING_FOR_SEND: + GPR_ASSERT(!continuation); + grpc_exec_ctx_enqueue(exec_ctx, merge_into_waiting_op(elem, op), 1); + if (!calld->waiting_op.send_ops && + calld->waiting_op.cancel_with_status == GRPC_STATUS_OK) { + gpr_mu_unlock(&calld->mu_state); + break; + } + *op = calld->waiting_op; + memset(&calld->waiting_op, 0, sizeof(calld->waiting_op)); + continuation = 1; + /* fall through */ + case CALL_WAITING_FOR_CALL: + if (!continuation) { + if (op->cancel_with_status != GRPC_STATUS_OK) { + calld->state = CALL_CANCELLED; + op2 = calld->waiting_op; + memset(&calld->waiting_op, 0, sizeof(calld->waiting_op)); + if (op->on_consumed) { + calld->waiting_op.on_consumed = op->on_consumed; + op->on_consumed = NULL; + } else if (op2.on_consumed) { + calld->waiting_op.on_consumed = op2.on_consumed; + op2.on_consumed = NULL; + } + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(exec_ctx, elem, op); + handle_op_after_cancellation(exec_ctx, elem, &op2); + grpc_subchannel_cancel_waiting_call(exec_ctx, chand->subchannel, 1); + } else { + grpc_exec_ctx_enqueue(exec_ctx, merge_into_waiting_op(elem, op), 1); + gpr_mu_unlock(&calld->mu_state); + } + break; + } + /* fall through */ + case CALL_CREATED: + if (op->cancel_with_status != GRPC_STATUS_OK) { + calld->state = CALL_CANCELLED; + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(exec_ctx, elem, op); + } else { + calld->waiting_op = *op; + if (op->send_ops == NULL) { + calld->state = CALL_WAITING_FOR_SEND; + gpr_mu_unlock(&calld->mu_state); + } else { + grpc_subchannel_call_create_status call_creation_status; + grpc_pollset *pollset = calld->waiting_op.bind_pollset; + calld->state = CALL_WAITING_FOR_CALL; + grpc_closure_init(&calld->async_setup_task, started_call, calld); + call_creation_status = grpc_subchannel_create_call( + exec_ctx, chand->subchannel, pollset, &calld->subchannel_call, + &calld->async_setup_task); + if (call_creation_status == GRPC_SUBCHANNEL_CALL_CREATE_READY) { + started_call_locked(exec_ctx, calld, 1); + } else { + gpr_mu_unlock(&calld->mu_state); + } + } + } + break; + } +} + +static void cuc_start_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op *op) { + perform_transport_stream_op(exec_ctx, elem, op, 0); +} + +static void cuc_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_transport_op *op) { + channel_data *chand = elem->channel_data; + + grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 1); + + GPR_ASSERT(op->set_accept_stream == NULL); + GPR_ASSERT(op->bind_pollset == NULL); + + if (op->on_connectivity_state_change != NULL) { + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &chand->state_tracker, op->connectivity_state, + op->on_connectivity_state_change); + op->on_connectivity_state_change = NULL; + op->connectivity_state = NULL; + } + + if (op->disconnect) { + grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE, "disconnect"); + } +} + +/* Constructor for call_data */ +static void cuc_init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + call_data *calld = elem->call_data; + memset(calld, 0, sizeof(call_data)); + + /* TODO(ctiller): is there something useful we can do here? */ + GPR_ASSERT(initial_op == NULL); + + GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter); + GPR_ASSERT(server_transport_data == NULL); + gpr_mu_init(&calld->mu_state); + calld->elem = elem; + calld->state = CALL_CREATED; + calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); +} + +/* Destructor for call_data */ +static void cuc_destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + call_data *calld = elem->call_data; + grpc_subchannel_call *subchannel_call; + + /* if the call got activated, we need to destroy the child stack also, and + remove it from the in-flight requests tracked by the child_entry we + picked */ + gpr_mu_lock(&calld->mu_state); + switch (calld->state) { + case CALL_ACTIVE: + subchannel_call = calld->subchannel_call; + gpr_mu_unlock(&calld->mu_state); + GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, subchannel_call, "client_uchannel"); + break; + case CALL_CREATED: + case CALL_CANCELLED: + gpr_mu_unlock(&calld->mu_state); + break; + case CALL_WAITING_FOR_CALL: + case CALL_WAITING_FOR_SEND: + GPR_UNREACHABLE_CODE(return ); + } +} + +/* Constructor for channel_data */ +static void cuc_init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel *master, + const grpc_channel_args *args, + grpc_mdctx *metadata_context, int is_first, + int is_last) { + channel_data *chand = elem->channel_data; + memset(chand, 0, sizeof(*chand)); + grpc_closure_init(&chand->connectivity_cb, monitor_subchannel, chand); + GPR_ASSERT(is_last); + GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter); + chand->mdctx = metadata_context; + chand->master = master; + grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, + "client_uchannel"); + gpr_mu_init(&chand->mu_state); +} + +/* Destructor for channel_data */ +static void cuc_destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + channel_data *chand = elem->channel_data; + grpc_subchannel_state_change_unsubscribe(exec_ctx, chand->subchannel, + &chand->connectivity_cb); + grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); + gpr_mu_destroy(&chand->mu_state); +} + +const grpc_channel_filter grpc_client_uchannel_filter = { + cuc_start_transport_stream_op, + cuc_start_transport_op, + sizeof(call_data), + cuc_init_call_elem, + cuc_destroy_call_elem, + sizeof(channel_data), + cuc_init_channel_elem, + cuc_destroy_channel_elem, + cuc_get_peer, + "client-uchannel", +}; + +grpc_connectivity_state grpc_client_uchannel_check_connectivity_state( + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) { + channel_data *chand = elem->channel_data; + grpc_connectivity_state out; + out = grpc_connectivity_state_check(&chand->state_tracker); + gpr_mu_lock(&chand->mu_state); + if (out == GRPC_CHANNEL_IDLE && try_to_connect) { + grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, + GRPC_CHANNEL_CONNECTING, + "uchannel_connecting_changed"); + chand->subchannel_connectivity = out; + grpc_subchannel_notify_on_state_change(exec_ctx, chand->subchannel, + &chand->subchannel_connectivity, + &chand->connectivity_cb); + } + gpr_mu_unlock(&chand->mu_state); + return out; +} + +void grpc_client_uchannel_watch_connectivity_state( + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_connectivity_state *state, grpc_closure *on_complete) { + channel_data *chand = elem->channel_data; + gpr_mu_lock(&chand->mu_state); + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &chand->state_tracker, state, on_complete); + gpr_mu_unlock(&chand->mu_state); +} + +grpc_pollset_set *grpc_client_uchannel_get_connecting_pollset_set( + grpc_channel_element *elem) { + channel_data *chand = elem->channel_data; + grpc_channel_element *parent_elem; + gpr_mu_lock(&chand->mu_state); + parent_elem = grpc_channel_stack_last_element(grpc_channel_get_channel_stack( + grpc_subchannel_get_master(chand->subchannel))); + gpr_mu_unlock(&chand->mu_state); + return grpc_client_channel_get_connecting_pollset_set(parent_elem); +} + +void grpc_client_uchannel_add_interested_party(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_pollset *pollset) { + grpc_pollset_set *master_pollset_set = + grpc_client_uchannel_get_connecting_pollset_set(elem); + grpc_pollset_set_add_pollset(exec_ctx, master_pollset_set, pollset); +} + +void grpc_client_uchannel_del_interested_party(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_pollset *pollset) { + grpc_pollset_set *master_pollset_set = + grpc_client_uchannel_get_connecting_pollset_set(elem); + grpc_pollset_set_del_pollset(exec_ctx, master_pollset_set, pollset); +} + +grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel, + grpc_channel_args *args) { + grpc_channel *channel = NULL; +#define MAX_FILTERS 3 + const grpc_channel_filter *filters[MAX_FILTERS]; + grpc_mdctx *mdctx = grpc_subchannel_get_mdctx(subchannel); + grpc_channel *master = grpc_subchannel_get_master(subchannel); + char *target = grpc_channel_get_target(master); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + size_t n = 0; + + grpc_mdctx_ref(mdctx); + if (grpc_channel_args_is_census_enabled(args)) { + filters[n++] = &grpc_client_census_filter; + } + filters[n++] = &grpc_compress_filter; + filters[n++] = &grpc_client_uchannel_filter; + GPR_ASSERT(n <= MAX_FILTERS); + + channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n, + args, mdctx, 1); + + gpr_free(target); + return channel; +} + +void grpc_client_uchannel_set_subchannel(grpc_channel *uchannel, + grpc_subchannel *subchannel) { + grpc_channel_element *elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(uchannel)); + channel_data *chand = elem->channel_data; + GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter); + gpr_mu_lock(&chand->mu_state); + chand->subchannel = subchannel; + gpr_mu_unlock(&chand->mu_state); +} diff --git a/src/core/channel/client_uchannel.h b/src/core/channel/client_uchannel.h new file mode 100644 index 0000000000..dfe6695ae3 --- /dev/null +++ b/src/core/channel/client_uchannel.h @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H +#define GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/resolver.h" + +#define GRPC_MICROCHANNEL_SUBCHANNEL_ARG "grpc.microchannel_subchannel_key" + +/* A client microchannel (aka uchannel) is a channel wrapping a subchannel, for + * the purposes of lightweight RPC communications from within the core.*/ + +extern const grpc_channel_filter grpc_client_uchannel_filter; + +grpc_connectivity_state grpc_client_uchannel_check_connectivity_state( + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect); + +void grpc_client_uchannel_watch_connectivity_state( + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_connectivity_state *state, grpc_closure *on_complete); + +grpc_pollset_set *grpc_client_uchannel_get_connecting_pollset_set( + grpc_channel_element *elem); + +void grpc_client_uchannel_add_interested_party(grpc_exec_ctx *exec_ctx, + grpc_channel_element *channel, + grpc_pollset *pollset); +void grpc_client_uchannel_del_interested_party(grpc_exec_ctx *exec_ctx, + grpc_channel_element *channel, + grpc_pollset *pollset); + +grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel, + grpc_channel_args *args); + +void grpc_client_uchannel_set_subchannel(grpc_channel *uchannel, + grpc_subchannel *subchannel); + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H */ diff --git a/src/core/channel/compress_filter.c b/src/core/channel/compress_filter.c index 762a4edc73..20b5084044 100644 --- a/src/core/channel/compress_filter.c +++ b/src/core/channel/compress_filter.c @@ -41,6 +41,7 @@ #include "src/core/channel/compress_filter.h" #include "src/core/channel/channel_args.h" +#include "src/core/profiling/timers.h" #include "src/core/compression/message_compress.h" #include "src/core/support/string.h" @@ -48,7 +49,8 @@ typedef struct call_data { gpr_slice_buffer slices; /**< Buffers up input slices to be compressed */ grpc_linked_mdelem compression_algorithm_storage; grpc_linked_mdelem accept_encoding_storage; - int remaining_slice_bytes; /**< Input data to be read, as per BEGIN_MESSAGE */ + gpr_uint32 remaining_slice_bytes; + /**< Input data to be read, as per BEGIN_MESSAGE */ int written_initial_metadata; /**< Already processed initial md? */ /** Compression algorithm we'll try to use. It may be given by incoming * metadata, or by the channel's default compression settings. */ @@ -70,6 +72,8 @@ typedef struct channel_data { grpc_mdelem *mdelem_accept_encoding; /** The default, channel-level, compression algorithm */ grpc_compression_algorithm default_compression_algorithm; + /** Compression options for the channel */ + grpc_compression_options compression_options; } channel_data; /** Compress \a slices in place using \a algorithm. Returns 1 if compression did @@ -102,7 +106,17 @@ static grpc_mdelem *compression_md_filter(void *user_data, grpc_mdelem *md) { const char *md_c_str = grpc_mdstr_as_c_string(md->value); if (!grpc_compression_algorithm_parse(md_c_str, strlen(md_c_str), &calld->compression_algorithm)) { - gpr_log(GPR_ERROR, "Invalid compression algorithm: '%s'. Ignoring.", + gpr_log(GPR_ERROR, + "Invalid compression algorithm: '%s' (unknown). Ignoring.", + md_c_str); + calld->compression_algorithm = GRPC_COMPRESS_NONE; + } + if (grpc_compression_options_is_algorithm_enabled( + &channeld->compression_options, calld->compression_algorithm) == + 0) { + gpr_log(GPR_ERROR, + "Invalid compression algorithm: '%s' (previously disabled). " + "Ignoring.", md_c_str); calld->compression_algorithm = GRPC_COMPRESS_NONE; } @@ -141,8 +155,9 @@ static void finish_compressed_sopb(grpc_stream_op_buffer *send_ops, grpc_stream_op *sop = &send_ops->ops[i]; switch (sop->type) { case GRPC_OP_BEGIN_MESSAGE: + GPR_ASSERT(calld->slices.length <= GPR_UINT32_MAX); grpc_sopb_add_begin_message( - &new_send_ops, calld->slices.length, + &new_send_ops, (gpr_uint32)calld->slices.length, sop->data.begin_message.flags | GRPC_WRITE_INTERNAL_COMPRESS); break; case GRPC_OP_SLICE: @@ -228,7 +243,10 @@ static void process_send_ops(grpc_call_element *elem, GPR_ASSERT(calld->remaining_slice_bytes > 0); /* Increase input ref count, gpr_slice_buffer_add takes ownership. */ gpr_slice_buffer_add(&calld->slices, gpr_slice_ref(sop->data.slice)); - calld->remaining_slice_bytes -= GPR_SLICE_LENGTH(sop->data.slice); + GPR_ASSERT(GPR_SLICE_LENGTH(sop->data.slice) <= + calld->remaining_slice_bytes); + calld->remaining_slice_bytes -= + (gpr_uint32)GPR_SLICE_LENGTH(sop->data.slice); if (calld->remaining_slice_bytes == 0) { did_compress = compress_send_sb(calld->compression_algorithm, &calld->slices); @@ -251,18 +269,23 @@ static void process_send_ops(grpc_call_element *elem, - a network event (or similar) from below, to receive something op contains type and call direction information, in addition to the data that is being sent or received. */ -static void compress_start_transport_stream_op(grpc_call_element *elem, +static void compress_start_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { + GPR_TIMER_BEGIN("compress_start_transport_stream_op", 0); + if (op->send_ops && op->send_ops->nops > 0) { process_send_ops(elem, op->send_ops); } + GPR_TIMER_END("compress_start_transport_stream_op", 0); + /* pass control down the stack */ - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); } /* Constructor for call_data */ -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { /* grab pointers to our data from the call element */ @@ -281,63 +304,81 @@ static void init_call_elem(grpc_call_element *elem, } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) { +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; gpr_slice_buffer_destroy(&calld->slices); } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { channel_data *channeld = elem->channel_data; grpc_compression_algorithm algo_idx; const char *supported_algorithms_names[GRPC_COMPRESS_ALGORITHMS_COUNT - 1]; + size_t supported_algorithms_idx = 0; char *accept_encoding_str; size_t accept_encoding_str_len; + grpc_compression_options_init(&channeld->compression_options); + channeld->compression_options.enabled_algorithms_bitset = + (gpr_uint32)grpc_channel_args_compression_algorithm_get_states(args); + channeld->default_compression_algorithm = grpc_channel_args_get_compression_algorithm(args); + /* Make sure the default isn't disabled. */ + GPR_ASSERT(grpc_compression_options_is_algorithm_enabled( + &channeld->compression_options, channeld->default_compression_algorithm)); + channeld->compression_options.default_compression_algorithm = + channeld->default_compression_algorithm; channeld->mdstr_request_compression_algorithm_key = - grpc_mdstr_from_string(mdctx, GRPC_COMPRESS_REQUEST_ALGORITHM_KEY, 0); + grpc_mdstr_from_string(mdctx, GRPC_COMPRESS_REQUEST_ALGORITHM_KEY); channeld->mdstr_outgoing_compression_algorithm_key = - grpc_mdstr_from_string(mdctx, "grpc-encoding", 0); + grpc_mdstr_from_string(mdctx, "grpc-encoding"); channeld->mdstr_compression_capabilities_key = - grpc_mdstr_from_string(mdctx, "grpc-accept-encoding", 0); + grpc_mdstr_from_string(mdctx, "grpc-accept-encoding"); for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) { char *algorithm_name; + /* skip disabled algorithms */ + if (grpc_compression_options_is_algorithm_enabled( + &channeld->compression_options, algo_idx) == 0) { + continue; + } GPR_ASSERT(grpc_compression_algorithm_name(algo_idx, &algorithm_name) != 0); channeld->mdelem_compression_algorithms[algo_idx] = grpc_mdelem_from_metadata_strings( mdctx, GRPC_MDSTR_REF(channeld->mdstr_outgoing_compression_algorithm_key), - grpc_mdstr_from_string(mdctx, algorithm_name, 0)); + grpc_mdstr_from_string(mdctx, algorithm_name)); if (algo_idx > 0) { - supported_algorithms_names[algo_idx - 1] = algorithm_name; + supported_algorithms_names[supported_algorithms_idx++] = algorithm_name; } } /* TODO(dgq): gpr_strjoin_sep could be made to work with statically allocated * arrays, as to avoid the heap allocs */ - accept_encoding_str = gpr_strjoin_sep( - supported_algorithms_names, GPR_ARRAY_SIZE(supported_algorithms_names), - ", ", &accept_encoding_str_len); + accept_encoding_str = + gpr_strjoin_sep(supported_algorithms_names, supported_algorithms_idx, ",", + &accept_encoding_str_len); channeld->mdelem_accept_encoding = grpc_mdelem_from_metadata_strings( mdctx, GRPC_MDSTR_REF(channeld->mdstr_compression_capabilities_key), - grpc_mdstr_from_string(mdctx, accept_encoding_str, 0)); + grpc_mdstr_from_string(mdctx, accept_encoding_str)); gpr_free(accept_encoding_str); GPR_ASSERT(!is_last); } /* Destructor for channel data */ -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { channel_data *channeld = elem->channel_data; grpc_compression_algorithm algo_idx; @@ -351,13 +392,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_compress_filter = { - compress_start_transport_stream_op, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_call_next_get_peer, - "compress"}; + compress_start_transport_stream_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, + destroy_channel_elem, grpc_call_next_get_peer, "compress"}; diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index b95ed06f2b..6d4d7be632 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -39,6 +39,7 @@ #include "src/core/support/string.h" #include "src/core/transport/transport.h" +#include "src/core/profiling/timers.h" #include <grpc/byte_buffer.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -61,25 +62,27 @@ typedef struct connected_channel_call_data { void *unused; } call_data; /* Intercept a call operation and either push it directly up or translate it into transport stream operations */ -static void con_start_transport_stream_op(grpc_call_element *elem, +static void con_start_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - grpc_transport_perform_stream_op(chand->transport, + grpc_transport_perform_stream_op(exec_ctx, chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), op); } -static void con_start_transport_op(grpc_channel_element *elem, +static void con_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_transport_op *op) { channel_data *chand = elem->channel_data; - grpc_transport_perform_op(chand->transport, op); + grpc_transport_perform_op(exec_ctx, chand->transport, op); } /* Constructor for call_data */ -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; @@ -87,23 +90,25 @@ static void init_call_elem(grpc_call_element *elem, int r; GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - r = grpc_transport_init_stream(chand->transport, + r = grpc_transport_init_stream(exec_ctx, chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), server_transport_data, initial_op); GPR_ASSERT(r == 0); } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) { +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - grpc_transport_destroy_stream(chand->transport, + grpc_transport_destroy_stream(exec_ctx, chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld)); } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { channel_data *cd = (channel_data *)elem->channel_data; @@ -113,28 +118,22 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, } /* Destructor for channel_data */ -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { channel_data *cd = (channel_data *)elem->channel_data; GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - grpc_transport_destroy(cd->transport); + grpc_transport_destroy(exec_ctx, cd->transport); } -static char *con_get_peer(grpc_call_element *elem) { +static char *con_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { channel_data *chand = elem->channel_data; - return grpc_transport_get_peer(chand->transport); + return grpc_transport_get_peer(exec_ctx, chand->transport); } const grpc_channel_filter grpc_connected_channel_filter = { - con_start_transport_stream_op, - con_start_transport_op, - sizeof(call_data), - init_call_elem, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - con_get_peer, - "connected", + con_start_transport_stream_op, con_start_transport_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, + destroy_channel_elem, con_get_peer, "connected", }; void grpc_connected_channel_bind_transport(grpc_channel_stack *channel_stack, diff --git a/src/core/channel/connected_channel.h b/src/core/channel/connected_channel.h index b615b0d350..eac6eb7ebe 100644 --- a/src/core/channel/connected_channel.h +++ b/src/core/channel/connected_channel.h @@ -43,7 +43,7 @@ extern const grpc_channel_filter grpc_connected_channel_filter; /* Post construction fixup: set the transport in the connected channel. Must be called before any call stack using this filter is used. */ -void grpc_connected_channel_bind_transport(grpc_channel_stack *channel_stack, - grpc_transport *transport); +void grpc_connected_channel_bind_transport(grpc_channel_stack* channel_stack, + grpc_transport* transport); #endif /* GRPC_INTERNAL_CORE_CHANNEL_CONNECTED_CHANNEL_H */ diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c index 2b61d33c29..f78a5cc315 100644 --- a/src/core/channel/http_client_filter.c +++ b/src/core/channel/http_client_filter.c @@ -36,6 +36,7 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> #include "src/core/support/string.h" +#include "src/core/profiling/timers.h" typedef struct call_data { grpc_linked_mdelem method; @@ -50,11 +51,11 @@ typedef struct call_data { grpc_stream_op_buffer *recv_ops; /** Closure to call when finished with the hc_on_recv hook */ - grpc_iomgr_closure *on_done_recv; + grpc_closure *on_done_recv; /** Receive closures are chained: we inject this closure as the on_done_recv up-call on transport_op, and remember to call our on_done_recv member after handling it. */ - grpc_iomgr_closure hc_on_recv; + grpc_closure hc_on_recv; } call_data; typedef struct channel_data { @@ -67,36 +68,42 @@ typedef struct channel_data { grpc_mdelem *user_agent; } channel_data; -/* used to silence 'variable not used' warnings */ -static void ignore_unused(void *ignored) {} +typedef struct { + grpc_call_element *elem; + grpc_exec_ctx *exec_ctx; +} client_recv_filter_args; -static grpc_mdelem *client_filter(void *user_data, grpc_mdelem *md) { - grpc_call_element *elem = user_data; +static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) { + client_recv_filter_args *a = user_data; + grpc_call_element *elem = a->elem; channel_data *channeld = elem->channel_data; if (md == channeld->status) { return NULL; } else if (md->key == channeld->status->key) { - grpc_call_element_send_cancel(elem); + grpc_call_element_send_cancel(a->exec_ctx, elem); + return NULL; + } else if (md->key == channeld->content_type->key) { return NULL; } return md; } -static void hc_on_recv(void *user_data, int success) { +static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; - if (success) { - size_t i; - size_t nops = calld->recv_ops->nops; - grpc_stream_op *ops = calld->recv_ops->ops; - for (i = 0; i < nops; i++) { - grpc_stream_op *op = &ops[i]; - if (op->type != GRPC_OP_METADATA) continue; - calld->got_initial_metadata = 1; - grpc_metadata_batch_filter(&op->data.metadata, client_filter, elem); - } + size_t i; + size_t nops = calld->recv_ops->nops; + grpc_stream_op *ops = calld->recv_ops->ops; + for (i = 0; i < nops; i++) { + grpc_stream_op *op = &ops[i]; + client_recv_filter_args a; + if (op->type != GRPC_OP_METADATA) continue; + calld->got_initial_metadata = 1; + a.elem = elem; + a.exec_ctx = exec_ctx; + grpc_metadata_batch_filter(&op->data.metadata, client_recv_filter, &a); } - calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); + calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success); } static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) { @@ -121,21 +128,25 @@ static void hc_mutate_op(grpc_call_element *elem, size_t nops = op->send_ops->nops; grpc_stream_op *ops = op->send_ops->ops; for (i = 0; i < nops; i++) { - grpc_stream_op *op = &ops[i]; - if (op->type != GRPC_OP_METADATA) continue; + grpc_stream_op *stream_op = &ops[i]; + if (stream_op->type != GRPC_OP_METADATA) continue; calld->sent_initial_metadata = 1; - grpc_metadata_batch_filter(&op->data.metadata, client_strip_filter, elem); + grpc_metadata_batch_filter(&stream_op->data.metadata, client_strip_filter, + elem); /* Send : prefixed headers, which have to be before any application layer headers. */ - grpc_metadata_batch_add_head(&op->data.metadata, &calld->method, + grpc_metadata_batch_add_head(&stream_op->data.metadata, &calld->method, GRPC_MDELEM_REF(channeld->method)); - grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme, + grpc_metadata_batch_add_head(&stream_op->data.metadata, &calld->scheme, GRPC_MDELEM_REF(channeld->scheme)); - grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers, + grpc_metadata_batch_add_tail(&stream_op->data.metadata, + &calld->te_trailers, GRPC_MDELEM_REF(channeld->te_trailers)); - grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type, + grpc_metadata_batch_add_tail(&stream_op->data.metadata, + &calld->content_type, GRPC_MDELEM_REF(channeld->content_type)); - grpc_metadata_batch_add_tail(&op->data.metadata, &calld->user_agent, + grpc_metadata_batch_add_tail(&stream_op->data.metadata, + &calld->user_agent, GRPC_MDELEM_REF(channeld->user_agent)); break; } @@ -149,34 +160,31 @@ static void hc_mutate_op(grpc_call_element *elem, } } -static void hc_start_transport_op(grpc_call_element *elem, +static void hc_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { + GPR_TIMER_BEGIN("hc_start_transport_op", 0); GRPC_CALL_LOG_OP(GPR_INFO, elem, op); hc_mutate_op(elem, op); - grpc_call_next_op(elem, op); + GPR_TIMER_END("hc_start_transport_op", 0); + grpc_call_next_op(exec_ctx, elem, op); } /* Constructor for call_data */ -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; calld->sent_initial_metadata = 0; calld->got_initial_metadata = 0; calld->on_done_recv = NULL; - grpc_iomgr_closure_init(&calld->hc_on_recv, hc_on_recv, elem); + grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem); if (initial_op) hc_mutate_op(elem, initial_op); } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) { - /* grab pointers to our data from the call element */ - call_data *calld = elem->call_data; - channel_data *channeld = elem->channel_data; - - ignore_unused(calld); - ignore_unused(channeld); -} +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) {} static const char *scheme_from_args(const grpc_channel_args *args) { unsigned i; @@ -234,14 +242,15 @@ static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx, tmp = gpr_strvec_flatten(&v, NULL); gpr_strvec_destroy(&v); - result = grpc_mdstr_from_string(mdctx, tmp, 0); + result = grpc_mdstr_from_string(mdctx, tmp); gpr_free(tmp); return result; } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *channel_args, grpc_mdctx *mdctx, int is_first, int is_last) { /* grab pointers to our data from the channel element */ @@ -261,12 +270,13 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc"); channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200"); channeld->user_agent = grpc_mdelem_from_metadata_strings( - mdctx, grpc_mdstr_from_string(mdctx, "user-agent", 0), + mdctx, grpc_mdstr_from_string(mdctx, "user-agent"), user_agent_from_args(mdctx, channel_args)); } /* Destructor for channel data */ -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { /* grab pointers to our data from the channel element */ channel_data *channeld = elem->channel_data; diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c index 0955ae319a..99e5066a4e 100644 --- a/src/core/channel/http_server_filter.c +++ b/src/core/channel/http_server_filter.c @@ -36,6 +36,7 @@ #include <string.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include "src/core/profiling/timers.h" typedef struct call_data { gpr_uint8 got_initial_metadata; @@ -46,14 +47,15 @@ typedef struct call_data { gpr_uint8 seen_te_trailers; gpr_uint8 seen_authority; grpc_linked_mdelem status; + grpc_linked_mdelem content_type; grpc_stream_op_buffer *recv_ops; /** Closure to call when finished with the hs_on_recv hook */ - grpc_iomgr_closure *on_done_recv; + grpc_closure *on_done_recv; /** Receive closures are chained: we inject this closure as the on_done_recv up-call on transport_op, and remember to call our on_done_recv member after handling it. */ - grpc_iomgr_closure hs_on_recv; + grpc_closure hs_on_recv; } call_data; typedef struct channel_data { @@ -73,8 +75,14 @@ typedef struct channel_data { grpc_mdctx *mdctx; } channel_data; +typedef struct { + grpc_call_element *elem; + grpc_exec_ctx *exec_ctx; +} server_filter_args; + static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { - grpc_call_element *elem = user_data; + server_filter_args *a = user_data; + grpc_call_element *elem = a->elem; channel_data *channeld = elem->channel_data; call_data *calld = elem->call_data; @@ -110,14 +118,13 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { return NULL; } else if (md->key == channeld->te_trailers->key || md->key == channeld->method_post->key || - md->key == channeld->http_scheme->key || - md->key == channeld->content_type->key) { + md->key == channeld->http_scheme->key) { gpr_log(GPR_ERROR, "Invalid %s: header: '%s'", grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)); /* swallow it and error everything out. */ /* TODO(klempner): We ought to generate more descriptive error messages on the wire here. */ - grpc_call_element_send_cancel(elem); + grpc_call_element_send_cancel(a->exec_ctx, elem); return NULL; } else if (md->key == channeld->path_key) { if (calld->seen_path) { @@ -143,7 +150,7 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { } } -static void hs_on_recv(void *user_data, int success) { +static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; if (success) { @@ -152,9 +159,12 @@ static void hs_on_recv(void *user_data, int success) { grpc_stream_op *ops = calld->recv_ops->ops; for (i = 0; i < nops; i++) { grpc_stream_op *op = &ops[i]; + server_filter_args a; if (op->type != GRPC_OP_METADATA) continue; calld->got_initial_metadata = 1; - grpc_metadata_batch_filter(&op->data.metadata, server_filter, elem); + a.elem = elem; + a.exec_ctx = exec_ctx; + grpc_metadata_batch_filter(&op->data.metadata, server_filter, &a); /* Have we seen the required http2 transport headers? (:method, :scheme, content-type, with :path and :authority covered at the channel level right now) */ @@ -179,11 +189,11 @@ static void hs_on_recv(void *user_data, int success) { } /* Error this call out */ success = 0; - grpc_call_element_send_cancel(elem); + grpc_call_element_send_cancel(exec_ctx, elem); } } } - calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); + calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success); } static void hs_mutate_op(grpc_call_element *elem, @@ -197,11 +207,14 @@ static void hs_mutate_op(grpc_call_element *elem, size_t nops = op->send_ops->nops; grpc_stream_op *ops = op->send_ops->ops; for (i = 0; i < nops; i++) { - grpc_stream_op *op = &ops[i]; - if (op->type != GRPC_OP_METADATA) continue; + grpc_stream_op *stream_op = &ops[i]; + if (stream_op->type != GRPC_OP_METADATA) continue; calld->sent_status = 1; - grpc_metadata_batch_add_head(&op->data.metadata, &calld->status, + grpc_metadata_batch_add_head(&stream_op->data.metadata, &calld->status, GRPC_MDELEM_REF(channeld->status_ok)); + grpc_metadata_batch_add_tail(&stream_op->data.metadata, + &calld->content_type, + GRPC_MDELEM_REF(channeld->content_type)); break; } } @@ -214,30 +227,35 @@ static void hs_mutate_op(grpc_call_element *elem, } } -static void hs_start_transport_op(grpc_call_element *elem, +static void hs_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { GRPC_CALL_LOG_OP(GPR_INFO, elem, op); + GPR_TIMER_BEGIN("hs_start_transport_op", 0); hs_mutate_op(elem, op); - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); + GPR_TIMER_END("hs_start_transport_op", 0); } /* Constructor for call_data */ -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; /* initialize members */ memset(calld, 0, sizeof(*calld)); - grpc_iomgr_closure_init(&calld->hs_on_recv, hs_on_recv, elem); + grpc_closure_init(&calld->hs_on_recv, hs_on_recv, elem); if (initial_op) hs_mutate_op(elem, initial_op); } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) {} +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) {} /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { /* grab pointers to our data from the channel element */ @@ -258,9 +276,9 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http"); channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https"); channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc"); - channeld->path_key = grpc_mdstr_from_string(mdctx, ":path", 0); - channeld->authority_key = grpc_mdstr_from_string(mdctx, ":authority", 0); - channeld->host_key = grpc_mdstr_from_string(mdctx, "host", 0); + channeld->path_key = grpc_mdstr_from_string(mdctx, ":path"); + channeld->authority_key = grpc_mdstr_from_string(mdctx, ":authority"); + channeld->host_key = grpc_mdstr_from_string(mdctx, "host"); channeld->content_type = grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc"); @@ -268,7 +286,8 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, } /* Destructor for channel data */ -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { /* grab pointers to our data from the channel element */ channel_data *channeld = elem->channel_data; diff --git a/src/core/channel/noop_filter.c b/src/core/channel/noop_filter.c index d631885aaf..48f6b1c650 100644 --- a/src/core/channel/noop_filter.c +++ b/src/core/channel/noop_filter.c @@ -62,16 +62,17 @@ static void noop_mutate_op(grpc_call_element *elem, - a network event (or similar) from below, to receive something op contains type and call direction information, in addition to the data that is being sent or received. */ -static void noop_start_transport_stream_op(grpc_call_element *elem, +static void noop_start_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { noop_mutate_op(elem, op); /* pass control down the stack */ - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); } /* Constructor for call_data */ -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { /* grab pointers to our data from the call element */ @@ -85,17 +86,12 @@ static void init_call_elem(grpc_call_element *elem, } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) { - /* grab pointers to our data from the call element */ - call_data *calld = elem->call_data; - channel_data *channeld = elem->channel_data; - - ignore_unused(calld); - ignore_unused(channeld); -} +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) {} /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { /* grab pointers to our data from the channel element */ @@ -112,20 +108,15 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, } /* Destructor for channel data */ -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { /* grab pointers to our data from the channel element */ channel_data *channeld = elem->channel_data; ignore_unused(channeld); } -const grpc_channel_filter grpc_no_op_filter = {noop_start_transport_stream_op, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_call_next_get_peer, - "no-op"}; +const grpc_channel_filter grpc_no_op_filter = { + noop_start_transport_stream_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, + destroy_channel_elem, grpc_call_next_get_peer, "no-op"}; diff --git a/src/core/client_config/client_config.c b/src/core/client_config/client_config.c index 4453824148..6ecffb3854 100644 --- a/src/core/client_config/client_config.c +++ b/src/core/client_config/client_config.c @@ -51,21 +51,19 @@ grpc_client_config *grpc_client_config_create() { void grpc_client_config_ref(grpc_client_config *c) { gpr_ref(&c->refs); } -void grpc_client_config_unref(grpc_client_config *c) { +void grpc_client_config_unref(grpc_exec_ctx *exec_ctx, grpc_client_config *c) { if (gpr_unref(&c->refs)) { - GRPC_LB_POLICY_UNREF(c->lb_policy, "client_config"); + GRPC_LB_POLICY_UNREF(exec_ctx, c->lb_policy, "client_config"); gpr_free(c); } } void grpc_client_config_set_lb_policy(grpc_client_config *c, grpc_lb_policy *lb_policy) { + GPR_ASSERT(c->lb_policy == NULL); if (lb_policy) { GRPC_LB_POLICY_REF(lb_policy, "client_config"); } - if (c->lb_policy) { - GRPC_LB_POLICY_UNREF(c->lb_policy, "client_config"); - } c->lb_policy = lb_policy; } diff --git a/src/core/client_config/client_config.h b/src/core/client_config/client_config.h index 47612da42c..04bf036b00 100644 --- a/src/core/client_config/client_config.h +++ b/src/core/client_config/client_config.h @@ -42,7 +42,8 @@ typedef struct grpc_client_config grpc_client_config; grpc_client_config *grpc_client_config_create(); void grpc_client_config_ref(grpc_client_config *client_config); -void grpc_client_config_unref(grpc_client_config *client_config); +void grpc_client_config_unref(grpc_exec_ctx *exec_ctx, + grpc_client_config *client_config); void grpc_client_config_set_lb_policy(grpc_client_config *client_config, grpc_lb_policy *lb_policy); diff --git a/src/core/client_config/connector.c b/src/core/client_config/connector.c index a8cd5fc149..1603ffb8be 100644 --- a/src/core/client_config/connector.c +++ b/src/core/client_config/connector.c @@ -33,17 +33,22 @@ #include "src/core/client_config/connector.h" -void grpc_connector_ref(grpc_connector *connector) { +void grpc_connector_ref(grpc_connector* connector) { connector->vtable->ref(connector); } -void grpc_connector_unref(grpc_connector *connector) { - connector->vtable->unref(connector); +void grpc_connector_unref(grpc_exec_ctx* exec_ctx, grpc_connector* connector) { + connector->vtable->unref(exec_ctx, connector); } -void grpc_connector_connect(grpc_connector *connector, - const grpc_connect_in_args *in_args, - grpc_connect_out_args *out_args, - grpc_iomgr_closure *notify) { - connector->vtable->connect(connector, in_args, out_args, notify); +void grpc_connector_connect(grpc_exec_ctx* exec_ctx, grpc_connector* connector, + const grpc_connect_in_args* in_args, + grpc_connect_out_args* out_args, + grpc_closure* notify) { + connector->vtable->connect(exec_ctx, connector, in_args, out_args, notify); +} + +void grpc_connector_shutdown(grpc_exec_ctx* exec_ctx, + grpc_connector* connector) { + connector->vtable->shutdown(exec_ctx, connector); } diff --git a/src/core/client_config/connector.h b/src/core/client_config/connector.h index edcb10a36e..e9b8be4b53 100644 --- a/src/core/client_config/connector.h +++ b/src/core/client_config/connector.h @@ -50,13 +50,11 @@ typedef struct { grpc_pollset_set *interested_parties; /** address to connect to */ const struct sockaddr *addr; - int addr_len; + size_t addr_len; /** deadline for connection */ gpr_timespec deadline; /** channel arguments (to be passed to transport) */ const grpc_channel_args *channel_args; - /** metadata context */ - grpc_mdctx *metadata_context; } grpc_connect_in_args; typedef struct { @@ -69,17 +67,24 @@ typedef struct { struct grpc_connector_vtable { void (*ref)(grpc_connector *connector); - void (*unref)(grpc_connector *connector); - void (*connect)(grpc_connector *connector, + void (*unref)(grpc_exec_ctx *exec_ctx, grpc_connector *connector); + /** Implementation of grpc_connector_shutdown */ + void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_connector *connector); + /** Implementation of grpc_connector_connect */ + void (*connect)(grpc_exec_ctx *exec_ctx, grpc_connector *connector, const grpc_connect_in_args *in_args, - grpc_connect_out_args *out_args, grpc_iomgr_closure *notify); + grpc_connect_out_args *out_args, grpc_closure *notify); }; void grpc_connector_ref(grpc_connector *connector); -void grpc_connector_unref(grpc_connector *connector); -void grpc_connector_connect(grpc_connector *connector, +void grpc_connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *connector); +/** Connect using the connector: max one outstanding call at a time */ +void grpc_connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *connector, const grpc_connect_in_args *in_args, grpc_connect_out_args *out_args, - grpc_iomgr_closure *notify); + grpc_closure *notify); +/** Cancel any pending connection */ +void grpc_connector_shutdown(grpc_exec_ctx *exec_ctx, + grpc_connector *connector); #endif diff --git a/src/core/client_config/lb_policies/pick_first.c b/src/core/client_config/lb_policies/pick_first.c index 5ae2e0ea52..e5bf0680ff 100644 --- a/src/core/client_config/lb_policies/pick_first.c +++ b/src/core/client_config/lb_policies/pick_first.c @@ -31,6 +31,7 @@ * */ +#include "src/core/client_config/lb_policy_factory.h" #include "src/core/client_config/lb_policies/pick_first.h" #include <string.h> @@ -42,7 +43,7 @@ typedef struct pending_pick { struct pending_pick *next; grpc_pollset *pollset; grpc_subchannel **target; - grpc_iomgr_closure *on_complete; + grpc_closure *on_complete; } pending_pick; typedef struct { @@ -52,7 +53,7 @@ typedef struct { grpc_subchannel **subchannels; size_t num_subchannels; - grpc_iomgr_closure connectivity_changed; + grpc_closure connectivity_changed; /** mutex protecting remaining members */ gpr_mu mu; @@ -75,87 +76,95 @@ typedef struct { grpc_connectivity_state_tracker state_tracker; } pick_first_lb_policy; -static void del_interested_parties_locked(pick_first_lb_policy *p) { +static void del_interested_parties_locked(grpc_exec_ctx *exec_ctx, + pick_first_lb_policy *p) { pending_pick *pp; for (pp = p->pending_picks; pp; pp = pp->next) { - grpc_subchannel_del_interested_party(p->subchannels[p->checking_subchannel], - pp->pollset); + grpc_subchannel_del_interested_party( + exec_ctx, p->subchannels[p->checking_subchannel], pp->pollset); } } -static void add_interested_parties_locked(pick_first_lb_policy *p) { +static void add_interested_parties_locked(grpc_exec_ctx *exec_ctx, + pick_first_lb_policy *p) { pending_pick *pp; for (pp = p->pending_picks; pp; pp = pp->next) { - grpc_subchannel_add_interested_party(p->subchannels[p->checking_subchannel], - pp->pollset); + grpc_subchannel_add_interested_party( + exec_ctx, p->subchannels[p->checking_subchannel], pp->pollset); } } -void pf_destroy(grpc_lb_policy *pol) { +void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; size_t i; - del_interested_parties_locked(p); + GPR_ASSERT(p->pending_picks == NULL); for (i = 0; i < p->num_subchannels; i++) { - GRPC_SUBCHANNEL_UNREF(p->subchannels[i], "pick_first"); + GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "pick_first"); } - grpc_connectivity_state_destroy(&p->state_tracker); + if (p->selected) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, p->selected, "picked_first"); + } + grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); gpr_free(p->subchannels); gpr_mu_destroy(&p->mu); gpr_free(p); } -void pf_shutdown(grpc_lb_policy *pol) { +void pf_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; pending_pick *pp; gpr_mu_lock(&p->mu); - del_interested_parties_locked(p); + del_interested_parties_locked(exec_ctx, p); p->shutdown = 1; - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; + pp = p->pending_picks; + p->pending_picks = NULL; + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE, "shutdown"); + gpr_mu_unlock(&p->mu); + while (pp != NULL) { + pending_pick *next = pp->next; *pp->target = NULL; - grpc_iomgr_add_delayed_callback(pp->on_complete, 0); + grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); + pp = next; } - grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, - "shutdown"); - gpr_mu_unlock(&p->mu); } -static void start_picking(pick_first_lb_policy *p) { +static void start_picking(grpc_exec_ctx *exec_ctx, pick_first_lb_policy *p) { p->started_picking = 1; p->checking_subchannel = 0; p->checking_connectivity = GRPC_CHANNEL_IDLE; GRPC_LB_POLICY_REF(&p->base, "pick_first_connectivity"); - grpc_subchannel_notify_on_state_change(p->subchannels[p->checking_subchannel], - &p->checking_connectivity, - &p->connectivity_changed); + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], + &p->checking_connectivity, &p->connectivity_changed); } -void pf_exit_idle(grpc_lb_policy *pol) { +void pf_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; gpr_mu_lock(&p->mu); if (!p->started_picking) { - start_picking(p); + start_picking(exec_ctx, p); } gpr_mu_unlock(&p->mu); } -void pf_pick(grpc_lb_policy *pol, grpc_pollset *pollset, - grpc_metadata_batch *initial_metadata, grpc_subchannel **target, - grpc_iomgr_closure *on_complete) { +void pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, + grpc_subchannel **target, grpc_closure *on_complete) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; pending_pick *pp; gpr_mu_lock(&p->mu); if (p->selected) { gpr_mu_unlock(&p->mu); *target = p->selected; - on_complete->cb(on_complete->cb_arg, 1); + grpc_exec_ctx_enqueue(exec_ctx, on_complete, 1); } else { if (!p->started_picking) { - start_picking(p); + start_picking(exec_ctx, p); } - grpc_subchannel_add_interested_party(p->subchannels[p->checking_subchannel], - pollset); + grpc_subchannel_add_interested_party( + exec_ctx, p->subchannels[p->checking_subchannel], pollset); pp = gpr_malloc(sizeof(*pp)); pp->next = p->pending_picks; pp->pollset = pollset; @@ -166,113 +175,157 @@ void pf_pick(grpc_lb_policy *pol, grpc_pollset *pollset, } } -static void pf_connectivity_changed(void *arg, int iomgr_success) { +static void destroy_subchannels(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + pick_first_lb_policy *p = arg; + size_t i; + grpc_transport_op op; + size_t num_subchannels = p->num_subchannels; + grpc_subchannel **subchannels; + grpc_subchannel *exclude_subchannel; + + gpr_mu_lock(&p->mu); + subchannels = p->subchannels; + p->num_subchannels = 0; + p->subchannels = NULL; + exclude_subchannel = p->selected; + gpr_mu_unlock(&p->mu); + GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "destroy_subchannels"); + + for (i = 0; i < num_subchannels; i++) { + if (subchannels[i] != exclude_subchannel) { + memset(&op, 0, sizeof(op)); + op.disconnect = 1; + grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], &op); + } + GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pick_first"); + } + + gpr_free(subchannels); +} + +static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { pick_first_lb_policy *p = arg; pending_pick *pp; - int unref = 0; gpr_mu_lock(&p->mu); if (p->shutdown) { - unref = 1; + gpr_mu_unlock(&p->mu); + GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); + return; } else if (p->selected != NULL) { - grpc_connectivity_state_set(&p->state_tracker, p->checking_connectivity, - "selected_changed"); + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + p->checking_connectivity, "selected_changed"); if (p->checking_connectivity != GRPC_CHANNEL_FATAL_FAILURE) { - grpc_subchannel_notify_on_state_change( - p->selected, &p->checking_connectivity, &p->connectivity_changed); + grpc_subchannel_notify_on_state_change(exec_ctx, p->selected, + &p->checking_connectivity, + &p->connectivity_changed); } else { - unref = 1; + GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); } } else { loop: switch (p->checking_connectivity) { case GRPC_CHANNEL_READY: - grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_READY, - "connecting_ready"); + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_READY, "connecting_ready"); p->selected = p->subchannels[p->checking_subchannel]; + GRPC_SUBCHANNEL_REF(p->selected, "picked_first"); + /* drop the pick list: we are connected now */ + GRPC_LB_POLICY_REF(&p->base, "destroy_subchannels"); + grpc_exec_ctx_enqueue(exec_ctx, + grpc_closure_create(destroy_subchannels, p), 1); + /* update any calls that were waiting for a pick */ while ((pp = p->pending_picks)) { p->pending_picks = pp->next; *pp->target = p->selected; - grpc_subchannel_del_interested_party(p->selected, pp->pollset); - grpc_iomgr_add_delayed_callback(pp->on_complete, 1); + grpc_subchannel_del_interested_party(exec_ctx, p->selected, + pp->pollset); + grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); } - grpc_subchannel_notify_on_state_change( - p->selected, &p->checking_connectivity, &p->connectivity_changed); + grpc_subchannel_notify_on_state_change(exec_ctx, p->selected, + &p->checking_connectivity, + &p->connectivity_changed); break; case GRPC_CHANNEL_TRANSIENT_FAILURE: - grpc_connectivity_state_set(&p->state_tracker, + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, "connecting_transient_failure"); - del_interested_parties_locked(p); + del_interested_parties_locked(exec_ctx, p); p->checking_subchannel = (p->checking_subchannel + 1) % p->num_subchannels; p->checking_connectivity = grpc_subchannel_check_connectivity( p->subchannels[p->checking_subchannel]); - add_interested_parties_locked(p); + add_interested_parties_locked(exec_ctx, p); if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { grpc_subchannel_notify_on_state_change( - p->subchannels[p->checking_subchannel], &p->checking_connectivity, - &p->connectivity_changed); + exec_ctx, p->subchannels[p->checking_subchannel], + &p->checking_connectivity, &p->connectivity_changed); } else { goto loop; } break; case GRPC_CHANNEL_CONNECTING: case GRPC_CHANNEL_IDLE: - grpc_connectivity_state_set(&p->state_tracker, p->checking_connectivity, + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_CONNECTING, "connecting_changed"); grpc_subchannel_notify_on_state_change( - p->subchannels[p->checking_subchannel], &p->checking_connectivity, - &p->connectivity_changed); + exec_ctx, p->subchannels[p->checking_subchannel], + &p->checking_connectivity, &p->connectivity_changed); break; case GRPC_CHANNEL_FATAL_FAILURE: - del_interested_parties_locked(p); + del_interested_parties_locked(exec_ctx, p); GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], p->subchannels[p->num_subchannels - 1]); p->num_subchannels--; - GRPC_SUBCHANNEL_UNREF(p->subchannels[p->num_subchannels], "pick_first"); + GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels], + "pick_first"); if (p->num_subchannels == 0) { - grpc_connectivity_state_set(&p->state_tracker, + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, "no_more_channels"); while ((pp = p->pending_picks)) { p->pending_picks = pp->next; *pp->target = NULL; - grpc_iomgr_add_delayed_callback(pp->on_complete, 1); + grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); } - unref = 1; + GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); } else { - grpc_connectivity_state_set(&p->state_tracker, + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, "subchannel_failed"); p->checking_subchannel %= p->num_subchannels; p->checking_connectivity = grpc_subchannel_check_connectivity( p->subchannels[p->checking_subchannel]); - add_interested_parties_locked(p); + add_interested_parties_locked(exec_ctx, p); goto loop; } } } gpr_mu_unlock(&p->mu); - - if (unref) { - GRPC_LB_POLICY_UNREF(&p->base, "pick_first_connectivity"); - } } -static void pf_broadcast(grpc_lb_policy *pol, grpc_transport_op *op) { +static void pf_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_transport_op *op) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; size_t i; size_t n; grpc_subchannel **subchannels; + grpc_subchannel *selected; gpr_mu_lock(&p->mu); n = p->num_subchannels; subchannels = gpr_malloc(n * sizeof(*subchannels)); + selected = p->selected; + if (selected) { + GRPC_SUBCHANNEL_REF(selected, "pf_broadcast_to_selected"); + } for (i = 0; i < n; i++) { subchannels[i] = p->subchannels[i]; GRPC_SUBCHANNEL_REF(subchannels[i], "pf_broadcast"); @@ -280,13 +333,19 @@ static void pf_broadcast(grpc_lb_policy *pol, grpc_transport_op *op) { gpr_mu_unlock(&p->mu); for (i = 0; i < n; i++) { - grpc_subchannel_process_transport_op(subchannels[i], op); - GRPC_SUBCHANNEL_UNREF(subchannels[i], "pf_broadcast"); + if (selected == subchannels[i]) continue; + grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], op); + GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pf_broadcast"); + } + if (p->selected) { + grpc_subchannel_process_transport_op(exec_ctx, selected, op); + GRPC_SUBCHANNEL_UNREF(exec_ctx, selected, "pf_broadcast_to_selected"); } gpr_free(subchannels); } -static grpc_connectivity_state pf_check_connectivity(grpc_lb_policy *pol) { +static grpc_connectivity_state pf_check_connectivity(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *pol) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; grpc_connectivity_state st; gpr_mu_lock(&p->mu); @@ -295,38 +354,49 @@ static grpc_connectivity_state pf_check_connectivity(grpc_lb_policy *pol) { return st; } -static void pf_notify_on_state_change(grpc_lb_policy *pol, - grpc_connectivity_state *current, - grpc_iomgr_closure *notify) { +void pf_notify_on_state_change(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_connectivity_state *current, + grpc_closure *notify) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; gpr_mu_lock(&p->mu); - grpc_connectivity_state_notify_on_state_change(&p->state_tracker, current, - notify); + grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker, + current, notify); gpr_mu_unlock(&p->mu); } static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = { - pf_destroy, - pf_shutdown, - pf_pick, - pf_exit_idle, - pf_broadcast, - pf_check_connectivity, - pf_notify_on_state_change}; - -grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels, - size_t num_subchannels) { + pf_destroy, pf_shutdown, pf_pick, pf_exit_idle, pf_broadcast, + pf_check_connectivity, pf_notify_on_state_change}; + +static void pick_first_factory_ref(grpc_lb_policy_factory *factory) {} + +static void pick_first_factory_unref(grpc_lb_policy_factory *factory) {} + +static grpc_lb_policy *create_pick_first(grpc_lb_policy_factory *factory, + grpc_lb_policy_args *args) { pick_first_lb_policy *p = gpr_malloc(sizeof(*p)); - GPR_ASSERT(num_subchannels); + GPR_ASSERT(args->num_subchannels > 0); memset(p, 0, sizeof(*p)); grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable); - p->subchannels = gpr_malloc(sizeof(grpc_subchannel *) * num_subchannels); - p->num_subchannels = num_subchannels; + p->subchannels = + gpr_malloc(sizeof(grpc_subchannel *) * args->num_subchannels); + p->num_subchannels = args->num_subchannels; grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, "pick_first"); - memcpy(p->subchannels, subchannels, - sizeof(grpc_subchannel *) * num_subchannels); - grpc_iomgr_closure_init(&p->connectivity_changed, pf_connectivity_changed, p); + memcpy(p->subchannels, args->subchannels, + sizeof(grpc_subchannel *) * args->num_subchannels); + grpc_closure_init(&p->connectivity_changed, pf_connectivity_changed, p); gpr_mu_init(&p->mu); return &p->base; } + +static const grpc_lb_policy_factory_vtable pick_first_factory_vtable = { + pick_first_factory_ref, pick_first_factory_unref, create_pick_first, + "pick_first"}; + +static grpc_lb_policy_factory pick_first_lb_policy_factory = { + &pick_first_factory_vtable}; + +grpc_lb_policy_factory *grpc_pick_first_lb_factory_create() { + return &pick_first_lb_policy_factory; +} diff --git a/src/core/client_config/lb_policies/pick_first.h b/src/core/client_config/lb_policies/pick_first.h index 31394985e5..3ca53ad42a 100644 --- a/src/core/client_config/lb_policies/pick_first.h +++ b/src/core/client_config/lb_policies/pick_first.h @@ -34,11 +34,10 @@ #ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_PICK_FIRST_H #define GRPC_INTERNAL_CORE_CLIENT_CONFIG_PICK_FIRST_H -#include "src/core/client_config/lb_policy.h" +#include "src/core/client_config/lb_policy_factory.h" -/** Returns a load balancing policy instance that picks up the first subchannel - * from \a subchannels to succesfully connect */ -grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels, - size_t num_subchannels); +/** Returns a load balancing factory for the pick first policy, which picks up + * the first subchannel from \a subchannels to succesfully connect */ +grpc_lb_policy_factory *grpc_pick_first_lb_factory_create(); #endif diff --git a/src/core/client_config/lb_policies/round_robin.c b/src/core/client_config/lb_policies/round_robin.c new file mode 100644 index 0000000000..d0b60a0df2 --- /dev/null +++ b/src/core/client_config/lb_policies/round_robin.c @@ -0,0 +1,549 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/client_config/lb_policies/round_robin.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include "src/core/transport/connectivity_state.h" + +int grpc_lb_round_robin_trace = 0; + +/** List of entities waiting for a pick. + * + * Once a pick is available, \a target is updated and \a on_complete called. */ +typedef struct pending_pick { + struct pending_pick *next; + grpc_pollset *pollset; + grpc_subchannel **target; + grpc_closure *on_complete; +} pending_pick; + +/** List of subchannels in a connectivity READY state */ +typedef struct ready_list { + grpc_subchannel *subchannel; + struct ready_list *next; + struct ready_list *prev; +} ready_list; + +typedef struct { + size_t subchannel_idx; /**< Index over p->subchannels */ + void *p; /**< round_robin_lb_policy instance */ +} connectivity_changed_cb_arg; + +typedef struct { + /** base policy: must be first */ + grpc_lb_policy base; + + /** all our subchannels */ + grpc_subchannel **subchannels; + size_t num_subchannels; + + /** Callbacks, one per subchannel being watched, to be called when their + * respective connectivity changes */ + grpc_closure *connectivity_changed_cbs; + connectivity_changed_cb_arg *cb_args; + + /** mutex protecting remaining members */ + gpr_mu mu; + /** have we started picking? */ + int started_picking; + /** are we shutting down? */ + int shutdown; + /** Connectivity state of the subchannels being watched */ + grpc_connectivity_state *subchannel_connectivity; + /** List of picks that are waiting on connectivity */ + pending_pick *pending_picks; + + /** our connectivity state tracker */ + grpc_connectivity_state_tracker state_tracker; + + /** (Dummy) root of the doubly linked list containing READY subchannels */ + ready_list ready_list; + /** Last pick from the ready list. */ + ready_list *ready_list_last_pick; + + /** Subchannel index to ready_list node. + * + * Kept in order to remove nodes from the ready list associated with a + * subchannel */ + ready_list **subchannel_index_to_readylist_node; +} round_robin_lb_policy; + +/** Returns the next subchannel from the connected list or NULL if the list is + * empty. + * + * Note that this function does *not* advance p->ready_list_last_pick. Use \a + * advance_last_picked_locked() for that. */ +static ready_list *peek_next_connected_locked(const round_robin_lb_policy *p) { + ready_list *selected; + selected = p->ready_list_last_pick->next; + + while (selected != NULL) { + if (selected == &p->ready_list) { + GPR_ASSERT(selected->subchannel == NULL); + /* skip dummy root */ + selected = selected->next; + } else { + GPR_ASSERT(selected->subchannel != NULL); + return selected; + } + } + return NULL; +} + +/** Advance the \a ready_list picking head. */ +static void advance_last_picked_locked(round_robin_lb_policy *p) { + if (p->ready_list_last_pick->next != NULL) { /* non-empty list */ + p->ready_list_last_pick = p->ready_list_last_pick->next; + if (p->ready_list_last_pick == &p->ready_list) { + /* skip dummy root */ + p->ready_list_last_pick = p->ready_list_last_pick->next; + } + } else { /* should be an empty list */ + GPR_ASSERT(p->ready_list_last_pick == &p->ready_list); + } + + if (grpc_lb_round_robin_trace) { + gpr_log(GPR_DEBUG, "[READYLIST] ADVANCED LAST PICK. NOW AT NODE %p (SC %p)", + p->ready_list_last_pick, p->ready_list_last_pick->subchannel); + } +} + +/** Prepends (relative to the root at p->ready_list) the connected subchannel \a + * csc to the list of ready subchannels. */ +static ready_list *add_connected_sc_locked(round_robin_lb_policy *p, + grpc_subchannel *csc) { + ready_list *new_elem = gpr_malloc(sizeof(ready_list)); + new_elem->subchannel = csc; + if (p->ready_list.prev == NULL) { + /* first element */ + new_elem->next = &p->ready_list; + new_elem->prev = &p->ready_list; + p->ready_list.next = new_elem; + p->ready_list.prev = new_elem; + } else { + new_elem->next = &p->ready_list; + new_elem->prev = p->ready_list.prev; + p->ready_list.prev->next = new_elem; + p->ready_list.prev = new_elem; + } + if (grpc_lb_round_robin_trace) { + gpr_log(GPR_DEBUG, "[READYLIST] ADDING NODE %p (SC %p)", new_elem, csc); + } + return new_elem; +} + +/** Removes \a node from the list of connected subchannels */ +static void remove_disconnected_sc_locked(round_robin_lb_policy *p, + ready_list *node) { + if (node == NULL) { + return; + } + if (node == p->ready_list_last_pick) { + /* If removing the lastly picked node, reset the last pick pointer to the + * dummy root of the list */ + p->ready_list_last_pick = &p->ready_list; + } + + /* removing last item */ + if (node->next == &p->ready_list && node->prev == &p->ready_list) { + GPR_ASSERT(p->ready_list.next == node); + GPR_ASSERT(p->ready_list.prev == node); + p->ready_list.next = NULL; + p->ready_list.prev = NULL; + } else { + node->prev->next = node->next; + node->next->prev = node->prev; + } + + if (grpc_lb_round_robin_trace) { + gpr_log(GPR_DEBUG, "[READYLIST] REMOVED NODE %p (SC %p)", node, + node->subchannel); + } + + node->next = NULL; + node->prev = NULL; + node->subchannel = NULL; + + gpr_free(node); +} + +static void del_interested_parties_locked(grpc_exec_ctx *exec_ctx, + round_robin_lb_policy *p, + const size_t subchannel_idx) { + pending_pick *pp; + for (pp = p->pending_picks; pp; pp = pp->next) { + grpc_subchannel_del_interested_party( + exec_ctx, p->subchannels[subchannel_idx], pp->pollset); + } +} + +void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + size_t i; + ready_list *elem; + for (i = 0; i < p->num_subchannels; i++) { + del_interested_parties_locked(exec_ctx, p, i); + } + for (i = 0; i < p->num_subchannels; i++) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "round_robin"); + } + gpr_free(p->connectivity_changed_cbs); + gpr_free(p->subchannel_connectivity); + + grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); + gpr_free(p->subchannels); + gpr_mu_destroy(&p->mu); + + elem = p->ready_list.next; + while (elem != NULL && elem != &p->ready_list) { + ready_list *tmp; + tmp = elem->next; + elem->next = NULL; + elem->prev = NULL; + elem->subchannel = NULL; + gpr_free(elem); + elem = tmp; + } + gpr_free(p->subchannel_index_to_readylist_node); + gpr_free(p->cb_args); + gpr_free(p); +} + +void rr_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + size_t i; + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + pending_pick *pp; + gpr_mu_lock(&p->mu); + + for (i = 0; i < p->num_subchannels; i++) { + del_interested_parties_locked(exec_ctx, p, i); + } + + p->shutdown = 1; + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 0); + gpr_free(pp); + } + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE, "shutdown"); + gpr_mu_unlock(&p->mu); +} + +static void start_picking(grpc_exec_ctx *exec_ctx, round_robin_lb_policy *p) { + size_t i; + p->started_picking = 1; + + for (i = 0; i < p->num_subchannels; i++) { + p->subchannel_connectivity[i] = GRPC_CHANNEL_IDLE; + grpc_subchannel_notify_on_state_change(exec_ctx, p->subchannels[i], + &p->subchannel_connectivity[i], + &p->connectivity_changed_cbs[i]); + GRPC_LB_POLICY_REF(&p->base, "round_robin_connectivity"); + } +} + +void rr_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + gpr_mu_lock(&p->mu); + if (!p->started_picking) { + start_picking(exec_ctx, p); + } + gpr_mu_unlock(&p->mu); +} + +void rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, + grpc_subchannel **target, grpc_closure *on_complete) { + size_t i; + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + pending_pick *pp; + ready_list *selected; + gpr_mu_lock(&p->mu); + if ((selected = peek_next_connected_locked(p))) { + gpr_mu_unlock(&p->mu); + *target = selected->subchannel; + if (grpc_lb_round_robin_trace) { + gpr_log(GPR_DEBUG, "[RR PICK] TARGET <-- SUBCHANNEL %p (NODE %p)", + selected->subchannel, selected); + } + /* only advance the last picked pointer if the selection was used */ + advance_last_picked_locked(p); + on_complete->cb(exec_ctx, on_complete->cb_arg, 1); + } else { + if (!p->started_picking) { + start_picking(exec_ctx, p); + } + for (i = 0; i < p->num_subchannels; i++) { + grpc_subchannel_add_interested_party(exec_ctx, p->subchannels[i], + pollset); + } + pp = gpr_malloc(sizeof(*pp)); + pp->next = p->pending_picks; + pp->pollset = pollset; + pp->target = target; + pp->on_complete = on_complete; + p->pending_picks = pp; + gpr_mu_unlock(&p->mu); + } +} + +static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + connectivity_changed_cb_arg *cb_arg = arg; + round_robin_lb_policy *p = cb_arg->p; + /* index over p->subchannels of this cb's subchannel */ + const size_t this_idx = cb_arg->subchannel_idx; + pending_pick *pp; + ready_list *selected; + + int unref = 0; + + /* connectivity state of this cb's subchannel */ + grpc_connectivity_state *this_connectivity; + + gpr_mu_lock(&p->mu); + + this_connectivity = &p->subchannel_connectivity[this_idx]; + + if (p->shutdown) { + unref = 1; + } else { + switch (*this_connectivity) { + case GRPC_CHANNEL_READY: + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_READY, "connecting_ready"); + /* add the newly connected subchannel to the list of connected ones. + * Note that it goes to the "end of the line". */ + p->subchannel_index_to_readylist_node[this_idx] = + add_connected_sc_locked(p, p->subchannels[this_idx]); + /* at this point we know there's at least one suitable subchannel. Go + * ahead and pick one and notify the pending suitors in + * p->pending_picks. This preemtively replicates rr_pick()'s actions. */ + selected = peek_next_connected_locked(p); + if (p->pending_picks != NULL) { + /* if the selected subchannel is going to be used for the pending + * picks, update the last picked pointer */ + advance_last_picked_locked(p); + } + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = selected->subchannel; + if (grpc_lb_round_robin_trace) { + gpr_log(GPR_DEBUG, + "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)", + selected->subchannel, selected); + } + grpc_subchannel_del_interested_party(exec_ctx, selected->subchannel, + pp->pollset); + grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); + gpr_free(pp); + } + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[this_idx], this_connectivity, + &p->connectivity_changed_cbs[this_idx]); + break; + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_IDLE: + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + *this_connectivity, "connecting_changed"); + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[this_idx], this_connectivity, + &p->connectivity_changed_cbs[this_idx]); + break; + case GRPC_CHANNEL_TRANSIENT_FAILURE: + del_interested_parties_locked(exec_ctx, p, this_idx); + /* renew state notification */ + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[this_idx], this_connectivity, + &p->connectivity_changed_cbs[this_idx]); + + /* remove from ready list if still present */ + if (p->subchannel_index_to_readylist_node[this_idx] != NULL) { + remove_disconnected_sc_locked( + p, p->subchannel_index_to_readylist_node[this_idx]); + p->subchannel_index_to_readylist_node[this_idx] = NULL; + } + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_TRANSIENT_FAILURE, + "connecting_transient_failure"); + break; + case GRPC_CHANNEL_FATAL_FAILURE: + del_interested_parties_locked(exec_ctx, p, this_idx); + if (p->subchannel_index_to_readylist_node[this_idx] != NULL) { + remove_disconnected_sc_locked( + p, p->subchannel_index_to_readylist_node[this_idx]); + p->subchannel_index_to_readylist_node[this_idx] = NULL; + } + + GPR_SWAP(grpc_subchannel *, p->subchannels[this_idx], + p->subchannels[p->num_subchannels - 1]); + p->num_subchannels--; + GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels], + "round_robin"); + + if (p->num_subchannels == 0) { + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE, + "no_more_channels"); + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); + gpr_free(pp); + } + unref = 1; + } else { + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_TRANSIENT_FAILURE, + "subchannel_failed"); + } + } /* switch */ + } /* !unref */ + + gpr_mu_unlock(&p->mu); + + if (unref) { + GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "round_robin_connectivity"); + } +} + +static void rr_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_transport_op *op) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + size_t i; + size_t n; + grpc_subchannel **subchannels; + + gpr_mu_lock(&p->mu); + n = p->num_subchannels; + subchannels = gpr_malloc(n * sizeof(*subchannels)); + for (i = 0; i < n; i++) { + subchannels[i] = p->subchannels[i]; + GRPC_SUBCHANNEL_REF(subchannels[i], "rr_broadcast"); + } + gpr_mu_unlock(&p->mu); + + for (i = 0; i < n; i++) { + grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], op); + GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "rr_broadcast"); + } + gpr_free(subchannels); +} + +static grpc_connectivity_state rr_check_connectivity(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *pol) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + grpc_connectivity_state st; + gpr_mu_lock(&p->mu); + st = grpc_connectivity_state_check(&p->state_tracker); + gpr_mu_unlock(&p->mu); + return st; +} + +static void rr_notify_on_state_change(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *pol, + grpc_connectivity_state *current, + grpc_closure *notify) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + gpr_mu_lock(&p->mu); + grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker, + current, notify); + gpr_mu_unlock(&p->mu); +} + +static const grpc_lb_policy_vtable round_robin_lb_policy_vtable = { + rr_destroy, rr_shutdown, rr_pick, rr_exit_idle, rr_broadcast, + rr_check_connectivity, rr_notify_on_state_change}; + +static void round_robin_factory_ref(grpc_lb_policy_factory *factory) {} + +static void round_robin_factory_unref(grpc_lb_policy_factory *factory) {} + +static grpc_lb_policy *create_round_robin(grpc_lb_policy_factory *factory, + grpc_lb_policy_args *args) { + size_t i; + round_robin_lb_policy *p = gpr_malloc(sizeof(*p)); + GPR_ASSERT(args->num_subchannels > 0); + memset(p, 0, sizeof(*p)); + grpc_lb_policy_init(&p->base, &round_robin_lb_policy_vtable); + p->subchannels = + gpr_malloc(sizeof(grpc_subchannel *) * args->num_subchannels); + p->num_subchannels = args->num_subchannels; + grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, + "round_robin"); + memcpy(p->subchannels, args->subchannels, + sizeof(grpc_subchannel *) * args->num_subchannels); + + gpr_mu_init(&p->mu); + p->connectivity_changed_cbs = + gpr_malloc(sizeof(grpc_closure) * args->num_subchannels); + p->subchannel_connectivity = + gpr_malloc(sizeof(grpc_connectivity_state) * args->num_subchannels); + + p->cb_args = + gpr_malloc(sizeof(connectivity_changed_cb_arg) * args->num_subchannels); + for (i = 0; i < args->num_subchannels; i++) { + p->cb_args[i].subchannel_idx = i; + p->cb_args[i].p = p; + grpc_closure_init(&p->connectivity_changed_cbs[i], rr_connectivity_changed, + &p->cb_args[i]); + } + + /* The (dummy node) root of the ready list */ + p->ready_list.subchannel = NULL; + p->ready_list.prev = NULL; + p->ready_list.next = NULL; + p->ready_list_last_pick = &p->ready_list; + + p->subchannel_index_to_readylist_node = + gpr_malloc(sizeof(grpc_subchannel *) * args->num_subchannels); + memset(p->subchannel_index_to_readylist_node, 0, + sizeof(grpc_subchannel *) * args->num_subchannels); + return &p->base; +} + +static const grpc_lb_policy_factory_vtable round_robin_factory_vtable = { + round_robin_factory_ref, round_robin_factory_unref, create_round_robin, + "round_robin"}; + +static grpc_lb_policy_factory round_robin_lb_policy_factory = { + &round_robin_factory_vtable}; + +grpc_lb_policy_factory *grpc_round_robin_lb_factory_create() { + return &round_robin_lb_policy_factory; +} diff --git a/src/node/examples/stock_client.js b/src/core/client_config/lb_policies/round_robin.h index ab9b050e9b..cf1f69c85f 100644 --- a/src/node/examples/stock_client.js +++ b/src/core/client_config/lb_policies/round_robin.h @@ -31,17 +31,16 @@ * */ -var grpc = require('..'); -var examples = grpc.load(__dirname + '/stock.proto').examples; +#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_ROUND_ROBIN_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_ROUND_ROBIN_H -/** - * This exports a client constructor for the Stock service. The usage looks like - * - * var StockClient = require('stock_client.js'); - * var stockClient = new StockClient(server_address, - * grpc.Credentials.createInsecure()); - * stockClient.getLastTradePrice({symbol: 'GOOG'}, function(error, response) { - * console.log(error || response); - * }); - */ -module.exports = examples.Stock; +#include "src/core/client_config/lb_policy.h" + +extern int grpc_lb_round_robin_trace; + +#include "src/core/client_config/lb_policy_factory.h" + +/** Returns a load balancing factory for the round robin policy */ +grpc_lb_policy_factory *grpc_round_robin_lb_factory_create(); + +#endif diff --git a/src/core/client_config/lb_policy.c b/src/core/client_config/lb_policy.c index 90ec44432f..c955186f7f 100644 --- a/src/core/client_config/lb_policy.c +++ b/src/core/client_config/lb_policy.c @@ -51,44 +51,48 @@ void grpc_lb_policy_ref(grpc_lb_policy *policy) { } #ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG -void grpc_lb_policy_unref(grpc_lb_policy *policy, const char *file, int line, - const char *reason) { +void grpc_lb_policy_unref(grpc_lb_policy *policy, + grpc_closure_list *closure_list, const char *file, + int line, const char *reason) { gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "LB_POLICY:%p unref %d -> %d %s", policy, (int)policy->refs.count, (int)policy->refs.count - 1, reason); #else -void grpc_lb_policy_unref(grpc_lb_policy *policy) { +void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { #endif if (gpr_unref(&policy->refs)) { - policy->vtable->destroy(policy); + policy->vtable->destroy(exec_ctx, policy); } } -void grpc_lb_policy_shutdown(grpc_lb_policy *policy) { - policy->vtable->shutdown(policy); +void grpc_lb_policy_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { + policy->vtable->shutdown(exec_ctx, policy); } -void grpc_lb_policy_pick(grpc_lb_policy *policy, grpc_pollset *pollset, +void grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, - grpc_subchannel **target, - grpc_iomgr_closure *on_complete) { - policy->vtable->pick(policy, pollset, initial_metadata, target, on_complete); + grpc_subchannel **target, grpc_closure *on_complete) { + policy->vtable->pick(exec_ctx, policy, pollset, initial_metadata, target, + on_complete); } -void grpc_lb_policy_broadcast(grpc_lb_policy *policy, grpc_transport_op *op) { - policy->vtable->broadcast(policy, op); +void grpc_lb_policy_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_transport_op *op) { + policy->vtable->broadcast(exec_ctx, policy, op); } -void grpc_lb_policy_exit_idle(grpc_lb_policy *policy) { - policy->vtable->exit_idle(policy); +void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { + policy->vtable->exit_idle(exec_ctx, policy); } -void grpc_lb_policy_notify_on_state_change(grpc_lb_policy *policy, +void grpc_lb_policy_notify_on_state_change(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy, grpc_connectivity_state *state, - grpc_iomgr_closure *closure) { - policy->vtable->notify_on_state_change(policy, state, closure); + grpc_closure *closure) { + policy->vtable->notify_on_state_change(exec_ctx, policy, state, closure); } grpc_connectivity_state grpc_lb_policy_check_connectivity( - grpc_lb_policy *policy) { - return policy->vtable->check_connectivity(policy); + grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { + return policy->vtable->check_connectivity(exec_ctx, policy); } diff --git a/src/core/client_config/lb_policy.h b/src/core/client_config/lb_policy.h index 3f7ca8f28d..0eefe64991 100644 --- a/src/core/client_config/lb_policy.h +++ b/src/core/client_config/lb_policy.h @@ -35,6 +35,7 @@ #define GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_H #include "src/core/client_config/subchannel.h" +#include "src/core/transport/connectivity_state.h" /** A load balancing policy: specified by a vtable and a struct (which is expected to be extended to contain some parameters) */ @@ -50,45 +51,48 @@ struct grpc_lb_policy { }; struct grpc_lb_policy_vtable { - void (*destroy)(grpc_lb_policy *policy); + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); - void (*shutdown)(grpc_lb_policy *policy); + void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); /** implement grpc_lb_policy_pick */ - void (*pick)(grpc_lb_policy *policy, grpc_pollset *pollset, - grpc_metadata_batch *initial_metadata, grpc_subchannel **target, - grpc_iomgr_closure *on_complete); + void (*pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, + grpc_subchannel **target, grpc_closure *on_complete); /** try to enter a READY connectivity state */ - void (*exit_idle)(grpc_lb_policy *policy); + void (*exit_idle)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); /** broadcast a transport op to all subchannels */ - void (*broadcast)(grpc_lb_policy *policy, grpc_transport_op *op); + void (*broadcast)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_transport_op *op); /** check the current connectivity of the lb_policy */ - grpc_connectivity_state (*check_connectivity)(grpc_lb_policy *policy); + grpc_connectivity_state (*check_connectivity)(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy); /** call notify when the connectivity state of a channel changes from *state. Updates *state with the new state of the policy */ - void (*notify_on_state_change)(grpc_lb_policy *policy, + void (*notify_on_state_change)(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy, grpc_connectivity_state *state, - grpc_iomgr_closure *closure); + grpc_closure *closure); }; #ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG #define GRPC_LB_POLICY_REF(p, r) \ grpc_lb_policy_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_LB_POLICY_UNREF(p, r) \ - grpc_lb_policy_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_LB_POLICY_UNREF(exec_ctx, p, r) \ + grpc_lb_policy_unref((exec_ctx), (p), __FILE__, __LINE__, (r)) void grpc_lb_policy_ref(grpc_lb_policy *policy, const char *file, int line, const char *reason); -void grpc_lb_policy_unref(grpc_lb_policy *policy, const char *file, int line, - const char *reason); +void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + const char *file, int line, const char *reason); #else #define GRPC_LB_POLICY_REF(p, r) grpc_lb_policy_ref((p)) -#define GRPC_LB_POLICY_UNREF(p, r) grpc_lb_policy_unref((p)) +#define GRPC_LB_POLICY_UNREF(cl, p, r) grpc_lb_policy_unref((cl), (p)) void grpc_lb_policy_ref(grpc_lb_policy *policy); -void grpc_lb_policy_unref(grpc_lb_policy *policy); +void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); #endif /** called by concrete implementations to initialize the base struct */ @@ -96,26 +100,28 @@ void grpc_lb_policy_init(grpc_lb_policy *policy, const grpc_lb_policy_vtable *vtable); /** Start shutting down (fail any pending picks) */ -void grpc_lb_policy_shutdown(grpc_lb_policy *policy); +void grpc_lb_policy_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); /** Given initial metadata in \a initial_metadata, find an appropriate target for this rpc, and 'return' it by calling \a on_complete after setting \a target. Picking can be asynchronous. Any IO should be done under \a pollset. */ -void grpc_lb_policy_pick(grpc_lb_policy *policy, grpc_pollset *pollset, +void grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, - grpc_subchannel **target, - grpc_iomgr_closure *on_complete); + grpc_subchannel **target, grpc_closure *on_complete); -void grpc_lb_policy_broadcast(grpc_lb_policy *policy, grpc_transport_op *op); +void grpc_lb_policy_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_transport_op *op); -void grpc_lb_policy_exit_idle(grpc_lb_policy *policy); +void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); -void grpc_lb_policy_notify_on_state_change(grpc_lb_policy *policy, +void grpc_lb_policy_notify_on_state_change(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy, grpc_connectivity_state *state, - grpc_iomgr_closure *closure); + grpc_closure *closure); grpc_connectivity_state grpc_lb_policy_check_connectivity( - grpc_lb_policy *policy); + grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); #endif /* GRPC_INTERNAL_CORE_CONFIG_LB_POLICY_H */ diff --git a/src/core/client_config/lb_policy_factory.c b/src/core/client_config/lb_policy_factory.c new file mode 100644 index 0000000000..e49de544e3 --- /dev/null +++ b/src/core/client_config/lb_policy_factory.c @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/client_config/lb_policy_factory.h" + +void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory) { + factory->vtable->ref(factory); +} + +void grpc_lb_policy_factory_unref(grpc_lb_policy_factory* factory) { + factory->vtable->unref(factory); +} + +grpc_lb_policy* grpc_lb_policy_factory_create_lb_policy( + grpc_lb_policy_factory* factory, grpc_lb_policy_args* args) { + if (factory == NULL) return NULL; + return factory->vtable->create_lb_policy(factory, args); +} diff --git a/src/node/examples/stock_server.js b/src/core/client_config/lb_policy_factory.h index 12e5479584..04610316ee 100644 --- a/src/node/examples/stock_server.js +++ b/src/core/client_config/lb_policy_factory.h @@ -31,57 +31,43 @@ * */ -'use strict'; +#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_FACTORY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_FACTORY_H -var _ = require('lodash'); -var grpc = require('..'); -var examples = grpc.load(__dirname + '/stock.proto').examples; +#include "src/core/client_config/lb_policy.h" +#include "src/core/client_config/subchannel.h" -function getLastTradePrice(call, callback) { - callback(null, {symbol: call.request.symbol, price: 88}); -} +typedef struct grpc_lb_policy_factory grpc_lb_policy_factory; +typedef struct grpc_lb_policy_factory_vtable grpc_lb_policy_factory_vtable; -function watchFutureTrades(call) { - for (var i = 0; i < call.request.num_trades_to_watch; i++) { - call.write({price: 88.00 + i * 10.00}); - } - call.end(); -} +/** grpc_lb_policy provides grpc_client_config objects to grpc_channel + objects */ +struct grpc_lb_policy_factory { + const grpc_lb_policy_factory_vtable *vtable; +}; -function getHighestTradePrice(call, callback) { - var trades = []; - call.on('data', function(data) { - trades.push({symbol: data.symbol, price: _.random(0, 100)}); - }); - call.on('end', function() { - if(_.isEmpty(trades)) { - callback(null, {}); - } else { - callback(null, _.max(trades, function(trade){return trade.price;})); - } - }); -} +typedef struct grpc_lb_policy_args { + grpc_subchannel **subchannels; + size_t num_subchannels; +} grpc_lb_policy_args; -function getLastTradePriceMultiple(call) { - call.on('data', function(data) { - call.write({price: 88}); - }); - call.on('end', function() { - call.end(); - }); -} +struct grpc_lb_policy_factory_vtable { + void (*ref)(grpc_lb_policy_factory *factory); + void (*unref)(grpc_lb_policy_factory *factory); -var stockServer = new grpc.Server(); -stockServer.addProtoService(examples.Stock.service, { - getLastTradePrice: getLastTradePrice, - getLastTradePriceMultiple: getLastTradePriceMultiple, - watchFutureTrades: watchFutureTrades, - getHighestTradePrice: getHighestTradePrice -}); + /** Implementation of grpc_lb_policy_factory_create_lb_policy */ + grpc_lb_policy *(*create_lb_policy)(grpc_lb_policy_factory *factory, + grpc_lb_policy_args *args); -if (require.main === module) { - stockServer.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); - stockServer.start(); -} + /** Name for the LB policy this factory implements */ + const char *name; +}; -module.exports = stockServer; +void grpc_lb_policy_factory_ref(grpc_lb_policy_factory *factory); +void grpc_lb_policy_factory_unref(grpc_lb_policy_factory *factory); + +/** Create a lb_policy instance. */ +grpc_lb_policy *grpc_lb_policy_factory_create_lb_policy( + grpc_lb_policy_factory *factory, grpc_lb_policy_args *args); + +#endif /* GRPC_INTERNAL_CORE_CONFIG_LB_POLICY_FACTORY_H */ diff --git a/src/core/client_config/lb_policy_registry.c b/src/core/client_config/lb_policy_registry.c new file mode 100644 index 0000000000..fc302e82d7 --- /dev/null +++ b/src/core/client_config/lb_policy_registry.c @@ -0,0 +1,88 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/client_config/lb_policy_registry.h" + +#include <string.h> + +#define MAX_POLICIES 10 + +static grpc_lb_policy_factory *g_all_of_the_lb_policies[MAX_POLICIES]; +static int g_number_of_lb_policies = 0; + +static grpc_lb_policy_factory *g_default_lb_policy_factory; + +void grpc_lb_policy_registry_init(grpc_lb_policy_factory *default_factory) { + g_number_of_lb_policies = 0; + g_default_lb_policy_factory = default_factory; +} + +void grpc_lb_policy_registry_shutdown(void) { + int i; + for (i = 0; i < g_number_of_lb_policies; i++) { + grpc_lb_policy_factory_unref(g_all_of_the_lb_policies[i]); + } +} + +void grpc_register_lb_policy(grpc_lb_policy_factory *factory) { + int i; + for (i = 0; i < g_number_of_lb_policies; i++) { + GPR_ASSERT(0 != strcmp(factory->vtable->name, + g_all_of_the_lb_policies[i]->vtable->name)); + } + GPR_ASSERT(g_number_of_lb_policies != MAX_POLICIES); + grpc_lb_policy_factory_ref(factory); + g_all_of_the_lb_policies[g_number_of_lb_policies++] = factory; +} + +static grpc_lb_policy_factory *lookup_factory(const char *name) { + int i; + + if (name == NULL) return NULL; + + for (i = 0; i < g_number_of_lb_policies; i++) { + if (0 == strcmp(name, g_all_of_the_lb_policies[i]->vtable->name)) { + return g_all_of_the_lb_policies[i]; + } + } + + return NULL; +} + +grpc_lb_policy *grpc_lb_policy_create(const char *name, + grpc_lb_policy_args *args) { + grpc_lb_policy_factory *factory = lookup_factory(name); + grpc_lb_policy *lb_policy = + grpc_lb_policy_factory_create_lb_policy(factory, args); + return lb_policy; +} diff --git a/src/core/client_config/lb_policy_registry.h b/src/core/client_config/lb_policy_registry.h new file mode 100644 index 0000000000..96fc2a1628 --- /dev/null +++ b/src/core/client_config/lb_policy_registry.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_REGISTRY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_REGISTRY_H + +#include "src/core/client_config/lb_policy_factory.h" + +/** Initialize the registry and set \a default_factory as the factory to be + * returned when no name is provided in a lookup */ +void grpc_lb_policy_registry_init(grpc_lb_policy_factory *default_factory); +void grpc_lb_policy_registry_shutdown(void); + +/** Register a LB policy factory. */ +void grpc_register_lb_policy(grpc_lb_policy_factory *factory); + +/** Create a \a grpc_lb_policy instance. + * + * If \a name is NULL, the default factory from \a grpc_lb_policy_registry_init + * will be returned. */ +grpc_lb_policy *grpc_lb_policy_create(const char *name, + grpc_lb_policy_args *args); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_REGISTRY_H */ diff --git a/src/core/client_config/resolver.c b/src/core/client_config/resolver.c index 91e42bb684..081097eb19 100644 --- a/src/core/client_config/resolver.c +++ b/src/core/client_config/resolver.c @@ -40,8 +40,8 @@ void grpc_resolver_init(grpc_resolver *resolver, } #ifdef GRPC_RESOLVER_REFCOUNT_DEBUG -void grpc_resolver_ref(grpc_resolver *resolver, const char *file, int line, - const char *reason) { +void grpc_resolver_ref(grpc_resolver *resolver, grpc_closure_list *closure_list, + const char *file, int line, const char *reason) { gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "RESOLVER:%p ref %d -> %d %s", resolver, (int)resolver->refs.count, (int)resolver->refs.count + 1, reason); @@ -52,32 +52,34 @@ void grpc_resolver_ref(grpc_resolver *resolver) { } #ifdef GRPC_RESOLVER_REFCOUNT_DEBUG -void grpc_resolver_unref(grpc_resolver *resolver, const char *file, int line, - const char *reason) { +void grpc_resolver_unref(grpc_resolver *resolver, + grpc_closure_list *closure_list, const char *file, + int line, const char *reason) { gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "RESOLVER:%p unref %d -> %d %s", resolver, (int)resolver->refs.count, (int)resolver->refs.count - 1, reason); #else -void grpc_resolver_unref(grpc_resolver *resolver) { +void grpc_resolver_unref(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver) { #endif if (gpr_unref(&resolver->refs)) { - resolver->vtable->destroy(resolver); + resolver->vtable->destroy(exec_ctx, resolver); } } -void grpc_resolver_shutdown(grpc_resolver *resolver) { - resolver->vtable->shutdown(resolver); +void grpc_resolver_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver) { + resolver->vtable->shutdown(exec_ctx, resolver); } -void grpc_resolver_channel_saw_error(grpc_resolver *resolver, +void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver, struct sockaddr *failing_address, int failing_address_len) { - resolver->vtable->channel_saw_error(resolver, failing_address, + resolver->vtable->channel_saw_error(exec_ctx, resolver, failing_address, failing_address_len); } -void grpc_resolver_next(grpc_resolver *resolver, +void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete) { - resolver->vtable->next(resolver, target_config, on_complete); + grpc_closure *on_complete) { + resolver->vtable->next(exec_ctx, resolver, target_config, on_complete); } diff --git a/src/core/client_config/resolver.h b/src/core/client_config/resolver.h index 8ad87d789b..7ba0cd5bd4 100644 --- a/src/core/client_config/resolver.h +++ b/src/core/client_config/resolver.h @@ -49,38 +49,39 @@ struct grpc_resolver { }; struct grpc_resolver_vtable { - void (*destroy)(grpc_resolver *resolver); - void (*shutdown)(grpc_resolver *resolver); - void (*channel_saw_error)(grpc_resolver *resolver, + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); + void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); + void (*channel_saw_error)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, struct sockaddr *failing_address, int failing_address_len); - void (*next)(grpc_resolver *resolver, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete); + void (*next)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, + grpc_client_config **target_config, grpc_closure *on_complete); }; #ifdef GRPC_RESOLVER_REFCOUNT_DEBUG #define GRPC_RESOLVER_REF(p, r) grpc_resolver_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_RESOLVER_UNREF(p, r) \ - grpc_resolver_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_RESOLVER_UNREF(cl, p, r) \ + grpc_resolver_unref((cl), (p), __FILE__, __LINE__, (r)) void grpc_resolver_ref(grpc_resolver *policy, const char *file, int line, const char *reason); -void grpc_resolver_unref(grpc_resolver *policy, const char *file, int line, - const char *reason); +void grpc_resolver_unref(grpc_resolver *policy, grpc_closure_list *closure_list, + const char *file, int line, const char *reason); #else #define GRPC_RESOLVER_REF(p, r) grpc_resolver_ref((p)) -#define GRPC_RESOLVER_UNREF(p, r) grpc_resolver_unref((p)) +#define GRPC_RESOLVER_UNREF(cl, p, r) grpc_resolver_unref((cl), (p)) void grpc_resolver_ref(grpc_resolver *policy); -void grpc_resolver_unref(grpc_resolver *policy); +void grpc_resolver_unref(grpc_exec_ctx *exec_ctx, grpc_resolver *policy); #endif void grpc_resolver_init(grpc_resolver *resolver, const grpc_resolver_vtable *vtable); -void grpc_resolver_shutdown(grpc_resolver *resolver); +void grpc_resolver_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); /** Notification that the channel has seen an error on some address. Can be used as a hint that re-resolution is desirable soon. */ -void grpc_resolver_channel_saw_error(grpc_resolver *resolver, +void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver, struct sockaddr *failing_address, int failing_address_len); @@ -90,8 +91,8 @@ void grpc_resolver_channel_saw_error(grpc_resolver *resolver, If resolution is fatally broken, set *target_config to NULL and schedule on_complete. */ -void grpc_resolver_next(grpc_resolver *resolver, +void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete); + grpc_closure *on_complete); #endif /* GRPC_INTERNAL_CORE_CONFIG_RESOLVER_H */ diff --git a/src/core/client_config/resolver_factory.c b/src/core/client_config/resolver_factory.c index 5b859a8d10..e7e9196ac4 100644 --- a/src/core/client_config/resolver_factory.c +++ b/src/core/client_config/resolver_factory.c @@ -33,24 +33,23 @@ #include "src/core/client_config/resolver_factory.h" -void grpc_resolver_factory_ref(grpc_resolver_factory *factory) { +void grpc_resolver_factory_ref(grpc_resolver_factory* factory) { factory->vtable->ref(factory); } -void grpc_resolver_factory_unref(grpc_resolver_factory *factory) { +void grpc_resolver_factory_unref(grpc_resolver_factory* factory) { factory->vtable->unref(factory); } /** Create a resolver instance for a name */ -grpc_resolver *grpc_resolver_factory_create_resolver( - grpc_resolver_factory *factory, grpc_uri *uri, - grpc_subchannel_factory *subchannel_factory) { +grpc_resolver* grpc_resolver_factory_create_resolver( + grpc_resolver_factory* factory, grpc_resolver_args* args) { if (factory == NULL) return NULL; - return factory->vtable->create_resolver(factory, uri, subchannel_factory); + return factory->vtable->create_resolver(factory, args); } -char *grpc_resolver_factory_get_default_authority( - grpc_resolver_factory *factory, grpc_uri *uri) { +char* grpc_resolver_factory_get_default_authority( + grpc_resolver_factory* factory, grpc_uri* uri) { if (factory == NULL) return NULL; return factory->vtable->get_default_authority(factory, uri); } diff --git a/src/core/client_config/resolver_factory.h b/src/core/client_config/resolver_factory.h index e243b23988..4c4df353f7 100644 --- a/src/core/client_config/resolver_factory.h +++ b/src/core/client_config/resolver_factory.h @@ -47,14 +47,18 @@ struct grpc_resolver_factory { const grpc_resolver_factory_vtable *vtable; }; +typedef struct grpc_resolver_args { + grpc_uri *uri; + grpc_subchannel_factory *subchannel_factory; +} grpc_resolver_args; + struct grpc_resolver_factory_vtable { void (*ref)(grpc_resolver_factory *factory); void (*unref)(grpc_resolver_factory *factory); /** Implementation of grpc_resolver_factory_create_resolver */ - grpc_resolver *(*create_resolver)( - grpc_resolver_factory *factory, grpc_uri *uri, - grpc_subchannel_factory *subchannel_factory); + grpc_resolver *(*create_resolver)(grpc_resolver_factory *factory, + grpc_resolver_args *args); /** Implementation of grpc_resolver_factory_get_default_authority */ char *(*get_default_authority)(grpc_resolver_factory *factory, grpc_uri *uri); @@ -68,8 +72,7 @@ void grpc_resolver_factory_unref(grpc_resolver_factory *resolver); /** Create a resolver instance for a name */ grpc_resolver *grpc_resolver_factory_create_resolver( - grpc_resolver_factory *factory, grpc_uri *uri, - grpc_subchannel_factory *subchannel_factory); + grpc_resolver_factory *factory, grpc_resolver_args *args); /** Return a (freshly allocated with gpr_malloc) string representing the default authority to use for this scheme. */ diff --git a/src/core/client_config/resolver_registry.c b/src/core/client_config/resolver_registry.c index 37979b3b86..89a945c2d3 100644 --- a/src/core/client_config/resolver_registry.c +++ b/src/core/client_config/resolver_registry.c @@ -118,8 +118,12 @@ grpc_resolver *grpc_resolver_create( const char *target, grpc_subchannel_factory *subchannel_factory) { grpc_uri *uri = NULL; grpc_resolver_factory *factory = resolve_factory(target, &uri); - grpc_resolver *resolver = - grpc_resolver_factory_create_resolver(factory, uri, subchannel_factory); + grpc_resolver *resolver; + grpc_resolver_args args; + memset(&args, 0, sizeof(args)); + args.uri = uri; + args.subchannel_factory = subchannel_factory; + resolver = grpc_resolver_factory_create_resolver(factory, &args); grpc_uri_destroy(uri); return resolver; } diff --git a/src/core/client_config/resolvers/dns_resolver.c b/src/core/client_config/resolvers/dns_resolver.c index 84643c464a..7f9dd2543f 100644 --- a/src/core/client_config/resolvers/dns_resolver.c +++ b/src/core/client_config/resolvers/dns_resolver.c @@ -39,7 +39,7 @@ #include <grpc/support/host_port.h> #include <grpc/support/string_util.h> -#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/client_config/lb_policy_registry.h" #include "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h" #include "src/core/iomgr/resolve_address.h" #include "src/core/support/string.h" @@ -55,9 +55,8 @@ typedef struct { char *default_port; /** subchannel factory */ grpc_subchannel_factory *subchannel_factory; - /** load balancing policy factory */ - grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, - size_t num_subchannels); + /** load balancing policy name */ + char *lb_policy_name; /** mutex guarding the rest of the state */ gpr_mu mu; @@ -68,40 +67,43 @@ typedef struct { /** which version of resolved_config is current? */ int resolved_version; /** pending next completion, or NULL */ - grpc_iomgr_closure *next_completion; + grpc_closure *next_completion; /** target config address for next completion */ grpc_client_config **target_config; /** current (fully resolved) config */ grpc_client_config *resolved_config; } dns_resolver; -static void dns_destroy(grpc_resolver *r); +static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void dns_start_resolving_locked(dns_resolver *r); -static void dns_maybe_finish_next_locked(dns_resolver *r); +static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + dns_resolver *r); -static void dns_shutdown(grpc_resolver *r); -static void dns_channel_saw_error(grpc_resolver *r, +static void dns_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); +static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *r, struct sockaddr *failing_address, int failing_address_len); -static void dns_next(grpc_resolver *r, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete); +static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, + grpc_client_config **target_config, + grpc_closure *on_complete); static const grpc_resolver_vtable dns_resolver_vtable = { dns_destroy, dns_shutdown, dns_channel_saw_error, dns_next}; -static void dns_shutdown(grpc_resolver *resolver) { +static void dns_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver) { dns_resolver *r = (dns_resolver *)resolver; gpr_mu_lock(&r->mu); if (r->next_completion != NULL) { *r->target_config = NULL; - grpc_iomgr_add_callback(r->next_completion); + grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, 1); r->next_completion = NULL; } gpr_mu_unlock(&r->mu); } -static void dns_channel_saw_error(grpc_resolver *resolver, struct sockaddr *sa, +static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver, struct sockaddr *sa, int len) { dns_resolver *r = (dns_resolver *)resolver; gpr_mu_lock(&r->mu); @@ -111,9 +113,9 @@ static void dns_channel_saw_error(grpc_resolver *resolver, struct sockaddr *sa, gpr_mu_unlock(&r->mu); } -static void dns_next(grpc_resolver *resolver, +static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete) { + grpc_closure *on_complete) { dns_resolver *r = (dns_resolver *)resolver; gpr_mu_lock(&r->mu); GPR_ASSERT(!r->next_completion); @@ -122,12 +124,13 @@ static void dns_next(grpc_resolver *resolver, if (r->resolved_version == 0 && !r->resolving) { dns_start_resolving_locked(r); } else { - dns_maybe_finish_next_locked(r); + dns_maybe_finish_next_locked(exec_ctx, r); } gpr_mu_unlock(&r->mu); } -static void dns_on_resolved(void *arg, grpc_resolved_addresses *addresses) { +static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, + grpc_resolved_addresses *addresses) { dns_resolver *r = arg; grpc_client_config *config = NULL; grpc_subchannel **subchannels; @@ -135,18 +138,22 @@ static void dns_on_resolved(void *arg, grpc_resolved_addresses *addresses) { grpc_lb_policy *lb_policy; size_t i; if (addresses) { + grpc_lb_policy_args lb_policy_args; config = grpc_client_config_create(); subchannels = gpr_malloc(sizeof(grpc_subchannel *) * addresses->naddrs); for (i = 0; i < addresses->naddrs; i++) { memset(&args, 0, sizeof(args)); args.addr = (struct sockaddr *)(addresses->addrs[i].addr); - args.addr_len = addresses->addrs[i].len; + args.addr_len = (size_t)addresses->addrs[i].len; subchannels[i] = grpc_subchannel_factory_create_subchannel( - r->subchannel_factory, &args); + exec_ctx, r->subchannel_factory, &args); } - lb_policy = r->lb_policy_factory(subchannels, addresses->naddrs); + memset(&lb_policy_args, 0, sizeof(lb_policy_args)); + lb_policy_args.subchannels = subchannels; + lb_policy_args.num_subchannels = addresses->naddrs; + lb_policy = grpc_lb_policy_create(r->lb_policy_name, &lb_policy_args); grpc_client_config_set_lb_policy(config, lb_policy); - GRPC_LB_POLICY_UNREF(lb_policy, "construction"); + GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "construction"); grpc_resolved_addresses_destroy(addresses); gpr_free(subchannels); } @@ -154,14 +161,14 @@ static void dns_on_resolved(void *arg, grpc_resolved_addresses *addresses) { GPR_ASSERT(r->resolving); r->resolving = 0; if (r->resolved_config) { - grpc_client_config_unref(r->resolved_config); + grpc_client_config_unref(exec_ctx, r->resolved_config); } r->resolved_config = config; r->resolved_version++; - dns_maybe_finish_next_locked(r); + dns_maybe_finish_next_locked(exec_ctx, r); gpr_mu_unlock(&r->mu); - GRPC_RESOLVER_UNREF(&r->base, "dns-resolving"); + GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "dns-resolving"); } static void dns_start_resolving_locked(dns_resolver *r) { @@ -171,41 +178,41 @@ static void dns_start_resolving_locked(dns_resolver *r) { grpc_resolve_address(r->name, r->default_port, dns_on_resolved, r); } -static void dns_maybe_finish_next_locked(dns_resolver *r) { +static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + dns_resolver *r) { if (r->next_completion != NULL && r->resolved_version != r->published_version) { *r->target_config = r->resolved_config; if (r->resolved_config) { grpc_client_config_ref(r->resolved_config); } - grpc_iomgr_add_callback(r->next_completion); + grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, 1); r->next_completion = NULL; r->published_version = r->resolved_version; } } -static void dns_destroy(grpc_resolver *gr) { +static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { dns_resolver *r = (dns_resolver *)gr; gpr_mu_destroy(&r->mu); if (r->resolved_config) { - grpc_client_config_unref(r->resolved_config); + grpc_client_config_unref(exec_ctx, r->resolved_config); } - grpc_subchannel_factory_unref(r->subchannel_factory); + grpc_subchannel_factory_unref(exec_ctx, r->subchannel_factory); gpr_free(r->name); gpr_free(r->default_port); + gpr_free(r->lb_policy_name); gpr_free(r); } -static grpc_resolver *dns_create( - grpc_uri *uri, const char *default_port, - grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, - size_t num_subchannels), - grpc_subchannel_factory *subchannel_factory) { +static grpc_resolver *dns_create(grpc_resolver_args *args, + const char *default_port, + const char *lb_policy_name) { dns_resolver *r; - const char *path = uri->path; + const char *path = args->uri->path; - if (0 != strcmp(uri->authority, "")) { - gpr_log(GPR_ERROR, "authority based uri's not supported"); + if (0 != strcmp(args->uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based dns uri's not supported"); return NULL; } @@ -218,9 +225,9 @@ static grpc_resolver *dns_create( grpc_resolver_init(&r->base, &dns_resolver_vtable); r->name = gpr_strdup(path); r->default_port = gpr_strdup(default_port); - r->subchannel_factory = subchannel_factory; - grpc_subchannel_factory_ref(subchannel_factory); - r->lb_policy_factory = lb_policy_factory; + r->subchannel_factory = args->subchannel_factory; + grpc_subchannel_factory_ref(r->subchannel_factory); + r->lb_policy_name = gpr_strdup(lb_policy_name); return &r->base; } @@ -233,10 +240,8 @@ static void dns_factory_ref(grpc_resolver_factory *factory) {} static void dns_factory_unref(grpc_resolver_factory *factory) {} static grpc_resolver *dns_factory_create_resolver( - grpc_resolver_factory *factory, grpc_uri *uri, - grpc_subchannel_factory *subchannel_factory) { - return dns_create(uri, "https", grpc_create_pick_first_lb_policy, - subchannel_factory); + grpc_resolver_factory *factory, grpc_resolver_args *args) { + return dns_create(args, "https", "pick_first"); } char *dns_factory_get_default_host_name(grpc_resolver_factory *factory, diff --git a/src/core/client_config/resolvers/sockaddr_resolver.c b/src/core/client_config/resolvers/sockaddr_resolver.c index 0d8540a566..0b017f06c7 100644 --- a/src/core/client_config/resolvers/sockaddr_resolver.c +++ b/src/core/client_config/resolvers/sockaddr_resolver.c @@ -45,7 +45,7 @@ #include <grpc/support/host_port.h> #include <grpc/support/string_util.h> -#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/client_config/lb_policy_registry.h" #include "src/core/iomgr/resolve_address.h" #include "src/core/support/string.h" @@ -56,14 +56,13 @@ typedef struct { gpr_refcount refs; /** subchannel factory */ grpc_subchannel_factory *subchannel_factory; - /** load balancing policy factory */ - grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, - size_t num_subchannels); + /** load balancing policy name */ + char *lb_policy_name; /** the addresses that we've 'resolved' */ struct sockaddr_storage *addrs; /** the corresponding length of the addresses */ - int *addrs_len; + size_t *addrs_len; /** how many elements in \a addrs */ size_t num_addrs; @@ -72,56 +71,62 @@ typedef struct { /** have we published? */ int published; /** pending next completion, or NULL */ - grpc_iomgr_closure *next_completion; + grpc_closure *next_completion; /** target config address for next completion */ grpc_client_config **target_config; } sockaddr_resolver; -static void sockaddr_destroy(grpc_resolver *r); +static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); -static void sockaddr_maybe_finish_next_locked(sockaddr_resolver *r); +static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + sockaddr_resolver *r); -static void sockaddr_shutdown(grpc_resolver *r); -static void sockaddr_channel_saw_error(grpc_resolver *r, +static void sockaddr_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); +static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, + grpc_resolver *r, struct sockaddr *failing_address, int failing_address_len); -static void sockaddr_next(grpc_resolver *r, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete); +static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, + grpc_client_config **target_config, + grpc_closure *on_complete); static const grpc_resolver_vtable sockaddr_resolver_vtable = { sockaddr_destroy, sockaddr_shutdown, sockaddr_channel_saw_error, sockaddr_next}; -static void sockaddr_shutdown(grpc_resolver *resolver) { +static void sockaddr_shutdown(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { sockaddr_resolver *r = (sockaddr_resolver *)resolver; gpr_mu_lock(&r->mu); if (r->next_completion != NULL) { *r->target_config = NULL; - /* TODO(ctiller): add delayed callback */ - grpc_iomgr_add_callback(r->next_completion); + grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, 1); r->next_completion = NULL; } gpr_mu_unlock(&r->mu); } -static void sockaddr_channel_saw_error(grpc_resolver *resolver, +static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver, struct sockaddr *sa, int len) {} -static void sockaddr_next(grpc_resolver *resolver, +static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete) { + grpc_closure *on_complete) { sockaddr_resolver *r = (sockaddr_resolver *)resolver; gpr_mu_lock(&r->mu); GPR_ASSERT(!r->next_completion); r->next_completion = on_complete; r->target_config = target_config; - sockaddr_maybe_finish_next_locked(r); + sockaddr_maybe_finish_next_locked(exec_ctx, r); gpr_mu_unlock(&r->mu); } -static void sockaddr_maybe_finish_next_locked(sockaddr_resolver *r) { +static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + sockaddr_resolver *r) { grpc_client_config *cfg; grpc_lb_policy *lb_policy; + grpc_lb_policy_args lb_policy_args; grpc_subchannel **subchannels; grpc_subchannel_args args; @@ -134,30 +139,35 @@ static void sockaddr_maybe_finish_next_locked(sockaddr_resolver *r) { args.addr = (struct sockaddr *)&r->addrs[i]; args.addr_len = r->addrs_len[i]; subchannels[i] = grpc_subchannel_factory_create_subchannel( - r->subchannel_factory, &args); + exec_ctx, r->subchannel_factory, &args); } - lb_policy = r->lb_policy_factory(subchannels, r->num_addrs); + memset(&lb_policy_args, 0, sizeof(lb_policy_args)); + lb_policy_args.subchannels = subchannels; + lb_policy_args.num_subchannels = r->num_addrs; + lb_policy = grpc_lb_policy_create(r->lb_policy_name, &lb_policy_args); gpr_free(subchannels); grpc_client_config_set_lb_policy(cfg, lb_policy); - GRPC_LB_POLICY_UNREF(lb_policy, "unix"); + GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "sockaddr"); r->published = 1; *r->target_config = cfg; - grpc_iomgr_add_callback(r->next_completion); + grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, 1); r->next_completion = NULL; } } -static void sockaddr_destroy(grpc_resolver *gr) { +static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { sockaddr_resolver *r = (sockaddr_resolver *)gr; gpr_mu_destroy(&r->mu); - grpc_subchannel_factory_unref(r->subchannel_factory); + grpc_subchannel_factory_unref(exec_ctx, r->subchannel_factory); gpr_free(r->addrs); gpr_free(r->addrs_len); + gpr_free(r->lb_policy_name); gpr_free(r); } #ifdef GPR_POSIX_SOCKET -static int parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, int *len) { +static int parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, + size_t *len) { struct sockaddr_un *un = (struct sockaddr_un *)addr; un->sun_family = AF_UNIX; @@ -189,7 +199,8 @@ static char *ipv6_get_default_authority(grpc_resolver_factory *factory, return ip_get_default_authority(uri); } -static int parse_ipv4(grpc_uri *uri, struct sockaddr_storage *addr, int *len) { +static int parse_ipv4(grpc_uri *uri, struct sockaddr_storage *addr, + size_t *len) { const char *host_port = uri->path; char *host; char *port; @@ -216,7 +227,7 @@ static int parse_ipv4(grpc_uri *uri, struct sockaddr_storage *addr, int *len) { gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port); goto done; } - in->sin_port = htons(port_num); + in->sin_port = htons((gpr_uint16)port_num); } else { gpr_log(GPR_ERROR, "no port given for ipv4 scheme"); goto done; @@ -229,7 +240,8 @@ done: return result; } -static int parse_ipv6(grpc_uri *uri, struct sockaddr_storage *addr, int *len) { +static int parse_ipv6(grpc_uri *uri, struct sockaddr_storage *addr, + size_t *len) { const char *host_port = uri->path; char *host; char *port; @@ -256,7 +268,7 @@ static int parse_ipv6(grpc_uri *uri, struct sockaddr_storage *addr, int *len) { gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port); goto done; } - in6->sin6_port = htons(port_num); + in6->sin6_port = htons((gpr_uint16)port_num); } else { gpr_log(GPR_ERROR, "no port given for ipv6 scheme"); goto done; @@ -270,37 +282,57 @@ done: } static void do_nothing(void *ignored) {} + static grpc_resolver *sockaddr_create( - grpc_uri *uri, - grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, - size_t num_subchannels), - grpc_subchannel_factory *subchannel_factory, - int parse(grpc_uri *uri, struct sockaddr_storage *dst, int *len)) { + grpc_resolver_args *args, const char *default_lb_policy_name, + int parse(grpc_uri *uri, struct sockaddr_storage *dst, size_t *len)) { size_t i; int errors_found = 0; /* GPR_FALSE */ sockaddr_resolver *r; gpr_slice path_slice; gpr_slice_buffer path_parts; - if (0 != strcmp(uri->authority, "")) { - gpr_log(GPR_ERROR, "authority based uri's not supported"); + if (0 != strcmp(args->uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme", + args->uri->scheme); return NULL; } r = gpr_malloc(sizeof(sockaddr_resolver)); memset(r, 0, sizeof(*r)); - path_slice = gpr_slice_new(uri->path, strlen(uri->path), do_nothing); + r->lb_policy_name = NULL; + if (0 != strcmp(args->uri->query, "")) { + gpr_slice query_slice; + gpr_slice_buffer query_parts; + + query_slice = + gpr_slice_new(args->uri->query, strlen(args->uri->query), do_nothing); + gpr_slice_buffer_init(&query_parts); + gpr_slice_split(query_slice, "=", &query_parts); + GPR_ASSERT(query_parts.count == 2); + if (0 == gpr_slice_str_cmp(query_parts.slices[0], "lb_policy")) { + r->lb_policy_name = gpr_dump_slice(query_parts.slices[1], GPR_DUMP_ASCII); + } + gpr_slice_buffer_destroy(&query_parts); + gpr_slice_unref(query_slice); + } + if (r->lb_policy_name == NULL) { + r->lb_policy_name = gpr_strdup(default_lb_policy_name); + } + + path_slice = + gpr_slice_new(args->uri->path, strlen(args->uri->path), do_nothing); gpr_slice_buffer_init(&path_parts); gpr_slice_split(path_slice, ",", &path_parts); r->num_addrs = path_parts.count; r->addrs = gpr_malloc(sizeof(struct sockaddr_storage) * r->num_addrs); - r->addrs_len = gpr_malloc(sizeof(int) * r->num_addrs); + r->addrs_len = gpr_malloc(sizeof(*r->addrs_len) * r->num_addrs); - for(i = 0; i < r->num_addrs; i++) { - grpc_uri ith_uri = *uri; - char* part_str = gpr_dump_slice(path_parts.slices[i], GPR_DUMP_ASCII); + for (i = 0; i < r->num_addrs; i++) { + grpc_uri ith_uri = *args->uri; + char *part_str = gpr_dump_slice(path_parts.slices[i], GPR_DUMP_ASCII); ith_uri.path = part_str; if (!parse(&ith_uri, &r->addrs[i], &r->addrs_len[i])) { errors_found = 1; /* GPR_TRUE */ @@ -319,10 +351,9 @@ static grpc_resolver *sockaddr_create( gpr_ref_init(&r->refs, 1); gpr_mu_init(&r->mu); grpc_resolver_init(&r->base, &sockaddr_resolver_vtable); - r->subchannel_factory = subchannel_factory; - r->lb_policy_factory = lb_policy_factory; + r->subchannel_factory = args->subchannel_factory; + grpc_subchannel_factory_ref(r->subchannel_factory); - grpc_subchannel_factory_ref(subchannel_factory); return &r->base; } @@ -336,10 +367,8 @@ static void sockaddr_factory_unref(grpc_resolver_factory *factory) {} #define DECL_FACTORY(name) \ static grpc_resolver *name##_factory_create_resolver( \ - grpc_resolver_factory *factory, grpc_uri *uri, \ - grpc_subchannel_factory *subchannel_factory) { \ - return sockaddr_create(uri, grpc_create_pick_first_lb_policy, \ - subchannel_factory, parse_##name); \ + grpc_resolver_factory *factory, grpc_resolver_args *args) { \ + return sockaddr_create(args, "pick_first", parse_##name); \ } \ static const grpc_resolver_factory_vtable name##_factory_vtable = { \ sockaddr_factory_ref, sockaddr_factory_unref, \ @@ -353,5 +382,4 @@ static void sockaddr_factory_unref(grpc_resolver_factory *factory) {} #ifdef GPR_POSIX_SOCKET DECL_FACTORY(unix) #endif -DECL_FACTORY(ipv4) -DECL_FACTORY(ipv6) +DECL_FACTORY(ipv4) DECL_FACTORY(ipv6) diff --git a/src/core/client_config/resolvers/zookeeper_resolver.c b/src/core/client_config/resolvers/zookeeper_resolver.c index da399f9954..136197d4c6 100644 --- a/src/core/client_config/resolvers/zookeeper_resolver.c +++ b/src/core/client_config/resolvers/zookeeper_resolver.c @@ -41,10 +41,11 @@ #include <grpc/grpc_zookeeper.h> #include <zookeeper/zookeeper.h> -#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/client_config/lb_policy_registry.h" #include "src/core/client_config/resolver_registry.h" #include "src/core/iomgr/resolve_address.h" #include "src/core/support/string.h" +#include "src/core/surface/api_trace.h" #include "src/core/json/json.h" /** Zookeeper session expiration time in milliseconds */ @@ -59,9 +60,8 @@ typedef struct { char *name; /** subchannel factory */ grpc_subchannel_factory *subchannel_factory; - /** load balancing policy factory */ - grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, - size_t num_subchannels); + /** load balancing policy name */ + char *lb_policy_name; /** mutex guarding the rest of the state */ gpr_mu mu; @@ -72,7 +72,7 @@ typedef struct { /** which version of resolved_config is current? */ int resolved_version; /** pending next completion, or NULL */ - grpc_iomgr_closure *next_completion; + grpc_closure *next_completion; /** target config address for next completion */ grpc_client_config **target_config; /** current (fully resolved) config */ @@ -88,35 +88,44 @@ typedef struct { int resolved_num; } zookeeper_resolver; -static void zookeeper_destroy(grpc_resolver *r); +static void zookeeper_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void zookeeper_start_resolving_locked(zookeeper_resolver *r); -static void zookeeper_maybe_finish_next_locked(zookeeper_resolver *r); +static void zookeeper_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + zookeeper_resolver *r); -static void zookeeper_shutdown(grpc_resolver *r); -static void zookeeper_channel_saw_error(grpc_resolver *r, +static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); +static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx, + grpc_resolver *r, struct sockaddr *failing_address, int failing_address_len); -static void zookeeper_next(grpc_resolver *r, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete); +static void zookeeper_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, + grpc_client_config **target_config, + grpc_closure *on_complete); static const grpc_resolver_vtable zookeeper_resolver_vtable = { zookeeper_destroy, zookeeper_shutdown, zookeeper_channel_saw_error, zookeeper_next}; -static void zookeeper_shutdown(grpc_resolver *resolver) { +static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { zookeeper_resolver *r = (zookeeper_resolver *)resolver; + grpc_closure *call = NULL; gpr_mu_lock(&r->mu); if (r->next_completion != NULL) { *r->target_config = NULL; - grpc_iomgr_add_callback(r->next_completion); + call = r->next_completion; r->next_completion = NULL; } zookeeper_close(r->zookeeper_handle); gpr_mu_unlock(&r->mu); + if (call != NULL) { + call->cb(exec_ctx, call->cb_arg, 1); + } } -static void zookeeper_channel_saw_error(grpc_resolver *resolver, +static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver, struct sockaddr *sa, int len) { zookeeper_resolver *r = (zookeeper_resolver *)resolver; gpr_mu_lock(&r->mu); @@ -126,9 +135,9 @@ static void zookeeper_channel_saw_error(grpc_resolver *resolver, gpr_mu_unlock(&r->mu); } -static void zookeeper_next(grpc_resolver *resolver, +static void zookeeper_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_client_config **target_config, - grpc_iomgr_closure *on_complete) { + grpc_closure *on_complete) { zookeeper_resolver *r = (zookeeper_resolver *)resolver; gpr_mu_lock(&r->mu); GPR_ASSERT(r->next_completion == NULL); @@ -137,7 +146,7 @@ static void zookeeper_next(grpc_resolver *resolver, if (r->resolved_version == 0 && r->resolving == 0) { zookeeper_start_resolving_locked(r); } else { - zookeeper_maybe_finish_next_locked(r); + zookeeper_maybe_finish_next_locked(exec_ctx, r); } gpr_mu_unlock(&r->mu); } @@ -174,7 +183,7 @@ static void zookeeper_watcher(zhandle_t *zookeeper_handle, int type, int state, /** Callback function after getting all resolved addresses Creates a subchannel for each address */ -static void zookeeper_on_resolved(void *arg, +static void zookeeper_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_resolved_addresses *addresses) { zookeeper_resolver *r = arg; grpc_client_config *config = NULL; @@ -183,6 +192,7 @@ static void zookeeper_on_resolved(void *arg, grpc_lb_policy *lb_policy; size_t i; if (addresses != NULL) { + grpc_lb_policy_args lb_policy_args; config = grpc_client_config_create(); subchannels = gpr_malloc(sizeof(grpc_subchannel *) * addresses->naddrs); for (i = 0; i < addresses->naddrs; i++) { @@ -190,11 +200,13 @@ static void zookeeper_on_resolved(void *arg, args.addr = (struct sockaddr *)(addresses->addrs[i].addr); args.addr_len = addresses->addrs[i].len; subchannels[i] = grpc_subchannel_factory_create_subchannel( - r->subchannel_factory, &args); + exec_ctx, r->subchannel_factory, &args); } - lb_policy = r->lb_policy_factory(subchannels, addresses->naddrs); + lb_policy_args.subchannels = subchannels; + lb_policy_args.num_subchannels = addresses->naddrs; + lb_policy = grpc_lb_policy_create(r->lb_policy_name, &lb_policy_args); grpc_client_config_set_lb_policy(config, lb_policy); - GRPC_LB_POLICY_UNREF(lb_policy, "construction"); + GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "construction"); grpc_resolved_addresses_destroy(addresses); gpr_free(subchannels); } @@ -202,18 +214,18 @@ static void zookeeper_on_resolved(void *arg, GPR_ASSERT(r->resolving == 1); r->resolving = 0; if (r->resolved_config != NULL) { - grpc_client_config_unref(r->resolved_config); + grpc_client_config_unref(exec_ctx, r->resolved_config); } r->resolved_config = config; r->resolved_version++; - zookeeper_maybe_finish_next_locked(r); + zookeeper_maybe_finish_next_locked(exec_ctx, r); gpr_mu_unlock(&r->mu); - GRPC_RESOLVER_UNREF(&r->base, "zookeeper-resolving"); + GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "zookeeper-resolving"); } /** Callback function for each DNS resolved address */ -static void zookeeper_dns_resolved(void *arg, +static void zookeeper_dns_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_resolved_addresses *addresses) { size_t i; zookeeper_resolver *r = arg; @@ -239,12 +251,12 @@ static void zookeeper_dns_resolved(void *arg, resolve_done = (r->resolved_num == r->resolved_total); gpr_mu_unlock(&r->mu); if (resolve_done) { - zookeeper_on_resolved(r, r->resolved_addrs); + zookeeper_on_resolved(exec_ctx, r, r->resolved_addrs); } } /** Parses JSON format address of a zookeeper node */ -static char *zookeeper_parse_address(const char *value, int value_len) { +static char *zookeeper_parse_address(const char *value, size_t value_len) { grpc_json *json; grpc_json *cur; const char *host; @@ -288,13 +300,15 @@ static void zookeeper_get_children_node_completion(int rc, const char *value, char *address = NULL; zookeeper_resolver *r = (zookeeper_resolver *)arg; int resolve_done = 0; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; if (rc != 0) { gpr_log(GPR_ERROR, "Error in getting a child node of %s", r->name); + grpc_exec_ctx_finish(&exec_ctx); return; } - address = zookeeper_parse_address(value, value_len); + address = zookeeper_parse_address(value, (size_t)value_len); if (address != NULL) { /** Further resolves address by DNS */ grpc_resolve_address(address, NULL, zookeeper_dns_resolved, r); @@ -306,9 +320,11 @@ static void zookeeper_get_children_node_completion(int rc, const char *value, resolve_done = (r->resolved_num == r->resolved_total); gpr_mu_unlock(&r->mu); if (resolve_done) { - zookeeper_on_resolved(r, r->resolved_addrs); + zookeeper_on_resolved(&exec_ctx, r, r->resolved_addrs); } } + + grpc_exec_ctx_finish(&exec_ctx); } static void zookeeper_get_children_completion( @@ -364,7 +380,7 @@ static void zookeeper_get_node_completion(int rc, const char *value, /** If zookeeper node of path r->name does not have address (i.e. service node), get its children */ - address = zookeeper_parse_address(value, value_len); + address = zookeeper_parse_address(value, (size_t)value_len); if (address != NULL) { r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); r->resolved_addrs->addrs = NULL; @@ -399,40 +415,39 @@ static void zookeeper_start_resolving_locked(zookeeper_resolver *r) { zookeeper_resolve_address(r); } -static void zookeeper_maybe_finish_next_locked(zookeeper_resolver *r) { +static void zookeeper_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + zookeeper_resolver *r) { if (r->next_completion != NULL && r->resolved_version != r->published_version) { *r->target_config = r->resolved_config; if (r->resolved_config != NULL) { grpc_client_config_ref(r->resolved_config); } - grpc_iomgr_add_callback(r->next_completion); + grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, 1); r->next_completion = NULL; r->published_version = r->resolved_version; } } -static void zookeeper_destroy(grpc_resolver *gr) { +static void zookeeper_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { zookeeper_resolver *r = (zookeeper_resolver *)gr; gpr_mu_destroy(&r->mu); if (r->resolved_config != NULL) { - grpc_client_config_unref(r->resolved_config); + grpc_client_config_unref(exec_ctx, r->resolved_config); } - grpc_subchannel_factory_unref(r->subchannel_factory); + grpc_subchannel_factory_unref(exec_ctx, r->subchannel_factory); gpr_free(r->name); + gpr_free(r->lb_policy_name); gpr_free(r); } -static grpc_resolver *zookeeper_create( - grpc_uri *uri, - grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, - size_t num_subchannels), - grpc_subchannel_factory *subchannel_factory) { +static grpc_resolver *zookeeper_create(grpc_resolver_args *args, + const char *lb_policy_name) { zookeeper_resolver *r; size_t length; - char *path = uri->path; + char *path = args->uri->path; - if (0 == strcmp(uri->authority, "")) { + if (0 == strcmp(args->uri->authority, "")) { gpr_log(GPR_ERROR, "No authority specified in zookeeper uri"); return NULL; } @@ -450,14 +465,16 @@ static grpc_resolver *zookeeper_create( grpc_resolver_init(&r->base, &zookeeper_resolver_vtable); r->name = gpr_strdup(path); - r->subchannel_factory = subchannel_factory; - r->lb_policy_factory = lb_policy_factory; - grpc_subchannel_factory_ref(subchannel_factory); + r->subchannel_factory = args->subchannel_factory; + grpc_subchannel_factory_ref(r->subchannel_factory); + + r->lb_policy_name = gpr_strdup(lb_policy_name); /** Initializes zookeeper client */ zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); - r->zookeeper_handle = zookeeper_init(uri->authority, zookeeper_global_watcher, - GRPC_ZOOKEEPER_SESSION_TIMEOUT, 0, 0, 0); + r->zookeeper_handle = + zookeeper_init(args->uri->authority, zookeeper_global_watcher, + GRPC_ZOOKEEPER_SESSION_TIMEOUT, 0, 0, 0); if (r->zookeeper_handle == NULL) { gpr_log(GPR_ERROR, "Unable to connect to zookeeper server"); return NULL; @@ -471,6 +488,7 @@ static void zookeeper_plugin_init() { } void grpc_zookeeper_register() { + GRPC_API_TRACE("grpc_zookeeper_register(void)", 0, ()); grpc_register_plugin(zookeeper_plugin_init, NULL); } @@ -488,16 +506,15 @@ static char *zookeeper_factory_get_default_hostname( } static grpc_resolver *zookeeper_factory_create_resolver( - grpc_resolver_factory *factory, grpc_uri *uri, - grpc_subchannel_factory *subchannel_factory) { - return zookeeper_create(uri, grpc_create_pick_first_lb_policy, - subchannel_factory); + grpc_resolver_factory *factory, grpc_resolver_args *args) { + return zookeeper_create(args, "pick_first"); } static const grpc_resolver_factory_vtable zookeeper_factory_vtable = { zookeeper_factory_ref, zookeeper_factory_unref, zookeeper_factory_create_resolver, zookeeper_factory_get_default_hostname, "zookeeper"}; + static grpc_resolver_factory zookeeper_resolver_factory = { &zookeeper_factory_vtable}; diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c index ca52c75beb..0401dd3868 100644 --- a/src/core/client_config/subchannel.c +++ b/src/core/client_config/subchannel.c @@ -40,7 +40,7 @@ #include "src/core/channel/channel_args.h" #include "src/core/channel/client_channel.h" #include "src/core/channel/connected_channel.h" -#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/timer.h" #include "src/core/transport/connectivity_state.h" #include "src/core/surface/channel.h" @@ -59,7 +59,7 @@ typedef struct { } connection; typedef struct { - grpc_iomgr_closure closure; + grpc_closure closure; size_t version; grpc_subchannel *subchannel; grpc_connectivity_state connectivity_state; @@ -67,11 +67,11 @@ typedef struct { typedef struct waiting_for_connect { struct waiting_for_connect *next; - grpc_iomgr_closure *notify; + grpc_closure *notify; grpc_pollset *pollset; grpc_subchannel_call **target; grpc_subchannel *subchannel; - grpc_iomgr_closure continuation; + grpc_closure continuation; } waiting_for_connect; struct grpc_subchannel { @@ -99,7 +99,7 @@ struct grpc_subchannel { grpc_connect_out_args connecting_result; /** callback for connection finishing */ - grpc_iomgr_closure connected; + grpc_closure connected; /** pollset_set tracking who's interested in a connection being setup - owned by the master channel (in particular the @@ -130,7 +130,7 @@ struct grpc_subchannel { /** do we have an active alarm? */ int have_alarm; /** our alarm */ - grpc_alarm alarm; + grpc_timer alarm; /** current random value */ gpr_uint32 random; }; @@ -143,21 +143,25 @@ struct grpc_subchannel_call { #define SUBCHANNEL_CALL_TO_CALL_STACK(call) ((grpc_call_stack *)((call) + 1)) #define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)((con) + 1)) -static grpc_subchannel_call *create_call(connection *con); -static void connectivity_state_changed_locked(grpc_subchannel *c, +static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx, + connection *con); +static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c, const char *reason); static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c); static gpr_timespec compute_connect_deadline(grpc_subchannel *c); -static void subchannel_connected(void *subchannel, int iomgr_success); +static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *subchannel, + int iomgr_success); -static void subchannel_ref_locked( - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +static void subchannel_ref_locked(grpc_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); static int subchannel_unref_locked( grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT; static void connection_ref_locked(connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS); static grpc_subchannel *connection_unref_locked( + grpc_exec_ctx *exec_ctx, connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT; -static void subchannel_destroy(grpc_subchannel *c); +static void subchannel_destroy(grpc_exec_ctx *exec_ctx, grpc_subchannel *c); #ifdef GRPC_SUBCHANNEL_REFCOUNT_DEBUG #define SUBCHANNEL_REF_LOCKED(p, r) \ @@ -166,8 +170,8 @@ static void subchannel_destroy(grpc_subchannel *c); subchannel_unref_locked((p), __FILE__, __LINE__, (r)) #define CONNECTION_REF_LOCKED(p, r) \ connection_ref_locked((p), __FILE__, __LINE__, (r)) -#define CONNECTION_UNREF_LOCKED(p, r) \ - connection_unref_locked((p), __FILE__, __LINE__, (r)) +#define CONNECTION_UNREF_LOCKED(cl, p, r) \ + connection_unref_locked((cl), (p), __FILE__, __LINE__, (r)) #define REF_PASS_ARGS , file, line, reason #define REF_LOG(name, p) \ gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p ref %d -> %d %s", \ @@ -179,7 +183,7 @@ static void subchannel_destroy(grpc_subchannel *c); #define SUBCHANNEL_REF_LOCKED(p, r) subchannel_ref_locked((p)) #define SUBCHANNEL_UNREF_LOCKED(p, r) subchannel_unref_locked((p)) #define CONNECTION_REF_LOCKED(p, r) connection_ref_locked((p)) -#define CONNECTION_UNREF_LOCKED(p, r) connection_unref_locked((p)) +#define CONNECTION_UNREF_LOCKED(cl, p, r) connection_unref_locked((cl), (p)) #define REF_PASS_ARGS #define REF_LOG(name, p) \ do { \ @@ -193,28 +197,28 @@ static void subchannel_destroy(grpc_subchannel *c); * connection implementation */ -static void connection_destroy(connection *c) { +static void connection_destroy(grpc_exec_ctx *exec_ctx, connection *c) { GPR_ASSERT(c->refs == 0); - grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CONNECTION(c)); + grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c)); gpr_free(c); } -static void connection_ref_locked( - connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { +static void connection_ref_locked(connection *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { REF_LOG("CONNECTION", c); subchannel_ref_locked(c->subchannel REF_PASS_ARGS); ++c->refs; } static grpc_subchannel *connection_unref_locked( - connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + grpc_exec_ctx *exec_ctx, connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { grpc_subchannel *destroy = NULL; UNREF_LOG("CONNECTION", c); if (subchannel_unref_locked(c->subchannel REF_PASS_ARGS)) { destroy = c->subchannel; } if (--c->refs == 0 && c->subchannel->active != c) { - connection_destroy(c); + connection_destroy(exec_ctx, c); } return destroy; } @@ -223,14 +227,14 @@ static grpc_subchannel *connection_unref_locked( * grpc_subchannel implementation */ -static void subchannel_ref_locked( - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { +static void subchannel_ref_locked(grpc_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { REF_LOG("SUBCHANNEL", c); ++c->refs; } -static int subchannel_unref_locked( - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { +static int subchannel_unref_locked(grpc_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { UNREF_LOG("SUBCHANNEL", c); return --c->refs == 0; } @@ -241,35 +245,38 @@ void grpc_subchannel_ref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { gpr_mu_unlock(&c->mu); } -void grpc_subchannel_unref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { +void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { int destroy; gpr_mu_lock(&c->mu); destroy = subchannel_unref_locked(c REF_PASS_ARGS); gpr_mu_unlock(&c->mu); - if (destroy) subchannel_destroy(c); + if (destroy) subchannel_destroy(exec_ctx, c); } -static void subchannel_destroy(grpc_subchannel *c) { +static void subchannel_destroy(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { if (c->active != NULL) { - connection_destroy(c->active); + connection_destroy(exec_ctx, c->active); } - gpr_free(c->filters); + gpr_free((void *)c->filters); grpc_channel_args_destroy(c->args); gpr_free(c->addr); grpc_mdctx_unref(c->mdctx); - grpc_connectivity_state_destroy(&c->state_tracker); - grpc_connector_unref(c->connector); + grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker); + grpc_connector_unref(exec_ctx, c->connector); gpr_free(c); } -void grpc_subchannel_add_interested_party(grpc_subchannel *c, +void grpc_subchannel_add_interested_party(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c, grpc_pollset *pollset) { - grpc_pollset_set_add_pollset(c->pollset_set, pollset); + grpc_pollset_set_add_pollset(exec_ctx, c->pollset_set, pollset); } -void grpc_subchannel_del_interested_party(grpc_subchannel *c, +void grpc_subchannel_del_interested_party(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c, grpc_pollset *pollset) { - grpc_pollset_set_del_pollset(c->pollset_set, pollset); + grpc_pollset_set_del_pollset(exec_ctx, c->pollset_set, pollset); } static gpr_uint32 random_seed() { @@ -287,7 +294,7 @@ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, grpc_connector_ref(c->connector); c->num_filters = args->filter_count; c->filters = gpr_malloc(sizeof(grpc_channel_filter *) * c->num_filters); - memcpy(c->filters, args->filters, + memcpy((void *)c->filters, args->filters, sizeof(grpc_channel_filter *) * c->num_filters); c->addr = gpr_malloc(args->addr_len); memcpy(c->addr, args->addr, args->addr_len); @@ -298,14 +305,37 @@ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, c->pollset_set = grpc_client_channel_get_connecting_pollset_set(parent_elem); c->random = random_seed(); grpc_mdctx_ref(c->mdctx); - grpc_iomgr_closure_init(&c->connected, subchannel_connected, c); + grpc_closure_init(&c->connected, subchannel_connected, c); grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE, "subchannel"); gpr_mu_init(&c->mu); return c; } -static void continue_connect(grpc_subchannel *c) { +void grpc_subchannel_cancel_waiting_call(grpc_exec_ctx *exec_ctx, + grpc_subchannel *subchannel, + int iomgr_success) { + waiting_for_connect *w4c; + gpr_mu_lock(&subchannel->mu); + w4c = subchannel->waiting; + subchannel->waiting = NULL; + gpr_mu_unlock(&subchannel->mu); + while (w4c != NULL) { + waiting_for_connect *next = w4c->next; + grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel, + w4c->pollset); + if (w4c->notify) { + w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, iomgr_success); + } + + GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect"); + gpr_free(w4c); + + w4c = next; + } +} + +static void continue_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { grpc_connect_in_args args; args.interested_parties = c->pollset_set; @@ -313,32 +343,35 @@ static void continue_connect(grpc_subchannel *c) { args.addr_len = c->addr_len; args.deadline = compute_connect_deadline(c); args.channel_args = c->args; - args.metadata_context = c->mdctx; - grpc_connector_connect(c->connector, &args, &c->connecting_result, + grpc_connector_connect(exec_ctx, c->connector, &args, &c->connecting_result, &c->connected); } -static void start_connect(grpc_subchannel *c) { +static void start_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { c->backoff_delta = gpr_time_from_seconds( GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS, GPR_TIMESPAN); c->next_attempt = gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), c->backoff_delta); - continue_connect(c); + continue_connect(exec_ctx, c); } -static void continue_creating_call(void *arg, int iomgr_success) { +static void continue_creating_call(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + grpc_subchannel_call_create_status call_creation_status; waiting_for_connect *w4c = arg; - grpc_subchannel_del_interested_party(w4c->subchannel, w4c->pollset); - grpc_subchannel_create_call(w4c->subchannel, w4c->pollset, w4c->target, - w4c->notify); - GRPC_SUBCHANNEL_UNREF(w4c->subchannel, "waiting_for_connect"); + grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel, w4c->pollset); + call_creation_status = grpc_subchannel_create_call( + exec_ctx, w4c->subchannel, w4c->pollset, w4c->target, w4c->notify); + GPR_ASSERT(call_creation_status == GRPC_SUBCHANNEL_CALL_CREATE_READY); + w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, iomgr_success); + GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect"); gpr_free(w4c); } -void grpc_subchannel_create_call(grpc_subchannel *c, grpc_pollset *pollset, - grpc_subchannel_call **target, - grpc_iomgr_closure *notify) { +grpc_subchannel_call_create_status grpc_subchannel_create_call( + grpc_exec_ctx *exec_ctx, grpc_subchannel *c, grpc_pollset *pollset, + grpc_subchannel_call **target, grpc_closure *notify) { connection *con; gpr_mu_lock(&c->mu); if (c->active != NULL) { @@ -346,8 +379,8 @@ void grpc_subchannel_create_call(grpc_subchannel *c, grpc_pollset *pollset, CONNECTION_REF_LOCKED(con, "call"); gpr_mu_unlock(&c->mu); - *target = create_call(con); - notify->cb(notify->cb_arg, 1); + *target = create_call(exec_ctx, con); + return GRPC_SUBCHANNEL_CALL_CREATE_READY; } else { waiting_for_connect *w4c = gpr_malloc(sizeof(*w4c)); w4c->next = c->waiting; @@ -357,21 +390,22 @@ void grpc_subchannel_create_call(grpc_subchannel *c, grpc_pollset *pollset, w4c->subchannel = c; /* released when clearing w4c */ SUBCHANNEL_REF_LOCKED(c, "waiting_for_connect"); - grpc_iomgr_closure_init(&w4c->continuation, continue_creating_call, w4c); + grpc_closure_init(&w4c->continuation, continue_creating_call, w4c); c->waiting = w4c; - grpc_subchannel_add_interested_party(c, pollset); + grpc_subchannel_add_interested_party(exec_ctx, c, pollset); if (!c->connecting) { c->connecting = 1; - connectivity_state_changed_locked(c, "create_call"); + connectivity_state_changed_locked(exec_ctx, c, "create_call"); /* released by connection */ SUBCHANNEL_REF_LOCKED(c, "connecting"); GRPC_CHANNEL_INTERNAL_REF(c->master, "connecting"); gpr_mu_unlock(&c->mu); - start_connect(c); + start_connect(exec_ctx, c); } else { gpr_mu_unlock(&c->mu); } + return GRPC_SUBCHANNEL_CALL_CREATE_PENDING; } } @@ -383,65 +417,84 @@ grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c) { return state; } -void grpc_subchannel_notify_on_state_change(grpc_subchannel *c, +void grpc_subchannel_notify_on_state_change(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c, grpc_connectivity_state *state, - grpc_iomgr_closure *notify) { + grpc_closure *notify) { int do_connect = 0; gpr_mu_lock(&c->mu); - if (grpc_connectivity_state_notify_on_state_change(&c->state_tracker, state, - notify)) { + if (grpc_connectivity_state_notify_on_state_change( + exec_ctx, &c->state_tracker, state, notify)) { do_connect = 1; c->connecting = 1; /* released by connection */ SUBCHANNEL_REF_LOCKED(c, "connecting"); GRPC_CHANNEL_INTERNAL_REF(c->master, "connecting"); - connectivity_state_changed_locked(c, "state_change"); + connectivity_state_changed_locked(exec_ctx, c, "state_change"); } gpr_mu_unlock(&c->mu); + if (do_connect) { - start_connect(c); + start_connect(exec_ctx, c); } } -void grpc_subchannel_process_transport_op(grpc_subchannel *c, +int grpc_subchannel_state_change_unsubscribe(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c, + grpc_closure *subscribed_notify) { + int success; + gpr_mu_lock(&c->mu); + success = grpc_connectivity_state_change_unsubscribe( + exec_ctx, &c->state_tracker, subscribed_notify); + gpr_mu_unlock(&c->mu); + return success; +} + +void grpc_subchannel_process_transport_op(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c, grpc_transport_op *op) { connection *con = NULL; grpc_subchannel *destroy; int cancel_alarm = 0; gpr_mu_lock(&c->mu); + if (c->active != NULL) { + con = c->active; + CONNECTION_REF_LOCKED(con, "transport-op"); + } if (op->disconnect) { c->disconnected = 1; - connectivity_state_changed_locked(c, "disconnect"); + connectivity_state_changed_locked(exec_ctx, c, "disconnect"); if (c->have_alarm) { cancel_alarm = 1; } } - if (c->active != NULL) { - con = c->active; - CONNECTION_REF_LOCKED(con, "transport-op"); - } gpr_mu_unlock(&c->mu); if (con != NULL) { grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con); grpc_channel_element *top_elem = grpc_channel_stack_element(channel_stack, 0); - top_elem->filter->start_transport_op(top_elem, op); + top_elem->filter->start_transport_op(exec_ctx, top_elem, op); gpr_mu_lock(&c->mu); - destroy = CONNECTION_UNREF_LOCKED(con, "transport-op"); + destroy = CONNECTION_UNREF_LOCKED(exec_ctx, con, "transport-op"); gpr_mu_unlock(&c->mu); if (destroy) { - subchannel_destroy(destroy); + subchannel_destroy(exec_ctx, destroy); } } if (cancel_alarm) { - grpc_alarm_cancel(&c->alarm); + grpc_timer_cancel(exec_ctx, &c->alarm); + } + + if (op->disconnect) { + grpc_connector_shutdown(exec_ctx, c->connector); } } -static void on_state_changed(void *p, int iomgr_success) { +static void on_state_changed(grpc_exec_ctx *exec_ctx, void *p, + int iomgr_success) { state_watcher *sw = p; grpc_subchannel *c = sw->subchannel; gpr_mu *mu = &c->mu; @@ -468,7 +521,7 @@ static void on_state_changed(void *p, int iomgr_success) { op.on_connectivity_state_change = &sw->closure; elem = grpc_channel_stack_element( CHANNEL_STACK_FROM_CONNECTION(c->active), 0); - elem->filter->start_transport_op(elem, &op); + elem->filter->start_transport_op(exec_ctx, elem, &op); /* early out */ gpr_mu_unlock(mu); return; @@ -479,27 +532,28 @@ static void on_state_changed(void *p, int iomgr_success) { destroy_connection = sw->subchannel->active; } sw->subchannel->active = NULL; - grpc_connectivity_state_set( - &c->state_tracker, c->disconnected ? GRPC_CHANNEL_FATAL_FAILURE - : GRPC_CHANNEL_TRANSIENT_FAILURE, - "connection_failed"); + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, + c->disconnected + ? GRPC_CHANNEL_FATAL_FAILURE + : GRPC_CHANNEL_TRANSIENT_FAILURE, + "connection_failed"); break; } done: - connectivity_state_changed_locked(c, "transport_state_changed"); + connectivity_state_changed_locked(exec_ctx, c, "transport_state_changed"); destroy = SUBCHANNEL_UNREF_LOCKED(c, "state_watcher"); gpr_free(sw); gpr_mu_unlock(mu); if (destroy) { - subchannel_destroy(c); + subchannel_destroy(exec_ctx, c); } if (destroy_connection != NULL) { - connection_destroy(destroy_connection); + connection_destroy(exec_ctx, destroy_connection); } } -static void publish_transport(grpc_subchannel *c) { +static void publish_transport(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { size_t channel_stack_size; connection *con; grpc_channel_stack *stk; @@ -514,8 +568,8 @@ static void publish_transport(grpc_subchannel *c) { /* build final filter list */ num_filters = c->num_filters + c->connecting_result.num_filters + 1; filters = gpr_malloc(sizeof(*filters) * num_filters); - memcpy(filters, c->filters, sizeof(*filters) * c->num_filters); - memcpy(filters + c->num_filters, c->connecting_result.filters, + memcpy((void *)filters, c->filters, sizeof(*filters) * c->num_filters); + memcpy((void *)(filters + c->num_filters), c->connecting_result.filters, sizeof(*filters) * c->connecting_result.num_filters); filters[num_filters - 1] = &grpc_connected_channel_filter; @@ -525,15 +579,15 @@ static void publish_transport(grpc_subchannel *c) { stk = (grpc_channel_stack *)(con + 1); con->refs = 0; con->subchannel = c; - grpc_channel_stack_init(filters, num_filters, c->master, c->args, c->mdctx, - stk); + grpc_channel_stack_init(exec_ctx, filters, num_filters, c->master, c->args, + c->mdctx, stk); grpc_connected_channel_bind_transport(stk, c->connecting_result.transport); - gpr_free(c->connecting_result.filters); + gpr_free((void *)c->connecting_result.filters); memset(&c->connecting_result, 0, sizeof(c->connecting_result)); /* initialize state watcher */ sw = gpr_malloc(sizeof(*sw)); - grpc_iomgr_closure_init(&sw->closure, on_state_changed, sw); + grpc_closure_init(&sw->closure, on_state_changed, sw); sw->subchannel = c; sw->connectivity_state = GRPC_CHANNEL_READY; @@ -542,10 +596,10 @@ static void publish_transport(grpc_subchannel *c) { if (c->disconnected) { gpr_mu_unlock(&c->mu); gpr_free(sw); - gpr_free(filters); - grpc_channel_stack_destroy(stk); - GRPC_CHANNEL_INTERNAL_UNREF(c->master, "connecting"); - GRPC_SUBCHANNEL_UNREF(c, "connecting"); + gpr_free((void *)filters); + grpc_channel_stack_destroy(exec_ctx, stk); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting"); + GRPC_SUBCHANNEL_UNREF(exec_ctx, c, "connecting"); return; } @@ -565,25 +619,29 @@ static void publish_transport(grpc_subchannel *c) { op.on_connectivity_state_change = &sw->closure; op.bind_pollset_set = c->pollset_set; SUBCHANNEL_REF_LOCKED(c, "state_watcher"); - GRPC_CHANNEL_INTERNAL_UNREF(c->master, "connecting"); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting"); GPR_ASSERT(!SUBCHANNEL_UNREF_LOCKED(c, "connecting")); elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(c->active), 0); - elem->filter->start_transport_op(elem, &op); + elem->filter->start_transport_op(exec_ctx, elem, &op); /* signal completion */ - connectivity_state_changed_locked(c, "connected"); - while ((w4c = c->waiting)) { - c->waiting = w4c->next; - grpc_iomgr_add_callback(&w4c->continuation); - } + connectivity_state_changed_locked(exec_ctx, c, "connected"); + w4c = c->waiting; + c->waiting = NULL; gpr_mu_unlock(&c->mu); - gpr_free(filters); + while (w4c != NULL) { + waiting_for_connect *next = w4c->next; + grpc_exec_ctx_enqueue(exec_ctx, &w4c->continuation, 1); + w4c = next; + } + + gpr_free((void *)filters); if (destroy_connection != NULL) { - connection_destroy(destroy_connection); + connection_destroy(exec_ctx, destroy_connection); } } @@ -616,35 +674,37 @@ static void update_reconnect_parameters(grpc_subchannel *c) { gpr_time_add(c->next_attempt, gpr_time_from_millis(jitter, GPR_TIMESPAN)); } -static void on_alarm(void *arg, int iomgr_success) { +static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) { grpc_subchannel *c = arg; gpr_mu_lock(&c->mu); c->have_alarm = 0; if (c->disconnected) { iomgr_success = 0; } - connectivity_state_changed_locked(c, "alarm"); + connectivity_state_changed_locked(exec_ctx, c, "alarm"); gpr_mu_unlock(&c->mu); if (iomgr_success) { update_reconnect_parameters(c); - continue_connect(c); + continue_connect(exec_ctx, c); } else { - GRPC_CHANNEL_INTERNAL_UNREF(c->master, "connecting"); - GRPC_SUBCHANNEL_UNREF(c, "connecting"); + grpc_subchannel_cancel_waiting_call(exec_ctx, c, iomgr_success); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting"); + GRPC_SUBCHANNEL_UNREF(exec_ctx, c, "connecting"); } } -static void subchannel_connected(void *arg, int iomgr_success) { +static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { grpc_subchannel *c = arg; if (c->connecting_result.transport != NULL) { - publish_transport(c); + publish_transport(exec_ctx, c); } else { gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); gpr_mu_lock(&c->mu); GPR_ASSERT(!c->have_alarm); c->have_alarm = 1; - connectivity_state_changed_locked(c, "connect_failed"); - grpc_alarm_init(&c->alarm, c->next_attempt, on_alarm, c, now); + connectivity_state_changed_locked(exec_ctx, c, "connect_failed"); + grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, on_alarm, c, now); gpr_mu_unlock(&c->mu); } } @@ -676,57 +736,70 @@ static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c) { return GRPC_CHANNEL_IDLE; } -static void connectivity_state_changed_locked(grpc_subchannel *c, +static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c, const char *reason) { grpc_connectivity_state current = compute_connectivity_locked(c); - grpc_connectivity_state_set(&c->state_tracker, current, reason); + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, current, reason); } /* * grpc_subchannel_call implementation */ -void grpc_subchannel_call_ref( - grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { +void grpc_subchannel_call_ref(grpc_subchannel_call *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { gpr_ref(&c->refs); } -void grpc_subchannel_call_unref( - grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { +void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel_call *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { if (gpr_unref(&c->refs)) { gpr_mu *mu = &c->connection->subchannel->mu; grpc_subchannel *destroy; - grpc_call_stack_destroy(SUBCHANNEL_CALL_TO_CALL_STACK(c)); + grpc_call_stack_destroy(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c)); gpr_mu_lock(mu); - destroy = CONNECTION_UNREF_LOCKED(c->connection, "call"); + destroy = CONNECTION_UNREF_LOCKED(exec_ctx, c->connection, "call"); gpr_mu_unlock(mu); gpr_free(c); if (destroy != NULL) { - subchannel_destroy(destroy); + subchannel_destroy(exec_ctx, destroy); } } } -char *grpc_subchannel_call_get_peer(grpc_subchannel_call *call) { +char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx, + grpc_subchannel_call *call) { grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0); - return top_elem->filter->get_peer(top_elem); + return top_elem->filter->get_peer(exec_ctx, top_elem); } -void grpc_subchannel_call_process_op(grpc_subchannel_call *call, +void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, + grpc_subchannel_call *call, grpc_transport_stream_op *op) { grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0); - top_elem->filter->start_transport_stream_op(top_elem, op); + top_elem->filter->start_transport_stream_op(exec_ctx, top_elem, op); } -grpc_subchannel_call *create_call(connection *con) { +static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx, + connection *con) { grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con); grpc_subchannel_call *call = gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size); grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(call); call->connection = con; gpr_ref_init(&call->refs, 1); - grpc_call_stack_init(chanstk, NULL, NULL, callstk); + grpc_call_stack_init(exec_ctx, chanstk, NULL, NULL, callstk); return call; } + +grpc_mdctx *grpc_subchannel_get_mdctx(grpc_subchannel *subchannel) { + return subchannel->mdctx; +} + +grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel) { + return subchannel->master; +} diff --git a/src/core/client_config/subchannel.h b/src/core/client_config/subchannel.h index 2e36c69134..ec1cc7cc69 100644 --- a/src/core/client_config/subchannel.h +++ b/src/core/client_config/subchannel.h @@ -36,6 +36,7 @@ #include "src/core/channel/channel_stack.h" #include "src/core/client_config/connector.h" +#include "src/core/transport/connectivity_state.h" /** A (sub-)channel that knows how to connect to exactly one target address. Provides a target for load balancing. */ @@ -46,39 +47,59 @@ typedef struct grpc_subchannel_args grpc_subchannel_args; #ifdef GRPC_SUBCHANNEL_REFCOUNT_DEBUG #define GRPC_SUBCHANNEL_REF(p, r) \ grpc_subchannel_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_SUBCHANNEL_UNREF(p, r) \ - grpc_subchannel_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_UNREF(cl, p, r) \ + grpc_subchannel_unref((cl), (p), __FILE__, __LINE__, (r)) #define GRPC_SUBCHANNEL_CALL_REF(p, r) \ grpc_subchannel_call_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_SUBCHANNEL_CALL_UNREF(p, r) \ - grpc_subchannel_call_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_CALL_UNREF(cl, p, r) \ + grpc_subchannel_call_unref((cl), (p), __FILE__, __LINE__, (r)) #define GRPC_SUBCHANNEL_REF_EXTRA_ARGS \ , const char *file, int line, const char *reason #else #define GRPC_SUBCHANNEL_REF(p, r) grpc_subchannel_ref((p)) -#define GRPC_SUBCHANNEL_UNREF(p, r) grpc_subchannel_unref((p)) +#define GRPC_SUBCHANNEL_UNREF(cl, p, r) grpc_subchannel_unref((cl), (p)) #define GRPC_SUBCHANNEL_CALL_REF(p, r) grpc_subchannel_call_ref((p)) -#define GRPC_SUBCHANNEL_CALL_UNREF(p, r) grpc_subchannel_call_unref((p)) +#define GRPC_SUBCHANNEL_CALL_UNREF(cl, p, r) \ + grpc_subchannel_call_unref((cl), (p)) #define GRPC_SUBCHANNEL_REF_EXTRA_ARGS #endif -void grpc_subchannel_ref( - grpc_subchannel *channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -void grpc_subchannel_unref( - grpc_subchannel *channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -void grpc_subchannel_call_ref( - grpc_subchannel_call *call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -void grpc_subchannel_call_unref( - grpc_subchannel_call *call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); - -/** construct a call (possibly asynchronously) */ -void grpc_subchannel_create_call(grpc_subchannel *subchannel, - grpc_pollset *pollset, - grpc_subchannel_call **target, - grpc_iomgr_closure *notify); +void grpc_subchannel_ref(grpc_subchannel *channel + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel *channel + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_call_ref(grpc_subchannel_call *call + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel_call *call + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); + +typedef enum { + GRPC_SUBCHANNEL_CALL_CREATE_READY, + GRPC_SUBCHANNEL_CALL_CREATE_PENDING +} grpc_subchannel_call_create_status; + +/** construct a subchannel call (possibly asynchronously). + * + * If the returned status is \a GRPC_SUBCHANNEL_CALL_CREATE_READY, the call will + * return immediately and \a target will point to a connected \a subchannel_call + * instance. Note that \a notify will \em not be invoked in this case. + * Otherwise, if the returned status is GRPC_SUBCHANNEL_CALL_CREATE_PENDING, the + * subchannel call will be created asynchronously, invoking the \a notify + * callback upon completion. */ +grpc_subchannel_call_create_status grpc_subchannel_create_call( + grpc_exec_ctx *exec_ctx, grpc_subchannel *subchannel, grpc_pollset *pollset, + grpc_subchannel_call **target, grpc_closure *notify); + +/** cancel \a call in the waiting state. */ +void grpc_subchannel_cancel_waiting_call(grpc_exec_ctx *exec_ctx, + grpc_subchannel *subchannel, + int iomgr_success); /** process a transport level op */ -void grpc_subchannel_process_transport_op(grpc_subchannel *subchannel, +void grpc_subchannel_process_transport_op(grpc_exec_ctx *exec_ctx, + grpc_subchannel *subchannel, grpc_transport_op *op); /** poll the current connectivity state of a channel */ @@ -87,23 +108,35 @@ grpc_connectivity_state grpc_subchannel_check_connectivity( /** call notify when the connectivity state of a channel changes from *state. Updates *state with the new state of the channel */ -void grpc_subchannel_notify_on_state_change(grpc_subchannel *channel, +void grpc_subchannel_notify_on_state_change(grpc_exec_ctx *exec_ctx, + grpc_subchannel *channel, grpc_connectivity_state *state, - grpc_iomgr_closure *notify); + grpc_closure *notify); + +/** Remove \a subscribed_notify from the list of closures to be called on a + * state change if present, returning 1. Otherwise, nothing is done and return + * 0. */ +int grpc_subchannel_state_change_unsubscribe(grpc_exec_ctx *exec_ctx, + grpc_subchannel *channel, + grpc_closure *subscribed_notify); /** express interest in \a channel's activities through \a pollset. */ -void grpc_subchannel_add_interested_party(grpc_subchannel *channel, +void grpc_subchannel_add_interested_party(grpc_exec_ctx *exec_ctx, + grpc_subchannel *channel, grpc_pollset *pollset); /** stop following \a channel's activity through \a pollset. */ -void grpc_subchannel_del_interested_party(grpc_subchannel *channel, +void grpc_subchannel_del_interested_party(grpc_exec_ctx *exec_ctx, + grpc_subchannel *channel, grpc_pollset *pollset); /** continue processing a transport op */ -void grpc_subchannel_call_process_op(grpc_subchannel_call *subchannel_call, +void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, + grpc_subchannel_call *subchannel_call, grpc_transport_stream_op *op); /** continue querying for peer */ -char *grpc_subchannel_call_get_peer(grpc_subchannel_call *subchannel_call); +char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx, + grpc_subchannel_call *subchannel_call); struct grpc_subchannel_args { /** Channel filters for this channel - wrapped factories will likely @@ -126,4 +159,10 @@ struct grpc_subchannel_args { grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, grpc_subchannel_args *args); +/** Return the metadata context associated with the subchannel */ +grpc_mdctx *grpc_subchannel_get_mdctx(grpc_subchannel *subchannel); + +/** Return the master channel associated with the subchannel */ +grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel); + #endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H */ diff --git a/src/core/client_config/subchannel_factory.c b/src/core/client_config/subchannel_factory.c index f71386594c..2c64219e8b 100644 --- a/src/core/client_config/subchannel_factory.c +++ b/src/core/client_config/subchannel_factory.c @@ -33,14 +33,17 @@ #include "src/core/client_config/subchannel_factory.h" -void grpc_subchannel_factory_ref(grpc_subchannel_factory *factory) { +void grpc_subchannel_factory_ref(grpc_subchannel_factory* factory) { factory->vtable->ref(factory); } -void grpc_subchannel_factory_unref(grpc_subchannel_factory *factory) { - factory->vtable->unref(factory); + +void grpc_subchannel_factory_unref(grpc_exec_ctx* exec_ctx, + grpc_subchannel_factory* factory) { + factory->vtable->unref(exec_ctx, factory); } -grpc_subchannel *grpc_subchannel_factory_create_subchannel( - grpc_subchannel_factory *factory, grpc_subchannel_args *args) { - return factory->vtable->create_subchannel(factory, args); +grpc_subchannel* grpc_subchannel_factory_create_subchannel( + grpc_exec_ctx* exec_ctx, grpc_subchannel_factory* factory, + grpc_subchannel_args* args) { + return factory->vtable->create_subchannel(exec_ctx, factory, args); } diff --git a/src/core/client_config/subchannel_factory.h b/src/core/client_config/subchannel_factory.h index d7eae1c964..c6d8cc90be 100644 --- a/src/core/client_config/subchannel_factory.h +++ b/src/core/client_config/subchannel_factory.h @@ -48,16 +48,19 @@ struct grpc_subchannel_factory { struct grpc_subchannel_factory_vtable { void (*ref)(grpc_subchannel_factory *factory); - void (*unref)(grpc_subchannel_factory *factory); - grpc_subchannel *(*create_subchannel)(grpc_subchannel_factory *factory, + void (*unref)(grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *factory); + grpc_subchannel *(*create_subchannel)(grpc_exec_ctx *exec_ctx, + grpc_subchannel_factory *factory, grpc_subchannel_args *args); }; void grpc_subchannel_factory_ref(grpc_subchannel_factory *factory); -void grpc_subchannel_factory_unref(grpc_subchannel_factory *factory); +void grpc_subchannel_factory_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel_factory *factory); /** Create a new grpc_subchannel */ grpc_subchannel *grpc_subchannel_factory_create_subchannel( - grpc_subchannel_factory *factory, grpc_subchannel_args *args); + grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *factory, + grpc_subchannel_args *args); #endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H */ diff --git a/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h b/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h index 8457294000..76a535ebed 100644 --- a/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h +++ b/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h @@ -43,4 +43,4 @@ grpc_subchannel_factory *grpc_subchannel_factory_add_channel_arg( grpc_subchannel_factory *input, const grpc_arg *arg); #endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_ADD_CHANNEL_ARG_H \ - */ + */ diff --git a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c index c1b5507fde..cd25fdcf0f 100644 --- a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c +++ b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c @@ -47,23 +47,25 @@ static void merge_args_factory_ref(grpc_subchannel_factory *scf) { gpr_ref(&f->refs); } -static void merge_args_factory_unref(grpc_subchannel_factory *scf) { +static void merge_args_factory_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel_factory *scf) { merge_args_factory *f = (merge_args_factory *)scf; if (gpr_unref(&f->refs)) { - grpc_subchannel_factory_unref(f->wrapped); + grpc_subchannel_factory_unref(exec_ctx, f->wrapped); grpc_channel_args_destroy(f->merge_args); gpr_free(f); } } static grpc_subchannel *merge_args_factory_create_subchannel( - grpc_subchannel_factory *scf, grpc_subchannel_args *args) { + grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *scf, + grpc_subchannel_args *args) { merge_args_factory *f = (merge_args_factory *)scf; grpc_channel_args *final_args = grpc_channel_args_merge(args->args, f->merge_args); grpc_subchannel *s; args->args = final_args; - s = grpc_subchannel_factory_create_subchannel(f->wrapped, args); + s = grpc_subchannel_factory_create_subchannel(exec_ctx, f->wrapped, args); grpc_channel_args_destroy(final_args); return s; } diff --git a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h index f4757f0650..a9e1691871 100644 --- a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h +++ b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h @@ -43,4 +43,4 @@ grpc_subchannel_factory *grpc_subchannel_factory_merge_channel_args( grpc_subchannel_factory *input, const grpc_channel_args *args); #endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_MERGE_CHANNEL_ARGS_H \ - */ + */ diff --git a/src/core/client_config/uri_parser.c b/src/core/client_config/uri_parser.c index 410a61c8cf..cbdfffcf8e 100644 --- a/src/core/client_config/uri_parser.c +++ b/src/core/client_config/uri_parser.c @@ -37,12 +37,16 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> -static grpc_uri *bad_uri(const char *uri_text, int pos, const char *section, +/** a size_t default value... maps to all 1's */ +#define NOT_SET (~(size_t)0) + +static grpc_uri *bad_uri(const char *uri_text, size_t pos, const char *section, int suppress_errors) { char *line_prefix; - int pfx_len; + size_t pfx_len; if (!suppress_errors) { gpr_asprintf(&line_prefix, "bad uri.%s: '", section); @@ -60,22 +64,88 @@ static grpc_uri *bad_uri(const char *uri_text, int pos, const char *section, return NULL; } -static char *copy_fragment(const char *src, int begin, int end) { +/** Returns a copy of \a src[begin, end) */ +static char *copy_component(const char *src, size_t begin, size_t end) { char *out = gpr_malloc(end - begin + 1); memcpy(out, src + begin, end - begin); out[end - begin] = 0; return out; } +/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar + * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent + * sign not followed by two hex digits), NOT_SET is returned. */ +static size_t parse_pchar(const char *uri_text, size_t i) { + /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" */ + char c = uri_text[i]; + if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || + ((c >= '0') && (c <= '9')) || + (c == '-' || c == '.' || c == '_' || c == '~') || /* unreserved */ + (c == '!' || c == '$' || c == '&' || c == '\'' || c == '$' || c == '&' || + c == '(' || c == ')' || c == '*' || c == '+' || c == ',' || c == ';' || + c == '=') /* sub-delims */) { + return 1; + } + if (c == '%') { /* pct-encoded */ + size_t j; + if (uri_text[i + 1] == 0 || uri_text[i + 2] == 0) { + return NOT_SET; + } + for (j = i + 1; j < 2; j++) { + c = uri_text[j]; + if (!(((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || + ((c >= 'A') && (c <= 'F')))) { + return NOT_SET; + } + } + return 2; + } + return 0; +} + +/* *( pchar / "?" / "/" ) */ +static int parse_fragment_or_query(const char *uri_text, size_t *i) { + char c; + while ((c = uri_text[*i]) != 0) { + const size_t advance = parse_pchar(uri_text, *i); /* pchar */ + switch (advance) { + case 0: /* uri_text[i] isn't in pchar */ + /* maybe it's ? or / */ + if (uri_text[*i] == '?' || uri_text[*i] == '/') { + (*i)++; + break; + } else { + return 1; + } + GPR_UNREACHABLE_CODE(return 0); + default: + (*i) += advance; + break; + case NOT_SET: /* uri_text[i] introduces an invalid URI */ + return 0; + } + } + /* *i is the first uri_text position past the \a query production, maybe \0 */ + return 1; +} + grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) { grpc_uri *uri; - int scheme_begin = 0; - int scheme_end = -1; - int authority_begin = -1; - int authority_end = -1; - int path_begin = -1; - int path_end = -1; - int i; + size_t scheme_begin = 0; + size_t scheme_end = NOT_SET; + size_t authority_begin = NOT_SET; + size_t authority_end = NOT_SET; + size_t path_begin = NOT_SET; + size_t path_end = NOT_SET; + size_t query_begin = NOT_SET; + size_t query_end = NOT_SET; + size_t fragment_begin = NOT_SET; + size_t fragment_end = NOT_SET; + size_t i; for (i = scheme_begin; uri_text[i] != 0; i++) { if (uri_text[i] == ':') { @@ -92,27 +162,22 @@ grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) { } break; } - if (scheme_end == -1) { + if (scheme_end == NOT_SET) { return bad_uri(uri_text, i, "scheme", suppress_errors); } if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') { authority_begin = scheme_end + 3; - for (i = authority_begin; uri_text[i] != 0 && authority_end == -1; i++) { - if (uri_text[i] == '/') { + for (i = authority_begin; uri_text[i] != 0 && authority_end == NOT_SET; + i++) { + if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') { authority_end = i; } - if (uri_text[i] == '?') { - return bad_uri(uri_text, i, "query_not_supported", suppress_errors); - } - if (uri_text[i] == '#') { - return bad_uri(uri_text, i, "fragment_not_supported", suppress_errors); - } } - if (authority_end == -1 && uri_text[i] == 0) { + if (authority_end == NOT_SET && uri_text[i] == 0) { authority_end = i; } - if (authority_end == -1) { + if (authority_end == NOT_SET) { return bad_uri(uri_text, i, "authority", suppress_errors); } /* TODO(ctiller): parse the authority correctly */ @@ -122,20 +187,46 @@ grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) { } for (i = path_begin; uri_text[i] != 0; i++) { - if (uri_text[i] == '?') { - return bad_uri(uri_text, i, "query_not_supported", suppress_errors); + if (uri_text[i] == '?' || uri_text[i] == '#') { + path_end = i; + break; + } + } + if (path_end == NOT_SET && uri_text[i] == 0) { + path_end = i; + } + if (path_end == NOT_SET) { + return bad_uri(uri_text, i, "path", suppress_errors); + } + + if (uri_text[i] == '?') { + query_begin = ++i; + if (!parse_fragment_or_query(uri_text, &i)) { + return bad_uri(uri_text, i, "query", suppress_errors); + } else if (uri_text[i] != 0 && uri_text[i] != '#') { + /* We must be at the end or at the beginning of a fragment */ + return bad_uri(uri_text, i, "query", suppress_errors); } - if (uri_text[i] == '#') { - return bad_uri(uri_text, i, "fragment_not_supported", suppress_errors); + query_end = i; + } + if (uri_text[i] == '#') { + fragment_begin = ++i; + if (!parse_fragment_or_query(uri_text, &i)) { + return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors); + } else if (uri_text[i] != 0) { + /* We must be at the end */ + return bad_uri(uri_text, i, "fragment", suppress_errors); } + fragment_end = i; } - path_end = i; uri = gpr_malloc(sizeof(*uri)); memset(uri, 0, sizeof(*uri)); - uri->scheme = copy_fragment(uri_text, scheme_begin, scheme_end); - uri->authority = copy_fragment(uri_text, authority_begin, authority_end); - uri->path = copy_fragment(uri_text, path_begin, path_end); + uri->scheme = copy_component(uri_text, scheme_begin, scheme_end); + uri->authority = copy_component(uri_text, authority_begin, authority_end); + uri->path = copy_component(uri_text, path_begin, path_end); + uri->query = copy_component(uri_text, query_begin, query_end); + uri->fragment = copy_component(uri_text, fragment_begin, fragment_end); return uri; } @@ -145,5 +236,7 @@ void grpc_uri_destroy(grpc_uri *uri) { gpr_free(uri->scheme); gpr_free(uri->authority); gpr_free(uri->path); + gpr_free(uri->query); + gpr_free(uri->fragment); gpr_free(uri); } diff --git a/src/core/client_config/uri_parser.h b/src/core/client_config/uri_parser.h index ce4e6aecb0..b8daa13bd4 100644 --- a/src/core/client_config/uri_parser.h +++ b/src/core/client_config/uri_parser.h @@ -38,6 +38,8 @@ typedef struct { char *scheme; char *authority; char *path; + char *query; + char *fragment; } grpc_uri; /** parse a uri, return NULL on failure */ diff --git a/src/core/compression/algorithm.c b/src/core/compression/algorithm.c index 6ed6dbe93f..fd95a3c891 100644 --- a/src/core/compression/algorithm.c +++ b/src/core/compression/algorithm.c @@ -33,7 +33,11 @@ #include <stdlib.h> #include <string.h> + #include <grpc/compression.h> +#include <grpc/support/useful.h> + +#include "src/core/surface/api_trace.h" int grpc_compression_algorithm_parse(const char *name, size_t name_length, grpc_compression_algorithm *algorithm) { @@ -41,6 +45,11 @@ int grpc_compression_algorithm_parse(const char *name, size_t name_length, * doesn't matter, given that we are comparing against string literals, but * because this way we needn't have "name" nil-terminated (useful for slice * data, for example) */ + GRPC_API_TRACE( + "grpc_compression_algorithm_parse(" + "name=%*.*s, name_length=%lu, algorithm=%p)", + 5, ((int)name_length, (int)name_length, name, (unsigned long)name_length, + algorithm)); if (name_length == 0) { return 0; } @@ -58,6 +67,8 @@ int grpc_compression_algorithm_parse(const char *name, size_t name_length, int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm, char **name) { + GRPC_API_TRACE("grpc_compression_algorithm_parse(algorithm=%d, name=%p)", 2, + ((int)algorithm, name)); switch (algorithm) { case GRPC_COMPRESS_NONE: *name = "identity"; @@ -78,6 +89,8 @@ int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm, * compression algorithms */ grpc_compression_algorithm grpc_compression_algorithm_for_level( grpc_compression_level level) { + GRPC_API_TRACE("grpc_compression_algorithm_for_level(level=%d)", 1, + ((int)level)); switch (level) { case GRPC_COMPRESS_LEVEL_NONE: return GRPC_COMPRESS_NONE; @@ -88,12 +101,15 @@ grpc_compression_algorithm grpc_compression_algorithm_for_level( default: /* we shouldn't be making it here */ abort(); + return GRPC_COMPRESS_NONE; } } grpc_compression_level grpc_compression_level_for_algorithm( grpc_compression_algorithm algorithm) { grpc_compression_level clevel; + GRPC_API_TRACE("grpc_compression_level_for_algorithm(algorithm=%d)", 1, + ((int)algorithm)); for (clevel = GRPC_COMPRESS_LEVEL_NONE; clevel < GRPC_COMPRESS_LEVEL_COUNT; ++clevel) { if (grpc_compression_algorithm_for_level(clevel) == algorithm) { @@ -101,4 +117,26 @@ grpc_compression_level grpc_compression_level_for_algorithm( } } abort(); + return GRPC_COMPRESS_LEVEL_NONE; +} + +void grpc_compression_options_init(grpc_compression_options *opts) { + opts->enabled_algorithms_bitset = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; + opts->default_compression_algorithm = GRPC_COMPRESS_NONE; +} + +void grpc_compression_options_enable_algorithm( + grpc_compression_options *opts, grpc_compression_algorithm algorithm) { + GPR_BITSET(&opts->enabled_algorithms_bitset, algorithm); +} + +void grpc_compression_options_disable_algorithm( + grpc_compression_options *opts, grpc_compression_algorithm algorithm) { + GPR_BITCLEAR(&opts->enabled_algorithms_bitset, algorithm); +} + +int grpc_compression_options_is_algorithm_enabled( + const grpc_compression_options *opts, + grpc_compression_algorithm algorithm) { + return GPR_BITGET(opts->enabled_algorithms_bitset, algorithm); } diff --git a/src/core/compression/message_compress.c b/src/core/compression/message_compress.c index 7856f40dd1..209c1f0ff1 100644 --- a/src/core/compression/message_compress.c +++ b/src/core/compression/message_compress.c @@ -42,26 +42,30 @@ #define OUTPUT_BLOCK_SIZE 1024 -static int zlib_body(z_stream *zs, gpr_slice_buffer *input, - gpr_slice_buffer *output, - int (*flate)(z_stream *zs, int flush)) { +static int zlib_body(z_stream* zs, gpr_slice_buffer* input, + gpr_slice_buffer* output, + int (*flate)(z_stream* zs, int flush)) { int r; int flush; size_t i; gpr_slice outbuf = gpr_slice_malloc(OUTPUT_BLOCK_SIZE); + const uInt uint_max = ~(uInt)0; - zs->avail_out = GPR_SLICE_LENGTH(outbuf); + GPR_ASSERT(GPR_SLICE_LENGTH(outbuf) <= uint_max); + zs->avail_out = (uInt)GPR_SLICE_LENGTH(outbuf); zs->next_out = GPR_SLICE_START_PTR(outbuf); flush = Z_NO_FLUSH; for (i = 0; i < input->count; i++) { if (i == input->count - 1) flush = Z_FINISH; - zs->avail_in = GPR_SLICE_LENGTH(input->slices[i]); + GPR_ASSERT(GPR_SLICE_LENGTH(input->slices[i]) <= uint_max); + zs->avail_in = (uInt)GPR_SLICE_LENGTH(input->slices[i]); zs->next_in = GPR_SLICE_START_PTR(input->slices[i]); do { if (zs->avail_out == 0) { gpr_slice_buffer_add_indexed(output, outbuf); outbuf = gpr_slice_malloc(OUTPUT_BLOCK_SIZE); - zs->avail_out = GPR_SLICE_LENGTH(outbuf); + GPR_ASSERT(GPR_SLICE_LENGTH(outbuf) <= uint_max); + zs->avail_out = (uInt)GPR_SLICE_LENGTH(outbuf); zs->next_out = GPR_SLICE_START_PTR(outbuf); } r = flate(zs, flush); @@ -87,7 +91,7 @@ error: return 0; } -static int zlib_compress(gpr_slice_buffer *input, gpr_slice_buffer *output, +static int zlib_compress(gpr_slice_buffer* input, gpr_slice_buffer* output, int gzip) { z_stream zs; int r; @@ -113,7 +117,7 @@ static int zlib_compress(gpr_slice_buffer *input, gpr_slice_buffer *output, return r; } -static int zlib_decompress(gpr_slice_buffer *input, gpr_slice_buffer *output, +static int zlib_decompress(gpr_slice_buffer* input, gpr_slice_buffer* output, int gzip) { z_stream zs; int r; @@ -138,7 +142,7 @@ static int zlib_decompress(gpr_slice_buffer *input, gpr_slice_buffer *output, return r; } -static int copy(gpr_slice_buffer *input, gpr_slice_buffer *output) { +static int copy(gpr_slice_buffer* input, gpr_slice_buffer* output) { size_t i; for (i = 0; i < input->count; i++) { gpr_slice_buffer_add(output, gpr_slice_ref(input->slices[i])); @@ -147,7 +151,7 @@ static int copy(gpr_slice_buffer *input, gpr_slice_buffer *output) { } int compress_inner(grpc_compression_algorithm algorithm, - gpr_slice_buffer *input, gpr_slice_buffer *output) { + gpr_slice_buffer* input, gpr_slice_buffer* output) { switch (algorithm) { case GRPC_COMPRESS_NONE: /* the fallback path always needs to be send uncompressed: we simply @@ -165,7 +169,7 @@ int compress_inner(grpc_compression_algorithm algorithm, } int grpc_msg_compress(grpc_compression_algorithm algorithm, - gpr_slice_buffer *input, gpr_slice_buffer *output) { + gpr_slice_buffer* input, gpr_slice_buffer* output) { if (!compress_inner(algorithm, input, output)) { copy(input, output); return 0; @@ -174,7 +178,7 @@ int grpc_msg_compress(grpc_compression_algorithm algorithm, } int grpc_msg_decompress(grpc_compression_algorithm algorithm, - gpr_slice_buffer *input, gpr_slice_buffer *output) { + gpr_slice_buffer* input, gpr_slice_buffer* output) { switch (algorithm) { case GRPC_COMPRESS_NONE: return copy(input, output); diff --git a/src/core/compression/message_compress.h b/src/core/compression/message_compress.h index b3eb8f579f..14652004b8 100644 --- a/src/core/compression/message_compress.h +++ b/src/core/compression/message_compress.h @@ -41,12 +41,12 @@ On success, appends compressed slices to output and returns 1. On failure, appends uncompressed slices to output and returns 0. */ int grpc_msg_compress(grpc_compression_algorithm algorithm, - gpr_slice_buffer *input, gpr_slice_buffer *output); + gpr_slice_buffer* input, gpr_slice_buffer* output); /* decompress 'input' to 'output' using 'algorithm'. On success, appends slices to output and returns 1. On failure, output is unchanged, and returns 0. */ int grpc_msg_decompress(grpc_compression_algorithm algorithm, - gpr_slice_buffer *input, gpr_slice_buffer *output); + gpr_slice_buffer* input, gpr_slice_buffer* output); #endif /* GRPC_INTERNAL_CORE_COMPRESSION_MESSAGE_COMPRESS_H */ diff --git a/src/core/debug/trace.c b/src/core/debug/trace.c index 1014b1f4db..3b35d81cd8 100644 --- a/src/core/debug/trace.c +++ b/src/core/debug/trace.c @@ -59,9 +59,13 @@ void grpc_register_tracer(const char *name, int *flag) { static void add(const char *beg, const char *end, char ***ss, size_t *ns) { size_t n = *ns; size_t np = n + 1; - char *s = gpr_malloc(end - beg + 1); - memcpy(s, beg, end - beg); - s[end - beg] = 0; + char *s; + size_t len; + GPR_ASSERT(end >= beg); + len = (size_t)(end - beg); + s = gpr_malloc(len + 1); + memcpy(s, beg, len); + s[len] = 0; *ss = gpr_realloc(*ss, sizeof(char **) * np); (*ss)[n] = s; *ns = np; diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c index 9012070e8e..a87f1aa87b 100644 --- a/src/core/httpcli/httpcli.c +++ b/src/core/httpcli/httpcli.c @@ -61,16 +61,22 @@ typedef struct { grpc_httpcli_context *context; grpc_pollset *pollset; grpc_iomgr_object iomgr_obj; + gpr_slice_buffer incoming; + gpr_slice_buffer outgoing; + grpc_closure on_read; + grpc_closure done_write; + grpc_closure connected; } internal_request; static grpc_httpcli_get_override g_get_override = NULL; static grpc_httpcli_post_override g_post_override = NULL; -static void plaintext_handshake(void *arg, grpc_endpoint *endpoint, - const char *host, - void (*on_done)(void *arg, +static void plaintext_handshake(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *endpoint, const char *host, + void (*on_done)(grpc_exec_ctx *exec_ctx, + void *arg, grpc_endpoint *endpoint)) { - on_done(arg, endpoint); + on_done(exec_ctx, arg, endpoint); } const grpc_httpcli_handshaker grpc_httpcli_plaintext = {"http", @@ -84,151 +90,133 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context) { grpc_pollset_set_destroy(&context->pollset_set); } -static void next_address(internal_request *req); +static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req); -static void finish(internal_request *req, int success) { - grpc_pollset_set_del_pollset(&req->context->pollset_set, req->pollset); - req->on_response(req->user_data, success ? &req->parser.r : NULL); +static void finish(grpc_exec_ctx *exec_ctx, internal_request *req, + int success) { + grpc_pollset_set_del_pollset(exec_ctx, &req->context->pollset_set, + req->pollset); + req->on_response(exec_ctx, req->user_data, success ? &req->parser.r : NULL); grpc_httpcli_parser_destroy(&req->parser); if (req->addresses != NULL) { grpc_resolved_addresses_destroy(req->addresses); } if (req->ep != NULL) { - grpc_endpoint_destroy(req->ep); + grpc_endpoint_destroy(exec_ctx, req->ep); } gpr_slice_unref(req->request_text); gpr_free(req->host); grpc_iomgr_unregister_object(&req->iomgr_obj); + gpr_slice_buffer_destroy(&req->incoming); + gpr_slice_buffer_destroy(&req->outgoing); gpr_free(req); } -static void on_read(void *user_data, gpr_slice *slices, size_t nslices, - grpc_endpoint_cb_status status) { +static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, int success); + +static void do_read(grpc_exec_ctx *exec_ctx, internal_request *req) { + grpc_endpoint_read(exec_ctx, req->ep, &req->incoming, &req->on_read); +} + +static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, int success) { internal_request *req = user_data; size_t i; - for (i = 0; i < nslices; i++) { - if (GPR_SLICE_LENGTH(slices[i])) { + for (i = 0; i < req->incoming.count; i++) { + if (GPR_SLICE_LENGTH(req->incoming.slices[i])) { req->have_read_byte = 1; - if (!grpc_httpcli_parser_parse(&req->parser, slices[i])) { - finish(req, 0); - goto done; + if (!grpc_httpcli_parser_parse(&req->parser, req->incoming.slices[i])) { + finish(exec_ctx, req, 0); + return; } } } - switch (status) { - case GRPC_ENDPOINT_CB_OK: - grpc_endpoint_notify_on_read(req->ep, on_read, req); - break; - case GRPC_ENDPOINT_CB_EOF: - case GRPC_ENDPOINT_CB_ERROR: - case GRPC_ENDPOINT_CB_SHUTDOWN: - if (!req->have_read_byte) { - next_address(req); - } else { - finish(req, grpc_httpcli_parser_eof(&req->parser)); - } - break; - } - -done: - for (i = 0; i < nslices; i++) { - gpr_slice_unref(slices[i]); + if (success) { + do_read(exec_ctx, req); + } else if (!req->have_read_byte) { + next_address(exec_ctx, req); + } else { + finish(exec_ctx, req, grpc_httpcli_parser_eof(&req->parser)); } } -static void on_written(internal_request *req) { - grpc_endpoint_notify_on_read(req->ep, on_read, req); +static void on_written(grpc_exec_ctx *exec_ctx, internal_request *req) { + do_read(exec_ctx, req); } -static void done_write(void *arg, grpc_endpoint_cb_status status) { +static void done_write(grpc_exec_ctx *exec_ctx, void *arg, int success) { internal_request *req = arg; - switch (status) { - case GRPC_ENDPOINT_CB_OK: - on_written(req); - break; - case GRPC_ENDPOINT_CB_EOF: - case GRPC_ENDPOINT_CB_SHUTDOWN: - case GRPC_ENDPOINT_CB_ERROR: - next_address(req); - break; + if (success) { + on_written(exec_ctx, req); + } else { + next_address(exec_ctx, req); } } -static void start_write(internal_request *req) { +static void start_write(grpc_exec_ctx *exec_ctx, internal_request *req) { gpr_slice_ref(req->request_text); - switch ( - grpc_endpoint_write(req->ep, &req->request_text, 1, done_write, req)) { - case GRPC_ENDPOINT_WRITE_DONE: - on_written(req); - break; - case GRPC_ENDPOINT_WRITE_PENDING: - break; - case GRPC_ENDPOINT_WRITE_ERROR: - finish(req, 0); - break; - } + gpr_slice_buffer_add(&req->outgoing, req->request_text); + grpc_endpoint_write(exec_ctx, req->ep, &req->outgoing, &req->done_write); } -static void on_handshake_done(void *arg, grpc_endpoint *ep) { +static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *ep) { internal_request *req = arg; if (!ep) { - next_address(req); + next_address(exec_ctx, req); return; } req->ep = ep; - start_write(req); + start_write(exec_ctx, req); } -static void on_connected(void *arg, grpc_endpoint *tcp) { +static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, int success) { internal_request *req = arg; - if (!tcp) { - next_address(req); + if (!req->ep) { + next_address(exec_ctx, req); return; } - req->handshaker->handshake(req, tcp, req->host, on_handshake_done); + req->handshaker->handshake(exec_ctx, req, req->ep, req->host, + on_handshake_done); } -static void next_address(internal_request *req) { +static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) { grpc_resolved_address *addr; if (req->next_address == req->addresses->naddrs) { - finish(req, 0); + finish(exec_ctx, req, 0); return; } addr = &req->addresses->addrs[req->next_address++]; - grpc_tcp_client_connect(on_connected, req, &req->context->pollset_set, - (struct sockaddr *)&addr->addr, addr->len, - req->deadline); + grpc_closure_init(&req->connected, on_connected, req); + grpc_tcp_client_connect( + exec_ctx, &req->connected, &req->ep, &req->context->pollset_set, + (struct sockaddr *)&addr->addr, addr->len, req->deadline); } -static void on_resolved(void *arg, grpc_resolved_addresses *addresses) { +static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, + grpc_resolved_addresses *addresses) { internal_request *req = arg; if (!addresses) { - finish(req, 0); + finish(exec_ctx, req, 0); return; } req->addresses = addresses; req->next_address = 0; - next_address(req); + next_address(exec_ctx, req); } -void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset, - const grpc_httpcli_request *request, - gpr_timespec deadline, - grpc_httpcli_response_cb on_response, void *user_data) { - internal_request *req; - char *name; - if (g_get_override && - g_get_override(request, deadline, on_response, user_data)) { - return; - } - req = gpr_malloc(sizeof(internal_request)); +static void internal_request_begin( + grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, + grpc_pollset *pollset, const grpc_httpcli_request *request, + gpr_timespec deadline, grpc_httpcli_response_cb on_response, + void *user_data, const char *name, gpr_slice request_text) { + internal_request *req = gpr_malloc(sizeof(internal_request)); memset(req, 0, sizeof(*req)); - req->request_text = grpc_httpcli_format_get_request(request); + req->request_text = request_text; grpc_httpcli_parser_init(&req->parser); req->on_response = on_response; req->user_data = user_data; @@ -237,47 +225,53 @@ void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset, request->handshaker ? request->handshaker : &grpc_httpcli_plaintext; req->context = context; req->pollset = pollset; - gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->path); + grpc_closure_init(&req->on_read, on_read, req); + grpc_closure_init(&req->done_write, done_write, req); + gpr_slice_buffer_init(&req->incoming); + gpr_slice_buffer_init(&req->outgoing); grpc_iomgr_register_object(&req->iomgr_obj, name); - gpr_free(name); req->host = gpr_strdup(request->host); - grpc_pollset_set_add_pollset(&req->context->pollset_set, req->pollset); + grpc_pollset_set_add_pollset(exec_ctx, &req->context->pollset_set, + req->pollset); grpc_resolve_address(request->host, req->handshaker->default_port, on_resolved, req); } -void grpc_httpcli_post(grpc_httpcli_context *context, grpc_pollset *pollset, +void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, + grpc_pollset *pollset, + const grpc_httpcli_request *request, + gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data) { + char *name; + if (g_get_override && + g_get_override(exec_ctx, request, deadline, on_response, user_data)) { + return; + } + gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->path); + internal_request_begin(exec_ctx, context, pollset, request, deadline, + on_response, user_data, name, + grpc_httpcli_format_get_request(request)); + gpr_free(name); +} + +void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, + grpc_pollset *pollset, const grpc_httpcli_request *request, const char *body_bytes, size_t body_size, gpr_timespec deadline, grpc_httpcli_response_cb on_response, void *user_data) { - internal_request *req; char *name; - if (g_post_override && g_post_override(request, body_bytes, body_size, - deadline, on_response, user_data)) { + if (g_post_override && + g_post_override(exec_ctx, request, body_bytes, body_size, deadline, + on_response, user_data)) { return; } - req = gpr_malloc(sizeof(internal_request)); - memset(req, 0, sizeof(*req)); - req->request_text = - grpc_httpcli_format_post_request(request, body_bytes, body_size); - grpc_httpcli_parser_init(&req->parser); - req->on_response = on_response; - req->user_data = user_data; - req->deadline = deadline; - req->handshaker = - request->handshaker ? request->handshaker : &grpc_httpcli_plaintext; - req->context = context; - req->pollset = pollset; - gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->path); - grpc_iomgr_register_object(&req->iomgr_obj, name); + gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->path); + internal_request_begin( + exec_ctx, context, pollset, request, deadline, on_response, user_data, + name, grpc_httpcli_format_post_request(request, body_bytes, body_size)); gpr_free(name); - req->host = gpr_strdup(request->host); - - grpc_pollset_set_add_pollset(&req->context->pollset_set, req->pollset); - grpc_resolve_address(request->host, req->handshaker->default_port, - on_resolved, req); } void grpc_httpcli_set_override(grpc_httpcli_get_override get, diff --git a/src/core/httpcli/httpcli.h b/src/core/httpcli/httpcli.h index c45966714c..6469c2f03e 100644 --- a/src/core/httpcli/httpcli.h +++ b/src/core/httpcli/httpcli.h @@ -61,8 +61,10 @@ typedef struct grpc_httpcli_context { typedef struct { const char *default_port; - void (*handshake)(void *arg, grpc_endpoint *endpoint, const char *host, - void (*on_done)(void *arg, grpc_endpoint *endpoint)); + void (*handshake)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint, + const char *host, + void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *endpoint)); } grpc_httpcli_handshaker; extern const grpc_httpcli_handshaker grpc_httpcli_plaintext; @@ -76,7 +78,7 @@ typedef struct grpc_httpcli_request { char *path; /* Additional headers: count and key/values; the following are supplied automatically and MUST NOT be set here: - Host, Connection, User-Agent */ + Host, Connection, User-Agent */ size_t hdr_count; grpc_httpcli_header *hdrs; /* handshaker to use ssl for the request */ @@ -96,7 +98,8 @@ typedef struct grpc_httpcli_response { } grpc_httpcli_response; /* Callback for grpc_httpcli_get and grpc_httpcli_post. */ -typedef void (*grpc_httpcli_response_cb)(void *user_data, +typedef void (*grpc_httpcli_response_cb)(grpc_exec_ctx *exec_ctx, + void *user_data, const grpc_httpcli_response *response); void grpc_httpcli_context_init(grpc_httpcli_context *context); @@ -112,7 +115,8 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context); 'deadline' contains a deadline for the request (or gpr_inf_future) 'on_response' is a callback to report results to (and 'user_data' is a user supplied pointer to pass to said call) */ -void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset, +void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, + grpc_pollset *pollset, const grpc_httpcli_request *request, gpr_timespec deadline, grpc_httpcli_response_cb on_response, void *user_data); @@ -132,23 +136,23 @@ void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset, 'on_response' is a callback to report results to (and 'user_data' is a user supplied pointer to pass to said call) Does not support ?var1=val1&var2=val2 in the path. */ -void grpc_httpcli_post(grpc_httpcli_context *context, grpc_pollset *pollset, +void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, + grpc_pollset *pollset, const grpc_httpcli_request *request, const char *body_bytes, size_t body_size, gpr_timespec deadline, grpc_httpcli_response_cb on_response, void *user_data); /* override functions return 1 if they handled the request, 0 otherwise */ -typedef int (*grpc_httpcli_get_override)(const grpc_httpcli_request *request, +typedef int (*grpc_httpcli_get_override)(grpc_exec_ctx *exec_ctx, + const grpc_httpcli_request *request, gpr_timespec deadline, grpc_httpcli_response_cb on_response, void *user_data); -typedef int (*grpc_httpcli_post_override)(const grpc_httpcli_request *request, - const char *body_bytes, - size_t body_size, - gpr_timespec deadline, - grpc_httpcli_response_cb on_response, - void *user_data); +typedef int (*grpc_httpcli_post_override)( + grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request, + const char *body_bytes, size_t body_size, gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data); void grpc_httpcli_set_override(grpc_httpcli_get_override get, grpc_httpcli_post_override post); diff --git a/src/core/httpcli/httpcli_security_connector.c b/src/core/httpcli/httpcli_security_connector.c index 7887f9d530..fc6699c918 100644 --- a/src/core/httpcli/httpcli_security_connector.c +++ b/src/core/httpcli/httpcli_security_connector.c @@ -35,7 +35,7 @@ #include <string.h> -#include "src/core/security/secure_transport_setup.h" +#include "src/core/security/handshake.h" #include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -58,20 +58,29 @@ static void httpcli_ssl_destroy(grpc_security_connector *sc) { gpr_free(sc); } -static grpc_security_status httpcli_ssl_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { +static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { grpc_httpcli_ssl_channel_security_connector *c = (grpc_httpcli_ssl_channel_security_connector *)sc; tsi_result result = TSI_OK; - if (c->handshaker_factory == NULL) return GRPC_SECURITY_ERROR; + tsi_handshaker *handshaker; + if (c->handshaker_factory == NULL) { + cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + return; + } result = tsi_ssl_handshaker_factory_create_handshaker( - c->handshaker_factory, c->secure_peer_name, handshaker); + c->handshaker_factory, c->secure_peer_name, &handshaker); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", tsi_result_to_string(result)); - return GRPC_SECURITY_ERROR; + cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + } else { + grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, + user_data); } - return GRPC_SECURITY_OK; } static grpc_security_status httpcli_ssl_check_peer(grpc_security_connector *sc, @@ -94,7 +103,7 @@ static grpc_security_status httpcli_ssl_check_peer(grpc_security_connector *sc, } static grpc_security_connector_vtable httpcli_ssl_vtable = { - httpcli_ssl_destroy, httpcli_ssl_create_handshaker, httpcli_ssl_check_peer}; + httpcli_ssl_destroy, httpcli_ssl_do_handshake, httpcli_ssl_check_peer}; static grpc_security_status httpcli_ssl_channel_security_connector_create( const unsigned char *pem_root_certs, size_t pem_root_certs_size, @@ -134,33 +143,35 @@ static grpc_security_status httpcli_ssl_channel_security_connector_create( /* handshaker */ typedef struct { - void (*func)(void *arg, grpc_endpoint *endpoint); + void (*func)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint); void *arg; } on_done_closure; -static void on_secure_transport_setup_done(void *rp, +static void on_secure_transport_setup_done(grpc_exec_ctx *exec_ctx, void *rp, grpc_security_status status, grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint) { on_done_closure *c = rp; if (status != GRPC_SECURITY_OK) { gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status); - c->func(c->arg, NULL); + c->func(exec_ctx, c->arg, NULL); } else { - c->func(c->arg, secure_endpoint); + c->func(exec_ctx, c->arg, secure_endpoint); } gpr_free(c); } -static void ssl_handshake(void *arg, grpc_endpoint *tcp, const char *host, - void (*on_done)(void *arg, grpc_endpoint *endpoint)) { +static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *tcp, const char *host, + void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *endpoint)) { grpc_channel_security_connector *sc = NULL; const unsigned char *pem_root_certs = NULL; on_done_closure *c = gpr_malloc(sizeof(*c)); size_t pem_root_certs_size = grpc_get_default_ssl_roots(&pem_root_certs); if (pem_root_certs == NULL || pem_root_certs_size == 0) { gpr_log(GPR_ERROR, "Could not get default pem root certs."); - on_done(arg, NULL); + on_done(exec_ctx, arg, NULL); gpr_free(c); return; } @@ -169,8 +180,8 @@ static void ssl_handshake(void *arg, grpc_endpoint *tcp, const char *host, GPR_ASSERT(httpcli_ssl_channel_security_connector_create( pem_root_certs, pem_root_certs_size, host, &sc) == GRPC_SECURITY_OK); - grpc_setup_secure_transport(&sc->base, tcp, on_secure_transport_setup_done, - c); + grpc_security_connector_do_handshake(exec_ctx, &sc->base, tcp, + on_secure_transport_setup_done, c); GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli"); } diff --git a/src/core/httpcli/parser.c b/src/core/httpcli/parser.c index 7b2a62060c..046770c094 100644 --- a/src/core/httpcli/parser.c +++ b/src/core/httpcli/parser.c @@ -96,13 +96,15 @@ static int add_header(grpc_httpcli_parser *parser) { gpr_log(GPR_ERROR, "Didn't find ':' in header string"); goto error; } - hdr.key = buf2str(beg, cur - beg); + GPR_ASSERT(cur >= beg); + hdr.key = buf2str(beg, (size_t)(cur - beg)); cur++; /* skip : */ while (cur != end && (*cur == ' ' || *cur == '\t')) { cur++; } - hdr.value = buf2str(cur, end - cur - 2); + GPR_ASSERT(end - cur >= 2); + hdr.value = buf2str(cur, (size_t)(end - cur) - 2); if (parser->r.hdr_count == parser->hdr_capacity) { parser->hdr_capacity = @@ -137,8 +139,7 @@ static int finish_line(grpc_httpcli_parser *parser) { } break; case GRPC_HTTPCLI_BODY: - gpr_log(GPR_ERROR, "should never reach here"); - abort(); + GPR_UNREACHABLE_CODE(return 0); } parser->cur_line_length = 0; @@ -163,22 +164,18 @@ static int addbyte(grpc_httpcli_parser *parser, gpr_uint8 byte) { } else { return 1; } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); + GPR_UNREACHABLE_CODE(return 0); case GRPC_HTTPCLI_BODY: if (parser->r.body_length == parser->body_capacity) { parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2); parser->r.body = gpr_realloc((void *)parser->r.body, parser->body_capacity); } - ((char *)parser->r.body)[parser->r.body_length] = byte; + parser->r.body[parser->r.body_length] = (char)byte; parser->r.body_length++; return 1; } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - - return 0; + GPR_UNREACHABLE_CODE(return 0); } void grpc_httpcli_parser_init(grpc_httpcli_parser *parser) { diff --git a/src/core/httpcli/parser.h b/src/core/httpcli/parser.h index 3fbb4c7479..40b28f0ee8 100644 --- a/src/core/httpcli/parser.h +++ b/src/core/httpcli/parser.h @@ -55,10 +55,10 @@ typedef struct { size_t cur_line_length; } grpc_httpcli_parser; -void grpc_httpcli_parser_init(grpc_httpcli_parser *parser); -void grpc_httpcli_parser_destroy(grpc_httpcli_parser *parser); +void grpc_httpcli_parser_init(grpc_httpcli_parser* parser); +void grpc_httpcli_parser_destroy(grpc_httpcli_parser* parser); -int grpc_httpcli_parser_parse(grpc_httpcli_parser *parser, gpr_slice slice); -int grpc_httpcli_parser_eof(grpc_httpcli_parser *parser); +int grpc_httpcli_parser_parse(grpc_httpcli_parser* parser, gpr_slice slice); +int grpc_httpcli_parser_eof(grpc_httpcli_parser* parser); #endif /* GRPC_INTERNAL_CORE_HTTPCLI_PARSER_H */ diff --git a/src/core/iomgr/closure.c b/src/core/iomgr/closure.c new file mode 100644 index 0000000000..b4f1817de4 --- /dev/null +++ b/src/core/iomgr/closure.c @@ -0,0 +1,105 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/iomgr/closure.h" + +#include <grpc/support/alloc.h> + +void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb, + void *cb_arg) { + closure->cb = cb; + closure->cb_arg = cb_arg; + closure->next = NULL; +} + +void grpc_closure_list_add(grpc_closure_list *closure_list, + grpc_closure *closure, int success) { + if (closure == NULL) return; + closure->next = NULL; + closure->success = success; + if (closure_list->head == NULL) { + closure_list->head = closure; + } else { + closure_list->tail->next = closure; + } + closure_list->tail = closure; +} + +int grpc_closure_list_empty(grpc_closure_list closure_list) { + return closure_list.head == NULL; +} + +void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst) { + if (src->head == NULL) { + return; + } + if (dst->head == NULL) { + *dst = *src; + } else { + dst->tail->next = src->head; + dst->tail = src->tail; + } + src->head = src->tail = NULL; +} + +grpc_closure *grpc_closure_list_pop(grpc_closure_list *list) { + grpc_closure *head; + if (list->head == NULL) { + return NULL; + } + head = list->head; + list->head = list->head->next; + return head; +} + +typedef struct { + grpc_iomgr_cb_func cb; + void *cb_arg; + grpc_closure wrapper; +} wrapped_closure; + +static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg, int success) { + wrapped_closure *wc = arg; + grpc_iomgr_cb_func cb = wc->cb; + void *cb_arg = wc->cb_arg; + gpr_free(wc); + cb(exec_ctx, cb_arg, success); +} + +grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg) { + wrapped_closure *wc = gpr_malloc(sizeof(*wc)); + wc->cb = cb; + wc->cb_arg = cb_arg; + grpc_closure_init(&wc->wrapper, closure_wrapper, wc); + return &wc->wrapper; +} diff --git a/src/core/iomgr/closure.h b/src/core/iomgr/closure.h new file mode 100644 index 0000000000..7a9f7ccad0 --- /dev/null +++ b/src/core/iomgr/closure.h @@ -0,0 +1,100 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_IOMGR_CLOSURE_H +#define GRPC_INTERNAL_CORE_IOMGR_CLOSURE_H + +#include <stddef.h> + +struct grpc_closure; +typedef struct grpc_closure grpc_closure; + +/* forward declaration for exec_ctx.h */ +struct grpc_exec_ctx; +typedef struct grpc_exec_ctx grpc_exec_ctx; + +typedef struct grpc_closure_list { + grpc_closure *head; + grpc_closure *tail; +} grpc_closure_list; + +/** gRPC Callback definition. + * + * \param arg Arbitrary input. + * \param success An indication on the state of the iomgr. On false, cleanup + * actions should be taken (eg, shutdown). */ +typedef void (*grpc_iomgr_cb_func)(grpc_exec_ctx *exec_ctx, void *arg, + int success); + +/** A closure over a grpc_iomgr_cb_func. */ +struct grpc_closure { + /** Bound callback. */ + grpc_iomgr_cb_func cb; + + /** Arguments to be passed to "cb". */ + void *cb_arg; + + /** Internal. A boolean indication to "cb" on the state of the iomgr. + * For instance, closures created during a shutdown would have this field set + * to false. */ + int success; + + /**< Internal. Do not touch */ + struct grpc_closure *next; +}; + +/** Initializes \a closure with \a cb and \a cb_arg. */ +void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb, + void *cb_arg); + +/* Create a heap allocated closure: try to avoid except for very rare events */ +grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg); + +#define GRPC_CLOSURE_LIST_INIT \ + { NULL, NULL } + +/** add \a closure to the end of \a list and set \a closure's success to \a + * success */ +void grpc_closure_list_add(grpc_closure_list *list, grpc_closure *closure, + int success); + +/** append all closures from \a src to \a dst and empty \a src. */ +void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst); + +/** pop (return and remove) the head closure from \a list. */ +grpc_closure *grpc_closure_list_pop(grpc_closure_list *list); + +/** return whether \a list is empty. */ +int grpc_closure_list_empty(grpc_closure_list list); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_CLOSURE_H */ diff --git a/src/core/iomgr/endpoint.c b/src/core/iomgr/endpoint.c index 8ee14bce9b..bd64707669 100644 --- a/src/core/iomgr/endpoint.c +++ b/src/core/iomgr/endpoint.c @@ -33,32 +33,35 @@ #include "src/core/iomgr/endpoint.h" -void grpc_endpoint_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, - void *user_data) { - ep->vtable->notify_on_read(ep, cb, user_data); +void grpc_endpoint_read(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, + gpr_slice_buffer* slices, grpc_closure* cb) { + ep->vtable->read(exec_ctx, ep, slices, cb); } -grpc_endpoint_write_status grpc_endpoint_write(grpc_endpoint *ep, - gpr_slice *slices, - size_t nslices, - grpc_endpoint_write_cb cb, - void *user_data) { - return ep->vtable->write(ep, slices, nslices, cb, user_data); +void grpc_endpoint_write(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, + gpr_slice_buffer* slices, grpc_closure* cb) { + ep->vtable->write(exec_ctx, ep, slices, cb); } -void grpc_endpoint_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) { - ep->vtable->add_to_pollset(ep, pollset); +void grpc_endpoint_add_to_pollset(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, + grpc_pollset* pollset) { + ep->vtable->add_to_pollset(exec_ctx, ep, pollset); } -void grpc_endpoint_add_to_pollset_set(grpc_endpoint *ep, - grpc_pollset_set *pollset_set) { - ep->vtable->add_to_pollset_set(ep, pollset_set); +void grpc_endpoint_add_to_pollset_set(grpc_exec_ctx* exec_ctx, + grpc_endpoint* ep, + grpc_pollset_set* pollset_set) { + ep->vtable->add_to_pollset_set(exec_ctx, ep, pollset_set); } -void grpc_endpoint_shutdown(grpc_endpoint *ep) { ep->vtable->shutdown(ep); } +void grpc_endpoint_shutdown(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep) { + ep->vtable->shutdown(exec_ctx, ep); +} -void grpc_endpoint_destroy(grpc_endpoint *ep) { ep->vtable->destroy(ep); } +void grpc_endpoint_destroy(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep) { + ep->vtable->destroy(exec_ctx, ep); +} -char *grpc_endpoint_get_peer(grpc_endpoint *ep) { +char* grpc_endpoint_get_peer(grpc_endpoint* ep) { return ep->vtable->get_peer(ep); } diff --git a/src/core/iomgr/endpoint.h b/src/core/iomgr/endpoint.h index ea92a500e8..cbdc947abb 100644 --- a/src/core/iomgr/endpoint.h +++ b/src/core/iomgr/endpoint.h @@ -37,6 +37,7 @@ #include "src/core/iomgr/pollset.h" #include "src/core/iomgr/pollset_set.h" #include <grpc/support/slice.h> +#include <grpc/support/slice_buffer.h> #include <grpc/support/time.h> /* An endpoint caps a streaming channel between two communicating processes. @@ -45,65 +46,53 @@ typedef struct grpc_endpoint grpc_endpoint; typedef struct grpc_endpoint_vtable grpc_endpoint_vtable; -typedef enum grpc_endpoint_cb_status { - GRPC_ENDPOINT_CB_OK = 0, /* Call completed successfully */ - GRPC_ENDPOINT_CB_EOF, /* Call completed successfully, end of file reached */ - GRPC_ENDPOINT_CB_SHUTDOWN, /* Call interrupted by shutdown */ - GRPC_ENDPOINT_CB_ERROR /* Call interrupted by socket error */ -} grpc_endpoint_cb_status; - -typedef enum grpc_endpoint_write_status { - GRPC_ENDPOINT_WRITE_DONE, /* completed immediately, cb won't be called */ - GRPC_ENDPOINT_WRITE_PENDING, /* cb will be called when completed */ - GRPC_ENDPOINT_WRITE_ERROR /* write errored out, cb won't be called */ -} grpc_endpoint_write_status; - -typedef void (*grpc_endpoint_read_cb)(void *user_data, gpr_slice *slices, - size_t nslices, - grpc_endpoint_cb_status error); -typedef void (*grpc_endpoint_write_cb)(void *user_data, - grpc_endpoint_cb_status error); - struct grpc_endpoint_vtable { - void (*notify_on_read)(grpc_endpoint *ep, grpc_endpoint_read_cb cb, - void *user_data); - grpc_endpoint_write_status (*write)(grpc_endpoint *ep, gpr_slice *slices, - size_t nslices, grpc_endpoint_write_cb cb, - void *user_data); - void (*add_to_pollset)(grpc_endpoint *ep, grpc_pollset *pollset); - void (*add_to_pollset_set)(grpc_endpoint *ep, grpc_pollset_set *pollset); - void (*shutdown)(grpc_endpoint *ep); - void (*destroy)(grpc_endpoint *ep); + void (*read)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *slices, grpc_closure *cb); + void (*write)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *slices, grpc_closure *cb); + void (*add_to_pollset)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *pollset); + void (*add_to_pollset_set)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset_set *pollset); + void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep); + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep); char *(*get_peer)(grpc_endpoint *ep); }; -/* When data is available on the connection, calls the callback with slices. */ -void grpc_endpoint_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, - void *user_data); +/* When data is available on the connection, calls the callback with slices. + Callback success indicates that the endpoint can accept more reads, failure + indicates the endpoint is closed. + Valid slices may be placed into \a slices even on callback success == 0. */ +void grpc_endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *slices, grpc_closure *cb); char *grpc_endpoint_get_peer(grpc_endpoint *ep); /* Write slices out to the socket. If the connection is ready for more data after the end of the call, it - returns GRPC_ENDPOINT_WRITE_DONE. - Otherwise it returns GRPC_ENDPOINT_WRITE_PENDING and calls cb when the - connection is ready for more data. */ -grpc_endpoint_write_status grpc_endpoint_write(grpc_endpoint *ep, - gpr_slice *slices, - size_t nslices, - grpc_endpoint_write_cb cb, - void *user_data); + returns GRPC_ENDPOINT_DONE. + Otherwise it returns GRPC_ENDPOINT_PENDING and calls cb when the + connection is ready for more data. + \a slices may be mutated at will by the endpoint until cb is called. + No guarantee is made to the content of slices after a write EXCEPT that + it is a valid slice buffer. + */ +void grpc_endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *slices, grpc_closure *cb); /* Causes any pending read/write callbacks to run immediately with - GRPC_ENDPOINT_CB_SHUTDOWN status */ -void grpc_endpoint_shutdown(grpc_endpoint *ep); -void grpc_endpoint_destroy(grpc_endpoint *ep); + success==0 */ +void grpc_endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep); +void grpc_endpoint_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep); /* Add an endpoint to a pollset, so that when the pollset is polled, events from this endpoint are considered */ -void grpc_endpoint_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset); -void grpc_endpoint_add_to_pollset_set(grpc_endpoint *ep, +void grpc_endpoint_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *pollset); +void grpc_endpoint_add_to_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_endpoint *ep, grpc_pollset_set *pollset_set); struct grpc_endpoint { diff --git a/src/core/iomgr/exec_ctx.c b/src/core/iomgr/exec_ctx.c new file mode 100644 index 0000000000..410b34c521 --- /dev/null +++ b/src/core/iomgr/exec_ctx.c @@ -0,0 +1,71 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/iomgr/exec_ctx.h" + +#include <grpc/support/log.h> + +#include "src/core/profiling/timers.h" + +int grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) { + int did_something = 0; + GPR_TIMER_BEGIN("grpc_exec_ctx_flush", 0); + while (!grpc_closure_list_empty(exec_ctx->closure_list)) { + grpc_closure *c = exec_ctx->closure_list.head; + exec_ctx->closure_list.head = exec_ctx->closure_list.tail = NULL; + while (c != NULL) { + grpc_closure *next = c->next; + did_something++; + GPR_TIMER_BEGIN("grpc_exec_ctx_flush.cb", 0); + c->cb(exec_ctx, c->cb_arg, c->success); + GPR_TIMER_END("grpc_exec_ctx_flush.cb", 0); + c = next; + } + } + GPR_TIMER_END("grpc_exec_ctx_flush", 0); + return did_something; +} + +void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx) { + grpc_exec_ctx_flush(exec_ctx); +} + +void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + int success) { + grpc_closure_list_add(&exec_ctx->closure_list, closure, success); +} + +void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx, + grpc_closure_list *list) { + grpc_closure_list_move(list, &exec_ctx->closure_list); +} diff --git a/src/core/iomgr/exec_ctx.h b/src/core/iomgr/exec_ctx.h new file mode 100644 index 0000000000..43df488094 --- /dev/null +++ b/src/core/iomgr/exec_ctx.h @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_IOMGR_EXEC_CTX_H +#define GRPC_INTERNAL_CORE_IOMGR_EXEC_CTX_H + +#include "src/core/iomgr/closure.h" + +/** Execution context. + * A bag of data that collects information along a callstack. + * Generally created at public API entry points, and passed down as + * pointer to child functions that manipulate it. + * + * Specific responsibilities (this may grow in the future): + * - track a list of work that needs to be delayed until the top of the + * call stack (this provides a convenient mechanism to run callbacks + * without worrying about locking issues) + * + * CONVENTIONS: + * Instance of this must ALWAYS be constructed on the stack, never + * heap allocated. Instances and pointers to them must always be called + * exec_ctx. Instances are always passed as the first argument + * to a function that takes it, and always as a pointer (grpc_exec_ctx + * is never copied). + */ +struct grpc_exec_ctx { + grpc_closure_list closure_list; +}; + +#define GRPC_EXEC_CTX_INIT \ + { GRPC_CLOSURE_LIST_INIT } + +/** Flush any work that has been enqueued onto this grpc_exec_ctx. + * Caller must guarantee that no interfering locks are held. + * Returns 1 if work was performed, 0 otherwise. */ +int grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx); +/** Finish any pending work for a grpc_exec_ctx. Must be called before + * the instance is destroyed, or work may be lost. */ +void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx); +/** Add a closure to be executed at the next flush/finish point */ +void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + int success); +/** Add a list of closures to be executed at the next flush/finish point. + * Leaves \a list empty. */ +void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx, + grpc_closure_list *list); + +#endif diff --git a/src/core/iomgr/executor.c b/src/core/iomgr/executor.c new file mode 100644 index 0000000000..457e5cdbac --- /dev/null +++ b/src/core/iomgr/executor.c @@ -0,0 +1,148 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/iomgr/executor.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd.h> +#include "src/core/iomgr/exec_ctx.h" + +typedef struct grpc_executor_data { + int busy; /**< is the thread currently running? */ + int shutting_down; /**< has \a grpc_shutdown() been invoked? */ + int pending_join; /**< has the thread finished but not been joined? */ + grpc_closure_list closures; /**< collection of pending work */ + gpr_thd_id tid; /**< thread id of the thread, only valid if \a busy or \a + pending_join are true */ + gpr_thd_options options; + gpr_mu mu; +} grpc_executor; + +static grpc_executor g_executor; + +void grpc_executor_init() { + memset(&g_executor, 0, sizeof(grpc_executor)); + gpr_mu_init(&g_executor.mu); + g_executor.options = gpr_thd_options_default(); + gpr_thd_options_set_joinable(&g_executor.options); +} + +/* thread body */ +static void closure_exec_thread_func(void *ignored) { + grpc_closure *closure; + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (1) { + gpr_mu_lock(&g_executor.mu); + if (g_executor.shutting_down != 0) { + gpr_mu_unlock(&g_executor.mu); + break; + } + closure = grpc_closure_list_pop(&g_executor.closures); + if (closure == NULL) { + /* no more work, time to die */ + GPR_ASSERT(g_executor.busy == 1); + g_executor.busy = 0; + gpr_mu_unlock(&g_executor.mu); + break; + } + gpr_mu_unlock(&g_executor.mu); + closure->cb(&exec_ctx, closure->cb_arg, closure->success); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); +} + +/* Spawn the thread if new work has arrived a no thread is up */ +static void maybe_spawn_locked() { + if (grpc_closure_list_empty(g_executor.closures) == 1) { + return; + } + if (g_executor.shutting_down == 1) { + return; + } + + if (g_executor.busy != 0) { + /* Thread still working. New work will be picked up by already running + * thread. Not spawning anything. */ + return; + } else if (g_executor.pending_join != 0) { + /* Pickup the remains of the previous incarnations of the thread. */ + gpr_thd_join(g_executor.tid); + g_executor.pending_join = 0; + } + + /* All previous instances of the thread should have been joined at this point. + * Spawn time! */ + g_executor.busy = 1; + gpr_thd_new(&g_executor.tid, closure_exec_thread_func, NULL, + &g_executor.options); + g_executor.pending_join = 1; +} + +void grpc_executor_enqueue(grpc_closure *closure, int success) { + gpr_mu_lock(&g_executor.mu); + if (g_executor.shutting_down == 0) { + grpc_closure_list_add(&g_executor.closures, closure, success); + maybe_spawn_locked(); + } + gpr_mu_unlock(&g_executor.mu); +} + +void grpc_executor_shutdown() { + int pending_join; + grpc_closure *closure; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + gpr_mu_lock(&g_executor.mu); + pending_join = g_executor.pending_join; + g_executor.shutting_down = 1; + gpr_mu_unlock(&g_executor.mu); + /* we can release the lock at this point despite the access to the closure + * list below because we aren't accepting new work */ + + /* Execute pending callbacks, some may be performing cleanups */ + while ((closure = grpc_closure_list_pop(&g_executor.closures)) != NULL) { + closure->cb(&exec_ctx, closure->cb_arg, closure->success); + } + grpc_exec_ctx_finish(&exec_ctx); + GPR_ASSERT(grpc_closure_list_empty(g_executor.closures)); + if (pending_join) { + gpr_thd_join(g_executor.tid); + } + gpr_mu_destroy(&g_executor.mu); +} diff --git a/src/core/iomgr/executor.h b/src/core/iomgr/executor.h new file mode 100644 index 0000000000..6da446ae9c --- /dev/null +++ b/src/core/iomgr/executor.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_IOMGR_EXECUTOR_H +#define GRPC_INTERNAL_CORE_IOMGR_EXECUTOR_H + +#include "src/core/iomgr/closure.h" + +/** Initialize the global executor. + * + * This mechanism is meant to outsource work (grpc_closure instances) to a + * thread, for those cases where blocking isn't an option but there isn't a + * non-blocking solution available. */ +void grpc_executor_init(); + +/** Enqueue \a closure for its eventual execution of \a f(arg) on a separate + * thread */ +void grpc_executor_enqueue(grpc_closure *closure, int success); + +/** Shutdown the executor, running all pending work as part of the call */ +void grpc_executor_shutdown(); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_EXECUTOR_H */ diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c index 2d08a77a70..7ff80e6cf8 100644 --- a/src/core/iomgr/fd_posix.c +++ b/src/core/iomgr/fd_posix.c @@ -45,10 +45,8 @@ #include <grpc/support/log.h> #include <grpc/support/useful.h> -enum descriptor_state { - NOT_READY = 0, - READY = 1 -}; /* or a pointer to a closure to call */ +#define CLOSURE_NOT_READY ((grpc_closure *)0) +#define CLOSURE_READY ((grpc_closure *)1) /* We need to keep a freelist not because of any concerns of malloc performance * but instead so that implementations with multiple threads in (for example) @@ -88,14 +86,13 @@ static grpc_fd *alloc_fd(int fd) { gpr_mu_unlock(&fd_freelist_mu); if (r == NULL) { r = gpr_malloc(sizeof(grpc_fd)); - gpr_mu_init(&r->set_state_mu); - gpr_mu_init(&r->watcher_mu); + gpr_mu_init(&r->mu); } gpr_atm_rel_store(&r->refst, 1); - gpr_atm_rel_store(&r->readst, NOT_READY); - gpr_atm_rel_store(&r->writest, NOT_READY); - gpr_atm_rel_store(&r->shutdown, 0); + r->shutdown = 0; + r->read_closure = CLOSURE_NOT_READY; + r->write_closure = CLOSURE_NOT_READY; r->fd = fd; r->inactive_watcher_root.next = r->inactive_watcher_root.prev = &r->inactive_watcher_root; @@ -107,8 +104,7 @@ static grpc_fd *alloc_fd(int fd) { } static void destroy(grpc_fd *fd) { - gpr_mu_destroy(&fd->set_state_mu); - gpr_mu_destroy(&fd->watcher_mu); + gpr_mu_destroy(&fd->mu); gpr_free(fd); } @@ -150,6 +146,8 @@ static void unref_by(grpc_fd *fd, int n) { void grpc_fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } void grpc_fd_global_shutdown(void) { + gpr_mu_lock(&fd_freelist_mu); + gpr_mu_unlock(&fd_freelist_mu); while (fd_freelist != NULL) { grpc_fd *fd = fd_freelist; fd_freelist = fd_freelist->freelist_next; @@ -161,6 +159,9 @@ void grpc_fd_global_shutdown(void) { grpc_fd *grpc_fd_create(int fd, const char *name) { grpc_fd *r = alloc_fd(fd); grpc_iomgr_register_object(&r->iomgr_object, name); +#ifdef GRPC_FD_REF_COUNT_DEBUG + gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, r, name); +#endif return r; } @@ -168,39 +169,35 @@ int grpc_fd_is_orphaned(grpc_fd *fd) { return (gpr_atm_acq_load(&fd->refst) & 1) == 0; } -static void pollset_kick_locked(grpc_pollset *pollset) { - gpr_mu_lock(GRPC_POLLSET_MU(pollset)); - grpc_pollset_kick(pollset, NULL); - gpr_mu_unlock(GRPC_POLLSET_MU(pollset)); +static void pollset_kick_locked(grpc_fd_watcher *watcher) { + gpr_mu_lock(GRPC_POLLSET_MU(watcher->pollset)); + GPR_ASSERT(watcher->worker); + grpc_pollset_kick_ext(watcher->pollset, watcher->worker, + GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP); + gpr_mu_unlock(GRPC_POLLSET_MU(watcher->pollset)); } static void maybe_wake_one_watcher_locked(grpc_fd *fd) { if (fd->inactive_watcher_root.next != &fd->inactive_watcher_root) { - pollset_kick_locked(fd->inactive_watcher_root.next->pollset); + pollset_kick_locked(fd->inactive_watcher_root.next); } else if (fd->read_watcher) { - pollset_kick_locked(fd->read_watcher->pollset); + pollset_kick_locked(fd->read_watcher); } else if (fd->write_watcher) { - pollset_kick_locked(fd->write_watcher->pollset); + pollset_kick_locked(fd->write_watcher); } } -static void maybe_wake_one_watcher(grpc_fd *fd) { - gpr_mu_lock(&fd->watcher_mu); - maybe_wake_one_watcher_locked(fd); - gpr_mu_unlock(&fd->watcher_mu); -} - static void wake_all_watchers_locked(grpc_fd *fd) { grpc_fd_watcher *watcher; for (watcher = fd->inactive_watcher_root.next; watcher != &fd->inactive_watcher_root; watcher = watcher->next) { - pollset_kick_locked(watcher->pollset); + pollset_kick_locked(watcher); } if (fd->read_watcher) { - pollset_kick_locked(fd->read_watcher->pollset); + pollset_kick_locked(fd->read_watcher); } if (fd->write_watcher && fd->write_watcher != fd->read_watcher) { - pollset_kick_locked(fd->write_watcher->pollset); + pollset_kick_locked(fd->write_watcher); } } @@ -209,23 +206,20 @@ static int has_watchers(grpc_fd *fd) { fd->inactive_watcher_root.next != &fd->inactive_watcher_root; } -void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_closure *on_done, +void grpc_fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done, const char *reason) { fd->on_done_closure = on_done; shutdown(fd->fd, SHUT_RDWR); + gpr_mu_lock(&fd->mu); REF_BY(fd, 1, reason); /* remove active status, but keep referenced */ - gpr_mu_lock(&fd->watcher_mu); if (!has_watchers(fd)) { - GPR_ASSERT(!fd->closed); fd->closed = 1; close(fd->fd); - if (fd->on_done_closure) { - grpc_iomgr_add_callback(fd->on_done_closure); - } + grpc_exec_ctx_enqueue(exec_ctx, fd->on_done_closure, 1); } else { wake_all_watchers_locked(fd); } - gpr_mu_unlock(&fd->watcher_mu); + gpr_mu_unlock(&fd->mu); UNREF_BY(fd, 2, reason); /* drop the reference */ } @@ -245,169 +239,127 @@ void grpc_fd_ref(grpc_fd *fd) { ref_by(fd, 2); } void grpc_fd_unref(grpc_fd *fd) { unref_by(fd, 2); } #endif -static void process_callback(grpc_iomgr_closure *closure, int success, - int allow_synchronous_callback) { - if (allow_synchronous_callback) { - closure->cb(closure->cb_arg, success); +static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure **st, grpc_closure *closure) { + if (*st == CLOSURE_NOT_READY) { + /* not ready ==> switch to a waiting state by setting the closure */ + *st = closure; + } else if (*st == CLOSURE_READY) { + /* already ready ==> queue the closure to run immediately */ + *st = CLOSURE_NOT_READY; + grpc_exec_ctx_enqueue(exec_ctx, closure, !fd->shutdown); + maybe_wake_one_watcher_locked(fd); } else { - grpc_iomgr_add_delayed_callback(closure, success); - } -} - -static void process_callbacks(grpc_iomgr_closure *callbacks, size_t n, - int success, int allow_synchronous_callback) { - size_t i; - for (i = 0; i < n; i++) { - process_callback(callbacks + i, success, allow_synchronous_callback); - } -} - -static void notify_on(grpc_fd *fd, gpr_atm *st, grpc_iomgr_closure *closure, - int allow_synchronous_callback) { - switch (gpr_atm_acq_load(st)) { - case NOT_READY: - /* There is no race if the descriptor is already ready, so we skip - the interlocked op in that case. As long as the app doesn't - try to set the same upcall twice (which it shouldn't) then - oldval should never be anything other than READY or NOT_READY. We - don't - check for user error on the fast path. */ - if (gpr_atm_rel_cas(st, NOT_READY, (gpr_intptr)closure)) { - /* swap was successful -- the closure will run after the next - set_ready call. NOTE: we don't have an ABA problem here, - since we should never have concurrent calls to the same - notify_on function. */ - maybe_wake_one_watcher(fd); - return; - } - /* swap was unsuccessful due to an intervening set_ready call. - Fall through to the READY code below */ - case READY: - GPR_ASSERT(gpr_atm_no_barrier_load(st) == READY); - gpr_atm_rel_store(st, NOT_READY); - process_callback(closure, !gpr_atm_acq_load(&fd->shutdown), - allow_synchronous_callback); - return; - default: /* WAITING */ - /* upcallptr was set to a different closure. This is an error! */ - gpr_log(GPR_ERROR, - "User called a notify_on function with a previous callback still " - "pending"); - abort(); + /* upcallptr was set to a different closure. This is an error! */ + gpr_log(GPR_ERROR, + "User called a notify_on function with a previous callback still " + "pending"); + abort(); } - gpr_log(GPR_ERROR, "Corrupt memory in &st->state"); - abort(); } -static void set_ready_locked(gpr_atm *st, grpc_iomgr_closure **callbacks, - size_t *ncallbacks) { - gpr_intptr state = gpr_atm_acq_load(st); - - switch (state) { - case READY: - /* duplicate ready, ignore */ - return; - case NOT_READY: - if (gpr_atm_rel_cas(st, NOT_READY, READY)) { - /* swap was successful -- the closure will run after the next - notify_on call. */ - return; - } - /* swap was unsuccessful due to an intervening set_ready call. - Fall through to the WAITING code below */ - state = gpr_atm_acq_load(st); - default: /* waiting */ - GPR_ASSERT(gpr_atm_no_barrier_load(st) != READY && - gpr_atm_no_barrier_load(st) != NOT_READY); - callbacks[(*ncallbacks)++] = (grpc_iomgr_closure *)state; - gpr_atm_rel_store(st, NOT_READY); - return; +/* returns 1 if state becomes not ready */ +static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure **st) { + if (*st == CLOSURE_READY) { + /* duplicate ready ==> ignore */ + return 0; + } else if (*st == CLOSURE_NOT_READY) { + /* not ready, and not waiting ==> flag ready */ + *st = CLOSURE_READY; + return 0; + } else { + /* waiting ==> queue closure */ + grpc_exec_ctx_enqueue(exec_ctx, *st, !fd->shutdown); + *st = CLOSURE_NOT_READY; + return 1; } } -static void set_ready(grpc_fd *fd, gpr_atm *st, - int allow_synchronous_callback) { +static void set_ready(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure **st) { /* only one set_ready can be active at once (but there may be a racing notify_on) */ - int success; - grpc_iomgr_closure *closure; - size_t ncb = 0; - - gpr_mu_lock(&fd->set_state_mu); - set_ready_locked(st, &closure, &ncb); - gpr_mu_unlock(&fd->set_state_mu); - success = !gpr_atm_acq_load(&fd->shutdown); - GPR_ASSERT(ncb <= 1); - if (ncb > 0) { - process_callbacks(closure, ncb, success, allow_synchronous_callback); - } + gpr_mu_lock(&fd->mu); + set_ready_locked(exec_ctx, fd, st); + gpr_mu_unlock(&fd->mu); } -void grpc_fd_shutdown(grpc_fd *fd) { - size_t ncb = 0; - gpr_mu_lock(&fd->set_state_mu); - GPR_ASSERT(!gpr_atm_no_barrier_load(&fd->shutdown)); - gpr_atm_rel_store(&fd->shutdown, 1); - set_ready_locked(&fd->readst, &fd->shutdown_closures[0], &ncb); - set_ready_locked(&fd->writest, &fd->shutdown_closures[0], &ncb); - gpr_mu_unlock(&fd->set_state_mu); - GPR_ASSERT(ncb <= 2); - process_callbacks(fd->shutdown_closures[0], ncb, 0 /* GPR_FALSE */, - 0 /* GPR_FALSE */); +void grpc_fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { + gpr_mu_lock(&fd->mu); + GPR_ASSERT(!fd->shutdown); + fd->shutdown = 1; + set_ready_locked(exec_ctx, fd, &fd->read_closure); + set_ready_locked(exec_ctx, fd, &fd->write_closure); + gpr_mu_unlock(&fd->mu); } -void grpc_fd_notify_on_read(grpc_fd *fd, grpc_iomgr_closure *closure) { - notify_on(fd, &fd->readst, closure, 0); +void grpc_fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + gpr_mu_lock(&fd->mu); + notify_on_locked(exec_ctx, fd, &fd->read_closure, closure); + gpr_mu_unlock(&fd->mu); } -void grpc_fd_notify_on_write(grpc_fd *fd, grpc_iomgr_closure *closure) { - notify_on(fd, &fd->writest, closure, 0); +void grpc_fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + gpr_mu_lock(&fd->mu); + notify_on_locked(exec_ctx, fd, &fd->write_closure, closure); + gpr_mu_unlock(&fd->mu); } gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, - gpr_uint32 read_mask, gpr_uint32 write_mask, - grpc_fd_watcher *watcher) { + grpc_pollset_worker *worker, gpr_uint32 read_mask, + gpr_uint32 write_mask, grpc_fd_watcher *watcher) { gpr_uint32 mask = 0; + grpc_closure *cur; + int requested; /* keep track of pollers that have requested our events, in case they change */ GRPC_FD_REF(fd, "poll"); - gpr_mu_lock(&fd->watcher_mu); + gpr_mu_lock(&fd->mu); + /* if we are shutdown, then don't add to the watcher set */ - if (gpr_atm_no_barrier_load(&fd->shutdown)) { + if (fd->shutdown) { watcher->fd = NULL; watcher->pollset = NULL; - gpr_mu_unlock(&fd->watcher_mu); + watcher->worker = NULL; + gpr_mu_unlock(&fd->mu); GRPC_FD_UNREF(fd, "poll"); return 0; } + /* if there is nobody polling for read, but we need to, then start doing so */ - if (read_mask && !fd->read_watcher && - (gpr_uintptr)gpr_atm_acq_load(&fd->readst) > READY) { + cur = fd->read_closure; + requested = cur != CLOSURE_READY; + if (read_mask && fd->read_watcher == NULL && requested) { fd->read_watcher = watcher; mask |= read_mask; } /* if there is nobody polling for write, but we need to, then start doing so */ - if (write_mask && !fd->write_watcher && - (gpr_uintptr)gpr_atm_acq_load(&fd->writest) > READY) { + cur = fd->write_closure; + requested = cur != CLOSURE_READY; + if (write_mask && fd->write_watcher == NULL && requested) { fd->write_watcher = watcher; mask |= write_mask; } /* if not polling, remember this watcher in case we need someone to later */ - if (mask == 0) { + if (mask == 0 && worker != NULL) { watcher->next = &fd->inactive_watcher_root; watcher->prev = watcher->next->prev; watcher->next->prev = watcher->prev->next = watcher; } watcher->pollset = pollset; + watcher->worker = worker; watcher->fd = fd; - gpr_mu_unlock(&fd->watcher_mu); + gpr_mu_unlock(&fd->mu); return mask; } -void grpc_fd_end_poll(grpc_fd_watcher *watcher, int got_read, int got_write) { +void grpc_fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *watcher, + int got_read, int got_write) { int was_polling = 0; int kick = 0; grpc_fd *fd = watcher->fd; @@ -416,45 +368,58 @@ void grpc_fd_end_poll(grpc_fd_watcher *watcher, int got_read, int got_write) { return; } - gpr_mu_lock(&fd->watcher_mu); + gpr_mu_lock(&fd->mu); + if (watcher == fd->read_watcher) { /* remove read watcher, kick if we still need a read */ was_polling = 1; - kick = kick || !got_read; + if (!got_read) { + kick = 1; + } fd->read_watcher = NULL; } if (watcher == fd->write_watcher) { /* remove write watcher, kick if we still need a write */ was_polling = 1; - kick = kick || !got_write; + if (!got_write) { + kick = 1; + } fd->write_watcher = NULL; } - if (!was_polling) { + if (!was_polling && watcher->worker != NULL) { /* remove from inactive list */ watcher->next->prev = watcher->prev; watcher->prev->next = watcher->next; } + if (got_read) { + if (set_ready_locked(exec_ctx, fd, &fd->read_closure)) { + kick = 1; + } + } + if (got_write) { + if (set_ready_locked(exec_ctx, fd, &fd->write_closure)) { + kick = 1; + } + } if (kick) { maybe_wake_one_watcher_locked(fd); } if (grpc_fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) { fd->closed = 1; close(fd->fd); - if (fd->on_done_closure != NULL) { - grpc_iomgr_add_callback(fd->on_done_closure); - } + grpc_exec_ctx_enqueue(exec_ctx, fd->on_done_closure, 1); } - gpr_mu_unlock(&fd->watcher_mu); + gpr_mu_unlock(&fd->mu); GRPC_FD_UNREF(fd, "poll"); } -void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback) { - set_ready(fd, &fd->readst, allow_synchronous_callback); +void grpc_fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { + set_ready(exec_ctx, fd, &fd->read_closure); } -void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback) { - set_ready(fd, &fd->writest, allow_synchronous_callback); +void grpc_fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { + set_ready(exec_ctx, fd, &fd->write_closure); } #endif diff --git a/src/core/iomgr/fd_posix.h b/src/core/iomgr/fd_posix.h index 835e9b339a..dc917ebbc0 100644 --- a/src/core/iomgr/fd_posix.h +++ b/src/core/iomgr/fd_posix.h @@ -46,20 +46,21 @@ typedef struct grpc_fd_watcher { struct grpc_fd_watcher *next; struct grpc_fd_watcher *prev; grpc_pollset *pollset; + grpc_pollset_worker *worker; grpc_fd *fd; } grpc_fd_watcher; struct grpc_fd { int fd; /* refst format: - bit0: 1=active/0=orphaned - bit1-n: refcount + bit0: 1=active/0=orphaned + bit1-n: refcount meaning that mostly we ref by two to avoid altering the orphaned bit, and just unref by 1 when we're ready to flag the object as orphaned */ gpr_atm refst; - gpr_mu set_state_mu; - gpr_atm shutdown; + gpr_mu mu; + int shutdown; int closed; /* The watcher list. @@ -84,18 +85,16 @@ struct grpc_fd { If at a later time there becomes need of a poller to poll, one of the inactive pollers may be kicked out of their poll loops to take that responsibility. */ - gpr_mu watcher_mu; grpc_fd_watcher inactive_watcher_root; grpc_fd_watcher *read_watcher; grpc_fd_watcher *write_watcher; - gpr_atm readst; - gpr_atm writest; + grpc_closure *read_closure; + grpc_closure *write_closure; struct grpc_fd *freelist_next; - grpc_iomgr_closure *on_done_closure; - grpc_iomgr_closure *shutdown_closures[2]; + grpc_closure *on_done_closure; grpc_iomgr_object iomgr_object; }; @@ -111,7 +110,7 @@ grpc_fd *grpc_fd_create(int fd, const char *name); Requires: *fd initialized; no outstanding notify_on_read or notify_on_write. MUST NOT be called with a pollset lock taken */ -void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_closure *on_done, +void grpc_fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done, const char *reason); /* Begin polling on an fd. @@ -126,17 +125,20 @@ void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_closure *on_done, fd's current interest (such as epoll) do not need to call this function. MUST NOT be called with a pollset lock taken */ gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, - gpr_uint32 read_mask, gpr_uint32 write_mask, - grpc_fd_watcher *rec); + grpc_pollset_worker *worker, gpr_uint32 read_mask, + gpr_uint32 write_mask, grpc_fd_watcher *rec); /* Complete polling previously started with grpc_fd_begin_poll - MUST NOT be called with a pollset lock taken */ -void grpc_fd_end_poll(grpc_fd_watcher *rec, int got_read, int got_write); + MUST NOT be called with a pollset lock taken + if got_read or got_write are 1, also does the become_{readable,writable} as + appropriate. */ +void grpc_fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *rec, + int got_read, int got_write); /* Return 1 if this fd is orphaned, 0 otherwise */ int grpc_fd_is_orphaned(grpc_fd *fd); /* Cause any current callbacks to error out with GRPC_CALLBACK_CANCELLED. */ -void grpc_fd_shutdown(grpc_fd *fd); +void grpc_fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd); /* Register read interest, causing read_cb to be called once when fd becomes readable, on deadline specified by deadline, or on shutdown triggered by @@ -151,17 +153,19 @@ void grpc_fd_shutdown(grpc_fd *fd); underlying platform. This means that users must drain fd in read_cb before calling notify_on_read again. Users are also expected to handle spurious events, i.e read_cb is called while nothing can be readable from fd */ -void grpc_fd_notify_on_read(grpc_fd *fd, grpc_iomgr_closure *closure); +void grpc_fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure); /* Exactly the same semantics as above, except based on writable events. */ -void grpc_fd_notify_on_write(grpc_fd *fd, grpc_iomgr_closure *closure); +void grpc_fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure); /* Notification from the poller to an fd that it has become readable or writable. If allow_synchronous_callback is 1, allow running the fd callback inline in this callstack, otherwise register an asynchronous callback and return */ -void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback); -void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback); +void grpc_fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd); +void grpc_fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd); /* Reference counting for fds */ #ifdef GRPC_FD_REF_COUNT_DEBUG diff --git a/src/core/iomgr/iocp_windows.c b/src/core/iomgr/iocp_windows.c index 09a457dd9a..65da3a9d2d 100644 --- a/src/core/iomgr/iocp_windows.c +++ b/src/core/iomgr/iocp_windows.c @@ -42,7 +42,7 @@ #include <grpc/support/alloc.h> #include <grpc/support/thd.h> -#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/timer_internal.h" #include "src/core/iomgr/iocp_windows.h" #include "src/core/iomgr/iomgr_internal.h" #include "src/core/iomgr/socket_windows.h" @@ -50,14 +50,28 @@ static ULONG g_iocp_kick_token; static OVERLAPPED g_iocp_custom_overlap; -static gpr_event g_shutdown_iocp; -static gpr_event g_iocp_done; -static gpr_atm g_orphans = 0; static gpr_atm g_custom_events = 0; static HANDLE g_iocp; -static void do_iocp_work() { +static DWORD deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now) { + gpr_timespec timeout; + static const int max_spin_polling_us = 10; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { + return INFINITE; + } + if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( + max_spin_polling_us, + GPR_TIMESPAN))) <= 0) { + return 0; + } + timeout = gpr_time_sub(deadline, now); + return gpr_time_to_millis(gpr_time_add( + timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); +} + +void grpc_iocp_work(grpc_exec_ctx *exec_ctx, gpr_timespec deadline) { BOOL success; DWORD bytes = 0; DWORD flags = 0; @@ -65,13 +79,13 @@ static void do_iocp_work() { LPOVERLAPPED overlapped; grpc_winsocket *socket; grpc_winsocket_callback_info *info; - void (*f)(void *, int) = NULL; - void *opaque = NULL; - success = GetQueuedCompletionStatus(g_iocp, &bytes, &completion_key, - &overlapped, INFINITE); - /* success = 0 and overlapped = NULL means the deadline got attained. - Which is impossible. since our wait time is +inf */ - GPR_ASSERT(success || overlapped); + grpc_closure *closure = NULL; + success = GetQueuedCompletionStatus( + g_iocp, &bytes, &completion_key, &overlapped, + deadline_to_millis_timeout(deadline, gpr_now(deadline.clock_type))); + if (success == 0 && overlapped == NULL) { + return; + } GPR_ASSERT(completion_key && overlapped); if (overlapped == &g_iocp_custom_overlap) { gpr_atm_full_fetch_add(&g_custom_events, -1); @@ -92,53 +106,27 @@ static void do_iocp_work() { gpr_log(GPR_ERROR, "Unknown IOCP operation"); abort(); } - GPR_ASSERT(info->outstanding); - if (socket->orphan) { - info->outstanding = 0; - if (!socket->read_info.outstanding && !socket->write_info.outstanding) { - grpc_winsocket_destroy(socket); - gpr_atm_full_fetch_add(&g_orphans, -1); - } - return; - } success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes, FALSE, &flags); info->bytes_transfered = bytes; info->wsa_error = success ? 0 : WSAGetLastError(); GPR_ASSERT(overlapped == &info->overlapped); - gpr_mu_lock(&socket->state_mu); GPR_ASSERT(!info->has_pending_iocp); - if (info->cb) { - f = info->cb; - opaque = info->opaque; - info->cb = NULL; + gpr_mu_lock(&socket->state_mu); + if (info->closure) { + closure = info->closure; + info->closure = NULL; } else { info->has_pending_iocp = 1; } gpr_mu_unlock(&socket->state_mu); - if (f) f(opaque, 1); -} - -static void iocp_loop(void *p) { - while (gpr_atm_acq_load(&g_orphans) || gpr_atm_acq_load(&g_custom_events) || - !gpr_event_get(&g_shutdown_iocp)) { - grpc_maybe_call_delayed_callbacks(NULL, 1); - do_iocp_work(); - } - - gpr_event_set(&g_iocp_done, (void *)1); + grpc_exec_ctx_enqueue(exec_ctx, closure, 1); } void grpc_iocp_init(void) { - gpr_thd_id id; - g_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0); GPR_ASSERT(g_iocp); - - gpr_event_init(&g_iocp_done); - gpr_event_init(&g_shutdown_iocp); - gpr_thd_new(&id, iocp_loop, NULL, NULL); } void grpc_iocp_kick(void) { @@ -150,13 +138,22 @@ void grpc_iocp_kick(void) { GPR_ASSERT(success); } +void grpc_iocp_flush(void) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + do { + grpc_iocp_work(&exec_ctx, gpr_inf_past(GPR_CLOCK_MONOTONIC)); + } while (grpc_exec_ctx_flush(&exec_ctx)); +} + void grpc_iocp_shutdown(void) { - BOOL success; - gpr_event_set(&g_shutdown_iocp, (void *)1); - grpc_iocp_kick(); - gpr_event_wait(&g_iocp_done, gpr_inf_future(GPR_CLOCK_REALTIME)); - success = CloseHandle(g_iocp); - GPR_ASSERT(success); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (gpr_atm_acq_load(&g_custom_events)) { + grpc_iocp_work(&exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC)); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); + GPR_ASSERT(CloseHandle(g_iocp)); } void grpc_iocp_add_socket(grpc_winsocket *socket) { @@ -175,41 +172,35 @@ void grpc_iocp_add_socket(grpc_winsocket *socket) { GPR_ASSERT(ret == g_iocp); } -void grpc_iocp_socket_orphan(grpc_winsocket *socket) { - GPR_ASSERT(!socket->orphan); - gpr_atm_full_fetch_add(&g_orphans, 1); - socket->orphan = 1; -} - /* Calling notify_on_read or write means either of two things: -) The IOCP already completed in the background, and we need to call the callback now. -) The IOCP hasn't completed yet, and we're queuing it for later. */ -static void socket_notify_on_iocp(grpc_winsocket *socket, - void (*cb)(void *, int), void *opaque, +static void socket_notify_on_iocp(grpc_exec_ctx *exec_ctx, + grpc_winsocket *socket, grpc_closure *closure, grpc_winsocket_callback_info *info) { int run_now = 0; - GPR_ASSERT(!info->cb); + GPR_ASSERT(info->closure == NULL); gpr_mu_lock(&socket->state_mu); if (info->has_pending_iocp) { run_now = 1; info->has_pending_iocp = 0; + grpc_exec_ctx_enqueue(exec_ctx, closure, 1); } else { - info->cb = cb; - info->opaque = opaque; + info->closure = closure; } gpr_mu_unlock(&socket->state_mu); - if (run_now) cb(opaque, 1); } -void grpc_socket_notify_on_write(grpc_winsocket *socket, - void (*cb)(void *, int), void *opaque) { - socket_notify_on_iocp(socket, cb, opaque, &socket->write_info); +void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx, + grpc_winsocket *socket, + grpc_closure *closure) { + socket_notify_on_iocp(exec_ctx, socket, closure, &socket->write_info); } -void grpc_socket_notify_on_read(grpc_winsocket *socket, void (*cb)(void *, int), - void *opaque) { - socket_notify_on_iocp(socket, cb, opaque, &socket->read_info); +void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, + grpc_closure *closure) { + socket_notify_on_iocp(exec_ctx, socket, closure, &socket->read_info); } #endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/iocp_windows.h b/src/core/iomgr/iocp_windows.h index ee3847a229..75f3ba8477 100644 --- a/src/core/iomgr/iocp_windows.h +++ b/src/core/iomgr/iocp_windows.h @@ -38,16 +38,19 @@ #include "src/core/iomgr/socket_windows.h" +void grpc_iocp_work(grpc_exec_ctx *exec_ctx, gpr_timespec deadline); void grpc_iocp_init(void); void grpc_iocp_kick(void); +void grpc_iocp_flush(void); void grpc_iocp_shutdown(void); void grpc_iocp_add_socket(grpc_winsocket *); -void grpc_iocp_socket_orphan(grpc_winsocket *); -void grpc_socket_notify_on_write(grpc_winsocket *, - void (*cb)(void *, int success), void *opaque); +void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx, + grpc_winsocket *winsocket, + grpc_closure *closure); -void grpc_socket_notify_on_read(grpc_winsocket *, - void (*cb)(void *, int success), void *opaque); +void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx, + grpc_winsocket *winsocket, + grpc_closure *closure); #endif /* GRPC_INTERNAL_CORE_IOMGR_IOCP_WINDOWS_H */ diff --git a/src/core/iomgr/iomgr.c b/src/core/iomgr/iomgr.c index fdc9adf4af..212ce5534d 100644 --- a/src/core/iomgr/iomgr.c +++ b/src/core/iomgr/iomgr.c @@ -34,69 +34,32 @@ #include "src/core/iomgr/iomgr.h" #include <stdlib.h> +#include <string.h> -#include "src/core/iomgr/iomgr_internal.h" -#include "src/core/iomgr/alarm_internal.h" -#include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/string_util.h> #include <grpc/support/sync.h> #include <grpc/support/thd.h> +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/timer_internal.h" +#include "src/core/support/string.h" + static gpr_mu g_mu; static gpr_cv g_rcv; -static grpc_iomgr_closure *g_cbs_head = NULL; -static grpc_iomgr_closure *g_cbs_tail = NULL; static int g_shutdown; -static gpr_event g_background_callback_executor_done; static grpc_iomgr_object g_root_object; -/* Execute followup callbacks continuously. - Other threads may check in and help during pollset_work() */ -static void background_callback_executor(void *ignored) { - gpr_mu_lock(&g_mu); - while (!g_shutdown) { - gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - gpr_timespec short_deadline = gpr_time_add( - gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_millis(100, GPR_TIMESPAN)); - if (g_cbs_head) { - grpc_iomgr_closure *closure = g_cbs_head; - g_cbs_head = closure->next; - if (!g_cbs_head) g_cbs_tail = NULL; - gpr_mu_unlock(&g_mu); - closure->cb(closure->cb_arg, closure->success); - gpr_mu_lock(&g_mu); - } else if (grpc_alarm_check(&g_mu, gpr_now(GPR_CLOCK_MONOTONIC), - &deadline)) { - } else { - gpr_mu_unlock(&g_mu); - gpr_sleep_until(gpr_time_min(short_deadline, deadline)); - gpr_mu_lock(&g_mu); - } - } - gpr_mu_unlock(&g_mu); - gpr_event_set(&g_background_callback_executor_done, (void *)1); -} - -void grpc_kick_poller(void) { - /* Empty. The background callback executor polls periodically. The activity - * the kicker is trying to draw the executor's attention to will be picked up - * either by one of the periodic wakeups or by one of the polling application - * threads. */ -} - void grpc_iomgr_init(void) { - gpr_thd_id id; g_shutdown = 0; gpr_mu_init(&g_mu); gpr_cv_init(&g_rcv); - grpc_alarm_list_init(gpr_now(GPR_CLOCK_MONOTONIC)); + grpc_timer_list_init(gpr_now(GPR_CLOCK_MONOTONIC)); g_root_object.next = g_root_object.prev = &g_root_object; g_root_object.name = "root"; grpc_iomgr_platform_init(); - gpr_event_init(&g_background_callback_executor_done); - gpr_thd_new(&id, background_callback_executor, NULL, NULL); + grpc_pollset_global_init(); } static size_t count_objects(void) { @@ -108,77 +71,67 @@ static size_t count_objects(void) { return n; } -void grpc_iomgr_shutdown(void) { +static void dump_objects(const char *kind) { grpc_iomgr_object *obj; - grpc_iomgr_closure *closure; + for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { + gpr_log(GPR_DEBUG, "%s OBJECT: %s %p", kind, obj->name, obj); + } +} + +void grpc_iomgr_shutdown(void) { gpr_timespec shutdown_deadline = gpr_time_add( gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN)); gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + grpc_iomgr_platform_flush(); gpr_mu_lock(&g_mu); g_shutdown = 1; - while (g_cbs_head != NULL || g_root_object.next != &g_root_object) { + while (g_root_object.next != &g_root_object) { if (gpr_time_cmp( gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time), gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { - if (g_cbs_head != NULL && g_root_object.next != &g_root_object) { - gpr_log(GPR_DEBUG, - "Waiting for %d iomgr objects to be destroyed and executing " - "final callbacks", - count_objects()); - } else if (g_cbs_head != NULL) { - gpr_log(GPR_DEBUG, "Executing final iomgr callbacks"); - } else { + if (g_root_object.next != &g_root_object) { gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed", count_objects()); } last_warning_time = gpr_now(GPR_CLOCK_REALTIME); } - if (g_cbs_head) { - do { - closure = g_cbs_head; - g_cbs_head = closure->next; - if (!g_cbs_head) g_cbs_tail = NULL; - gpr_mu_unlock(&g_mu); - - closure->cb(closure->cb_arg, 0); - gpr_mu_lock(&g_mu); - } while (g_cbs_head); - continue; - } - if (grpc_alarm_check(&g_mu, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL)) { + if (grpc_timer_check(&exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC), + NULL)) { + gpr_mu_unlock(&g_mu); + grpc_exec_ctx_flush(&exec_ctx); + gpr_mu_lock(&g_mu); continue; } if (g_root_object.next != &g_root_object) { - int timeout = 0; gpr_timespec short_deadline = gpr_time_add( gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100, GPR_TIMESPAN)); - while (gpr_cv_wait(&g_rcv, &g_mu, short_deadline) && g_cbs_head == NULL) { + if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline)) { if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) { - timeout = 1; + if (g_root_object.next != &g_root_object) { + gpr_log(GPR_DEBUG, + "Failed to free %d iomgr objects before shutdown deadline: " + "memory leaks are likely", + count_objects()); + dump_objects("LEAKED"); + } break; } } - if (timeout) { - gpr_log(GPR_DEBUG, - "Failed to free %d iomgr objects before shutdown deadline: " - "memory leaks are likely", - count_objects()); - for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { - gpr_log(GPR_DEBUG, "LEAKED OBJECT: %s %p", obj->name, obj); - } - break; - } } } gpr_mu_unlock(&g_mu); - grpc_kick_poller(); - gpr_event_wait(&g_background_callback_executor_done, - gpr_inf_future(GPR_CLOCK_REALTIME)); + grpc_timer_list_shutdown(&exec_ctx); + grpc_exec_ctx_finish(&exec_ctx); - grpc_alarm_list_shutdown(); + /* ensure all threads have left g_mu */ + gpr_mu_lock(&g_mu); + gpr_mu_unlock(&g_mu); + grpc_pollset_global_shutdown(); grpc_iomgr_platform_shutdown(); gpr_mu_destroy(&g_mu); gpr_cv_destroy(&g_rcv); @@ -188,7 +141,7 @@ void grpc_iomgr_register_object(grpc_iomgr_object *obj, const char *name) { obj->name = gpr_strdup(name); gpr_mu_lock(&g_mu); obj->next = &g_root_object; - obj->prev = obj->next->prev; + obj->prev = g_root_object.prev; obj->next->prev = obj->prev->next = obj; gpr_mu_unlock(&g_mu); } @@ -201,74 +154,3 @@ void grpc_iomgr_unregister_object(grpc_iomgr_object *obj) { gpr_mu_unlock(&g_mu); gpr_free(obj->name); } - -void grpc_iomgr_closure_init(grpc_iomgr_closure *closure, grpc_iomgr_cb_func cb, - void *cb_arg) { - closure->cb = cb; - closure->cb_arg = cb_arg; - closure->next = NULL; -} - -static void assert_not_scheduled_locked(grpc_iomgr_closure *closure) { -#ifndef NDEBUG - grpc_iomgr_closure *c; - - for (c = g_cbs_head; c; c = c->next) { - GPR_ASSERT(c != closure); - } -#endif -} - -void grpc_iomgr_add_delayed_callback(grpc_iomgr_closure *closure, int success) { - closure->success = success; - GPR_ASSERT(closure->cb); - gpr_mu_lock(&g_mu); - assert_not_scheduled_locked(closure); - closure->next = NULL; - if (!g_cbs_tail) { - g_cbs_head = g_cbs_tail = closure; - } else { - g_cbs_tail->next = closure; - g_cbs_tail = closure; - } - if (g_shutdown) { - gpr_cv_signal(&g_rcv); - } - gpr_mu_unlock(&g_mu); -} - -void grpc_iomgr_add_callback(grpc_iomgr_closure *closure) { - grpc_iomgr_add_delayed_callback(closure, 1 /* GPR_TRUE */); -} - -int grpc_maybe_call_delayed_callbacks(gpr_mu *drop_mu, int success) { - int n = 0; - gpr_mu *retake_mu = NULL; - grpc_iomgr_closure *closure; - for (;;) { - /* check for new work */ - if (!gpr_mu_trylock(&g_mu)) { - break; - } - closure = g_cbs_head; - if (!closure) { - gpr_mu_unlock(&g_mu); - break; - } - g_cbs_head = closure->next; - if (!g_cbs_head) g_cbs_tail = NULL; - gpr_mu_unlock(&g_mu); - /* if we have a mutex to drop, do so before executing work */ - if (drop_mu) { - gpr_mu_unlock(drop_mu); - retake_mu = drop_mu; - drop_mu = NULL; - } - closure->cb(closure->cb_arg, success && closure->success); - n++; - } - if (retake_mu) { - gpr_mu_lock(retake_mu); - } - return n; -} diff --git a/src/core/iomgr/iomgr.h b/src/core/iomgr/iomgr.h index 261c17366a..c9ea84c605 100644 --- a/src/core/iomgr/iomgr.h +++ b/src/core/iomgr/iomgr.h @@ -34,47 +34,10 @@ #ifndef GRPC_INTERNAL_CORE_IOMGR_IOMGR_H #define GRPC_INTERNAL_CORE_IOMGR_IOMGR_H -/** gRPC Callback definition. - * - * \param arg Arbitrary input. - * \param success An indication on the state of the iomgr. On false, cleanup - * actions should be taken (eg, shutdown). */ -typedef void (*grpc_iomgr_cb_func)(void *arg, int success); - -/** A closure over a grpc_iomgr_cb_func. */ -typedef struct grpc_iomgr_closure { - /** Bound callback. */ - grpc_iomgr_cb_func cb; - - /** Arguments to be passed to "cb". */ - void *cb_arg; - - /** Internal. A boolean indication to "cb" on the state of the iomgr. - * For instance, closures created during a shutdown would have this field set - * to false. */ - int success; - - /**< Internal. Do not touch */ - struct grpc_iomgr_closure *next; -} grpc_iomgr_closure; - -/** Initializes \a closure with \a cb and \a cb_arg. */ -void grpc_iomgr_closure_init(grpc_iomgr_closure *closure, grpc_iomgr_cb_func cb, - void *cb_arg); - /** Initializes the iomgr. */ void grpc_iomgr_init(void); /** Signals the intention to shutdown the iomgr. */ void grpc_iomgr_shutdown(void); -/** Registers a closure to be invoked at some point in the future. - * - * Can be called from within a callback or from anywhere else */ -void grpc_iomgr_add_callback(grpc_iomgr_closure *closure); - -/** As per grpc_iomgr_add_callback, with the ability to set the success - argument. */ -void grpc_iomgr_add_delayed_callback(grpc_iomgr_closure *iocb, int success); - #endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_H */ diff --git a/src/core/iomgr/iomgr_internal.h b/src/core/iomgr/iomgr_internal.h index 4cec973ba0..e372c18e8a 100644 --- a/src/core/iomgr/iomgr_internal.h +++ b/src/core/iomgr/iomgr_internal.h @@ -43,13 +43,16 @@ typedef struct grpc_iomgr_object { struct grpc_iomgr_object *prev; } grpc_iomgr_object; -int grpc_maybe_call_delayed_callbacks(gpr_mu *drop_mu, int success); -void grpc_iomgr_add_delayed_callback(grpc_iomgr_closure *iocb, int success); +void grpc_pollset_global_init(void); +void grpc_pollset_global_shutdown(void); void grpc_iomgr_register_object(grpc_iomgr_object *obj, const char *name); void grpc_iomgr_unregister_object(grpc_iomgr_object *obj); void grpc_iomgr_platform_init(void); +/** flush any globally queued work from iomgr */ +void grpc_iomgr_platform_flush(void); +/** tear down all platform specific global iomgr structures */ void grpc_iomgr_platform_shutdown(void); #endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_INTERNAL_H */ diff --git a/src/core/iomgr/iomgr_posix.c b/src/core/iomgr/iomgr_posix.c index 2425e59941..fecb7b9760 100644 --- a/src/core/iomgr/iomgr_posix.c +++ b/src/core/iomgr/iomgr_posix.c @@ -42,13 +42,11 @@ void grpc_iomgr_platform_init(void) { grpc_fd_global_init(); - grpc_pollset_global_init(); grpc_register_tracer("tcp", &grpc_tcp_trace); } -void grpc_iomgr_platform_shutdown(void) { - grpc_pollset_global_shutdown(); - grpc_fd_global_shutdown(); -} +void grpc_iomgr_platform_flush(void) {} + +void grpc_iomgr_platform_shutdown(void) { grpc_fd_global_shutdown(); } #endif /* GRPC_POSIX_SOCKET */ diff --git a/src/core/iomgr/iomgr_posix.h b/src/core/iomgr/iomgr_posix.h index 716fedb636..068a5c6d7c 100644 --- a/src/core/iomgr/iomgr_posix.h +++ b/src/core/iomgr/iomgr_posix.h @@ -36,7 +36,4 @@ #include "src/core/iomgr/iomgr_internal.h" -void grpc_pollset_global_init(void); -void grpc_pollset_global_shutdown(void); - #endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_POSIX_H */ diff --git a/src/core/iomgr/iomgr_windows.c b/src/core/iomgr/iomgr_windows.c index b49cb87e97..14775516bb 100644 --- a/src/core/iomgr/iomgr_windows.c +++ b/src/core/iomgr/iomgr_windows.c @@ -63,6 +63,8 @@ void grpc_iomgr_platform_init(void) { grpc_iocp_init(); } +void grpc_iomgr_platform_flush(void) { grpc_iocp_flush(); } + void grpc_iomgr_platform_shutdown(void) { grpc_iocp_shutdown(); winsock_shutdown(); diff --git a/src/core/iomgr/pollset.h b/src/core/iomgr/pollset.h index 337596cb74..d15553a12a 100644 --- a/src/core/iomgr/pollset.h +++ b/src/core/iomgr/pollset.h @@ -55,9 +55,8 @@ #endif void grpc_pollset_init(grpc_pollset *pollset); -void grpc_pollset_shutdown(grpc_pollset *pollset, - void (*shutdown_done)(void *arg), - void *shutdown_done_arg); +void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure); void grpc_pollset_destroy(grpc_pollset *pollset); /* Do some work on a pollset. @@ -74,9 +73,13 @@ void grpc_pollset_destroy(grpc_pollset *pollset); grpc_pollset_work, and it is guaranteed that GRPC_POLLSET_MU(pollset) will not be released by grpc_pollset_work AFTER worker has been destroyed. - Tries not to block past deadline. */ -void grpc_pollset_work(grpc_pollset *pollset, grpc_pollset_worker *worker, - gpr_timespec now, gpr_timespec deadline); + Tries not to block past deadline. + May call grpc_closure_list_run on grpc_closure_list, without holding the + pollset + lock */ +void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *worker, gpr_timespec now, + gpr_timespec deadline); /* Break one polling thread out of polling work for this pollset. If specific_worker is GRPC_POLLSET_KICK_BROADCAST, kick ALL the workers. diff --git a/src/core/iomgr/pollset_multipoller_with_epoll.c b/src/core/iomgr/pollset_multipoller_with_epoll.c index fe66ebed77..2aafd21dfb 100644 --- a/src/core/iomgr/pollset_multipoller_with_epoll.c +++ b/src/core/iomgr/pollset_multipoller_with_epoll.c @@ -41,9 +41,11 @@ #include <sys/epoll.h> #include <unistd.h> -#include "src/core/iomgr/fd_posix.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include "src/core/iomgr/fd_posix.h" +#include "src/core/support/block_annotate.h" +#include "src/core/profiling/timers.h" typedef struct wakeup_fd_hdl { grpc_wakeup_fd wakeup_fd; @@ -53,7 +55,7 @@ typedef struct wakeup_fd_hdl { typedef struct { grpc_pollset *pollset; grpc_fd *fd; - grpc_iomgr_closure closure; + grpc_closure closure; } delayed_add; typedef struct { @@ -61,7 +63,8 @@ typedef struct { wakeup_fd_hdl *free_wakeup_fds; } pollset_hdr; -static void finally_add_fd(grpc_pollset *pollset, grpc_fd *fd) { +static void finally_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd) { pollset_hdr *h = pollset->data.ptr; struct epoll_event ev; int err; @@ -70,9 +73,9 @@ static void finally_add_fd(grpc_pollset *pollset, grpc_fd *fd) { /* We pretend to be polling whilst adding an fd to keep the fd from being closed during the add. This may result in a spurious wakeup being assigned to this pollset whilst adding, but that should be benign. */ - GPR_ASSERT(grpc_fd_begin_poll(fd, pollset, 0, 0, &watcher) == 0); + GPR_ASSERT(grpc_fd_begin_poll(fd, pollset, NULL, 0, 0, &watcher) == 0); if (watcher.fd != NULL) { - ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET); ev.data.ptr = fd; err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev); if (err < 0) { @@ -83,15 +86,15 @@ static void finally_add_fd(grpc_pollset *pollset, grpc_fd *fd) { } } } - grpc_fd_end_poll(&watcher, 0, 0); + grpc_fd_end_poll(exec_ctx, &watcher, 0, 0); } -static void perform_delayed_add(void *arg, int iomgr_status) { +static void perform_delayed_add(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_status) { delayed_add *da = arg; - int do_shutdown_cb = 0; if (!grpc_fd_is_orphaned(da->fd)) { - finally_add_fd(da->pollset, da->fd); + finally_add_fd(exec_ctx, da->pollset, da->fd); } gpr_mu_lock(&da->pollset->mu); @@ -99,40 +102,37 @@ static void perform_delayed_add(void *arg, int iomgr_status) { if (da->pollset->shutting_down) { /* We don't care about this pollset anymore. */ if (da->pollset->in_flight_cbs == 0 && !da->pollset->called_shutdown) { - GPR_ASSERT(!grpc_pollset_has_workers(da->pollset)); da->pollset->called_shutdown = 1; - do_shutdown_cb = 1; + grpc_exec_ctx_enqueue(exec_ctx, da->pollset->shutdown_done, 1); } } gpr_mu_unlock(&da->pollset->mu); GRPC_FD_UNREF(da->fd, "delayed_add"); - if (do_shutdown_cb) { - da->pollset->shutdown_done_cb(da->pollset->shutdown_done_arg); - } - gpr_free(da); } -static void multipoll_with_epoll_pollset_add_fd(grpc_pollset *pollset, +static void multipoll_with_epoll_pollset_add_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, grpc_fd *fd, int and_unlock_pollset) { if (and_unlock_pollset) { gpr_mu_unlock(&pollset->mu); - finally_add_fd(pollset, fd); + finally_add_fd(exec_ctx, pollset, fd); } else { delayed_add *da = gpr_malloc(sizeof(*da)); da->pollset = pollset; da->fd = fd; GRPC_FD_REF(fd, "delayed_add"); - grpc_iomgr_closure_init(&da->closure, perform_delayed_add, da); + grpc_closure_init(&da->closure, perform_delayed_add, da); pollset->in_flight_cbs++; - grpc_iomgr_add_callback(&da->closure); + grpc_exec_ctx_enqueue(exec_ctx, &da->closure, 1); } } -static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset, +static void multipoll_with_epoll_pollset_del_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, grpc_fd *fd, int and_unlock_pollset) { pollset_hdr *h = pollset->data.ptr; @@ -154,9 +154,9 @@ static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset, /* TODO(klempner): We probably want to turn this down a bit */ #define GRPC_EPOLL_MAX_EVENTS 1000 -static void multipoll_with_epoll_pollset_maybe_work( - grpc_pollset *pollset, grpc_pollset_worker *worker, gpr_timespec deadline, - gpr_timespec now, int allow_synchronous_callback) { +static void multipoll_with_epoll_pollset_maybe_work_and_unlock( + grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker, + gpr_timespec deadline, gpr_timespec now) { struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; int ep_rv; int poll_rv; @@ -181,7 +181,13 @@ static void multipoll_with_epoll_pollset_maybe_work( pfds[1].events = POLLIN; pfds[1].revents = 0; + /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid + even going into the blocking annotation if possible */ + GPR_TIMER_BEGIN("poll", 0); + GRPC_SCHEDULING_START_BLOCKING_REGION; poll_rv = grpc_poll_function(pfds, 2, timeout_ms); + GRPC_SCHEDULING_END_BLOCKING_REGION; + GPR_TIMER_END("poll", 0); if (poll_rv < 0) { if (errno != EINTR) { @@ -195,6 +201,7 @@ static void multipoll_with_epoll_pollset_maybe_work( } if (pfds[1].revents) { do { + /* The following epoll_wait never blocks; it has a timeout of 0 */ ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0); if (ep_rv < 0) { if (errno != EINTR) { @@ -207,21 +214,23 @@ static void multipoll_with_epoll_pollset_maybe_work( /* TODO(klempner): We might want to consider making err and pri * separate events */ int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP); - int read = ep_ev[i].events & (EPOLLIN | EPOLLPRI); - int write = ep_ev[i].events & EPOLLOUT; - if (read || cancel) { - grpc_fd_become_readable(fd, allow_synchronous_callback); - } - if (write || cancel) { - grpc_fd_become_writable(fd, allow_synchronous_callback); + int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI); + int write_ev = ep_ev[i].events & EPOLLOUT; + if (fd == NULL) { + grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd); + } else { + if (read_ev || cancel) { + grpc_fd_become_readable(exec_ctx, fd); + } + if (write_ev || cancel) { + grpc_fd_become_writable(exec_ctx, fd); + } } } } } while (ep_rv == GRPC_EPOLL_MAX_EVENTS); } } - - gpr_mu_lock(&pollset->mu); } static void multipoll_with_epoll_pollset_finish_shutdown( @@ -235,14 +244,17 @@ static void multipoll_with_epoll_pollset_destroy(grpc_pollset *pollset) { static const grpc_pollset_vtable multipoll_with_epoll_pollset = { multipoll_with_epoll_pollset_add_fd, multipoll_with_epoll_pollset_del_fd, - multipoll_with_epoll_pollset_maybe_work, + multipoll_with_epoll_pollset_maybe_work_and_unlock, multipoll_with_epoll_pollset_finish_shutdown, multipoll_with_epoll_pollset_destroy}; -static void epoll_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, +static void epoll_become_multipoller(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, grpc_fd **fds, size_t nfds) { size_t i; pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr)); + struct epoll_event ev; + int err; pollset->vtable = &multipoll_with_epoll_pollset; pollset->data.ptr = h; @@ -252,8 +264,19 @@ static void epoll_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, gpr_log(GPR_ERROR, "epoll_create1 failed: %s", strerror(errno)); abort(); } + + ev.events = (uint32_t)(EPOLLIN | EPOLLET); + ev.data.ptr = NULL; + err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, + GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), &ev); + if (err < 0) { + gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", + GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), + strerror(errno)); + } + for (i = 0; i < nfds; i++) { - multipoll_with_epoll_pollset_add_fd(pollset, fds[i], 0); + multipoll_with_epoll_pollset_add_fd(exec_ctx, pollset, fds[i], 0); } } diff --git a/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/iomgr/pollset_multipoller_with_poll_posix.c index 30ee6e24db..faa6c14491 100644 --- a/src/core/iomgr/pollset_multipoller_with_poll_posix.c +++ b/src/core/iomgr/pollset_multipoller_with_poll_posix.c @@ -44,6 +44,7 @@ #include "src/core/iomgr/fd_posix.h" #include "src/core/iomgr/iomgr_internal.h" +#include "src/core/support/block_annotate.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/useful.h> @@ -59,7 +60,8 @@ typedef struct { grpc_fd **dels; } pollset_hdr; -static void multipoll_with_poll_pollset_add_fd(grpc_pollset *pollset, +static void multipoll_with_poll_pollset_add_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, grpc_fd *fd, int and_unlock_pollset) { size_t i; @@ -80,7 +82,8 @@ exit: } } -static void multipoll_with_poll_pollset_del_fd(grpc_pollset *pollset, +static void multipoll_with_poll_pollset_del_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, grpc_fd *fd, int and_unlock_pollset) { /* will get removed next poll cycle */ @@ -96,12 +99,16 @@ static void multipoll_with_poll_pollset_del_fd(grpc_pollset *pollset, } } -static void multipoll_with_poll_pollset_maybe_work( - grpc_pollset *pollset, grpc_pollset_worker *worker, gpr_timespec deadline, - gpr_timespec now, int allow_synchronous_callback) { +static void multipoll_with_poll_pollset_maybe_work_and_unlock( + grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker, + gpr_timespec deadline, gpr_timespec now) { +#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR) +#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR) + int timeout; int r; - size_t i, j, pfd_count, fd_count; + size_t i, j, fd_count; + nfds_t pfd_count; pollset_hdr *h; /* TODO(ctiller): inline some elements to avoid an allocation */ grpc_fd_watcher *watchers; @@ -110,13 +117,16 @@ static void multipoll_with_poll_pollset_maybe_work( h = pollset->data.ptr; timeout = grpc_poll_deadline_to_millis_timeout(deadline, now); /* TODO(ctiller): perform just one malloc here if we exceed the inline case */ - pfds = gpr_malloc(sizeof(*pfds) * (h->fd_count + 1)); - watchers = gpr_malloc(sizeof(*watchers) * (h->fd_count + 1)); + pfds = gpr_malloc(sizeof(*pfds) * (h->fd_count + 2)); + watchers = gpr_malloc(sizeof(*watchers) * (h->fd_count + 2)); fd_count = 0; - pfd_count = 1; - pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd); + pfd_count = 2; + pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd); pfds[0].events = POLLIN; - pfds[0].revents = POLLOUT; + pfds[0].revents = 0; + pfds[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd); + pfds[1].events = POLLIN; + pfds[1].revents = 0; for (i = 0; i < h->fd_count; i++) { int remove = grpc_fd_is_orphaned(h->fds[i]); for (j = 0; !remove && j < h->del_count; j++) { @@ -139,45 +149,45 @@ static void multipoll_with_poll_pollset_maybe_work( h->fd_count = fd_count; gpr_mu_unlock(&pollset->mu); - for (i = 1; i < pfd_count; i++) { - pfds[i].events = grpc_fd_begin_poll(watchers[i].fd, pollset, POLLIN, - POLLOUT, &watchers[i]); + for (i = 2; i < pfd_count; i++) { + pfds[i].events = (short)grpc_fd_begin_poll(watchers[i].fd, pollset, worker, + POLLIN, POLLOUT, &watchers[i]); } + /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid + even going into the blocking annotation if possible */ + GRPC_SCHEDULING_START_BLOCKING_REGION; r = grpc_poll_function(pfds, pfd_count, timeout); - - for (i = 1; i < pfd_count; i++) { - grpc_fd_end_poll(&watchers[i], pfds[i].revents & POLLIN, - pfds[i].revents & POLLOUT); - } + GRPC_SCHEDULING_END_BLOCKING_REGION; if (r < 0) { - if (errno != EINTR) { - gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); + gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); + for (i = 2; i < pfd_count; i++) { + grpc_fd_end_poll(exec_ctx, &watchers[i], 0, 0); } } else if (r == 0) { - /* do nothing */ + for (i = 2; i < pfd_count; i++) { + grpc_fd_end_poll(exec_ctx, &watchers[i], 0, 0); + } } else { - if (pfds[0].revents & POLLIN) { + if (pfds[0].revents & POLLIN_CHECK) { + grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd); + } + if (pfds[1].revents & POLLIN_CHECK) { grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd); } - for (i = 1; i < pfd_count; i++) { + for (i = 2; i < pfd_count; i++) { if (watchers[i].fd == NULL) { + grpc_fd_end_poll(exec_ctx, &watchers[i], 0, 0); continue; } - if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) { - grpc_fd_become_readable(watchers[i].fd, allow_synchronous_callback); - } - if (pfds[i].revents & (POLLOUT | POLLHUP | POLLERR)) { - grpc_fd_become_writable(watchers[i].fd, allow_synchronous_callback); - } + grpc_fd_end_poll(exec_ctx, &watchers[i], pfds[i].revents & POLLIN_CHECK, + pfds[i].revents & POLLOUT_CHECK); } } gpr_free(pfds); gpr_free(watchers); - - gpr_mu_lock(&pollset->mu); } static void multipoll_with_poll_pollset_finish_shutdown(grpc_pollset *pollset) { @@ -203,11 +213,12 @@ static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) { static const grpc_pollset_vtable multipoll_with_poll_pollset = { multipoll_with_poll_pollset_add_fd, multipoll_with_poll_pollset_del_fd, - multipoll_with_poll_pollset_maybe_work, + multipoll_with_poll_pollset_maybe_work_and_unlock, multipoll_with_poll_pollset_finish_shutdown, multipoll_with_poll_pollset_destroy}; -void grpc_poll_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, +void grpc_poll_become_multipoller(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, grpc_fd **fds, size_t nfds) { size_t i; pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr)); diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 6bd1b61f24..6f478ccacb 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -42,11 +42,12 @@ #include <string.h> #include <unistd.h> -#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/timer_internal.h" #include "src/core/iomgr/fd_posix.h" #include "src/core/iomgr/iomgr_internal.h" #include "src/core/iomgr/socket_utils_posix.h" #include "src/core/profiling/timers.h" +#include "src/core/support/block_annotate.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/thd.h> @@ -56,8 +57,16 @@ GPR_TLS_DECL(g_current_thread_poller); GPR_TLS_DECL(g_current_thread_worker); +/** Default poll() function - a pointer so that it can be overridden by some + * tests */ grpc_poll_function_type grpc_poll_function = poll; +/** The alarm system needs to be able to wakeup 'some poller' sometimes + * (specifically when a new alarm needs to be triggered earlier than the next + * alarm 'epoch'). + * This wakeup_fd gives us something to alert on when such a case occurs. */ +grpc_wakeup_fd grpc_global_wakeup_fd; + static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { worker->prev->next = worker->next; worker->next->prev = worker->prev; @@ -89,43 +98,92 @@ static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { worker->prev->next = worker->next->prev = worker; } -void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) { +void grpc_pollset_kick_ext(grpc_pollset *p, + grpc_pollset_worker *specific_worker, + gpr_uint32 flags) { + GPR_TIMER_BEGIN("grpc_pollset_kick_ext", 0); + /* pollset->mu already held */ if (specific_worker != NULL) { if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { + GPR_TIMER_BEGIN("grpc_pollset_kick_ext.broadcast", 0); + GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); for (specific_worker = p->root_worker.next; specific_worker != &p->root_worker; specific_worker = specific_worker->next) { grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd); } p->kicked_without_pollers = 1; + GPR_TIMER_END("grpc_pollset_kick_ext.broadcast", 0); } else if (gpr_tls_get(&g_current_thread_worker) != (gpr_intptr)specific_worker) { + GPR_TIMER_MARK("different_thread_worker", 0); + if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { + specific_worker->reevaluate_polling_on_wakeup = 1; + } + specific_worker->kicked_specifically = 1; + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd); + } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) { + GPR_TIMER_MARK("kick_yoself", 0); + if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { + specific_worker->reevaluate_polling_on_wakeup = 1; + } + specific_worker->kicked_specifically = 1; grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd); } } else if (gpr_tls_get(&g_current_thread_poller) != (gpr_intptr)p) { + GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); + GPR_TIMER_MARK("kick_anonymous", 0); specific_worker = pop_front_worker(p); if (specific_worker != NULL) { - push_back_worker(p, specific_worker); - grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd); + if (gpr_tls_get(&g_current_thread_worker) == + (gpr_intptr)specific_worker) { + GPR_TIMER_MARK("kick_anonymous_not_self", 0); + push_back_worker(p, specific_worker); + specific_worker = pop_front_worker(p); + if ((flags & GRPC_POLLSET_CAN_KICK_SELF) == 0 && + gpr_tls_get(&g_current_thread_worker) == + (gpr_intptr)specific_worker) { + push_back_worker(p, specific_worker); + specific_worker = NULL; + } + } + if (specific_worker != NULL) { + GPR_TIMER_MARK("finally_kick", 0); + push_back_worker(p, specific_worker); + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd); + } } else { + GPR_TIMER_MARK("kicked_no_pollers", 0); p->kicked_without_pollers = 1; } } + + GPR_TIMER_END("grpc_pollset_kick_ext", 0); +} + +void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) { + grpc_pollset_kick_ext(p, specific_worker, 0); } /* global state management */ void grpc_pollset_global_init(void) { gpr_tls_init(&g_current_thread_poller); + gpr_tls_init(&g_current_thread_worker); grpc_wakeup_fd_global_init(); + grpc_wakeup_fd_init(&grpc_global_wakeup_fd); } void grpc_pollset_global_shutdown(void) { - gpr_tls_destroy(&g_current_thread_poller); + grpc_wakeup_fd_destroy(&grpc_global_wakeup_fd); grpc_wakeup_fd_global_destroy(); + gpr_tls_destroy(&g_current_thread_poller); + gpr_tls_destroy(&g_current_thread_worker); } +void grpc_kick_poller(void) { grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd); } + /* main interface */ static void become_basic_pollset(grpc_pollset *pollset, grpc_fd *fd_or_null); @@ -136,12 +194,14 @@ void grpc_pollset_init(grpc_pollset *pollset) { pollset->in_flight_cbs = 0; pollset->shutting_down = 0; pollset->called_shutdown = 0; + pollset->idle_jobs.head = pollset->idle_jobs.tail = NULL; become_basic_pollset(pollset, NULL); } -void grpc_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { +void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd) { gpr_mu_lock(&pollset->mu); - pollset->vtable->add_fd(pollset, fd, 1); + pollset->vtable->add_fd(exec_ctx, pollset, fd, 1); /* the following (enabled only in debug) will reacquire and then release our lock - meaning that if the unlocking flag passed to del_fd above is not respected, the code will deadlock (in a way that we have a chance of @@ -152,9 +212,10 @@ void grpc_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { #endif } -void grpc_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { +void grpc_pollset_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd) { gpr_mu_lock(&pollset->mu); - pollset->vtable->del_fd(pollset, fd, 1); + pollset->vtable->del_fd(exec_ctx, pollset, fd, 1); /* the following (enabled only in debug) will reacquire and then release our lock - meaning that if the unlocking flag passed to del_fd above is not respected, the code will deadlock (in a way that we have a chance of @@ -165,61 +226,129 @@ void grpc_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { #endif } -static void finish_shutdown(grpc_pollset *pollset) { +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + GPR_ASSERT(grpc_closure_list_empty(pollset->idle_jobs)); pollset->vtable->finish_shutdown(pollset); - pollset->shutdown_done_cb(pollset->shutdown_done_arg); + grpc_exec_ctx_enqueue(exec_ctx, pollset->shutdown_done, 1); } -void grpc_pollset_work(grpc_pollset *pollset, grpc_pollset_worker *worker, - gpr_timespec now, gpr_timespec deadline) { +void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *worker, gpr_timespec now, + gpr_timespec deadline) { /* pollset->mu already held */ int added_worker = 0; + int locked = 1; + int queued_work = 0; + int keep_polling = 0; + GPR_TIMER_BEGIN("grpc_pollset_work", 0); /* this must happen before we (potentially) drop pollset->mu */ worker->next = worker->prev = NULL; + worker->reevaluate_polling_on_wakeup = 0; + worker->kicked_specifically = 0; /* TODO(ctiller): pool these */ grpc_wakeup_fd_init(&worker->wakeup_fd); - if (grpc_maybe_call_delayed_callbacks(&pollset->mu, 1)) { + /* If there's work waiting for the pollset to be idle, and the + pollset is idle, then do that work */ + if (!grpc_pollset_has_workers(pollset) && + !grpc_closure_list_empty(pollset->idle_jobs)) { + grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs); goto done; } - if (grpc_alarm_check(&pollset->mu, now, &deadline)) { + /* Check alarms - these are a global resource so we just ping + each time through on every pollset. + May update deadline to ensure timely wakeups. + TODO(ctiller): can this work be localized? */ + if (grpc_timer_check(exec_ctx, now, &deadline)) { + gpr_mu_unlock(&pollset->mu); + locked = 0; goto done; } + /* If we're shutting down then we don't execute any extended work */ if (pollset->shutting_down) { goto done; } - if (!pollset->kicked_without_pollers) { - push_front_worker(pollset, worker); - added_worker = 1; - gpr_tls_set(&g_current_thread_poller, (gpr_intptr)pollset); - pollset->vtable->maybe_work(pollset, worker, deadline, now, 1); - gpr_tls_set(&g_current_thread_poller, 0); - } else { - pollset->kicked_without_pollers = 0; + /* Give do_promote priority so we don't starve it out */ + if (pollset->in_flight_cbs) { + gpr_mu_unlock(&pollset->mu); + locked = 0; + goto done; + } + /* Start polling, and keep doing so while we're being asked to + re-evaluate our pollers (this allows poll() based pollers to + ensure they don't miss wakeups) */ + keep_polling = 1; + while (keep_polling) { + keep_polling = 0; + if (!pollset->kicked_without_pollers) { + if (!added_worker) { + push_front_worker(pollset, worker); + added_worker = 1; + gpr_tls_set(&g_current_thread_worker, (gpr_intptr)worker); + } + gpr_tls_set(&g_current_thread_poller, (gpr_intptr)pollset); + GPR_TIMER_BEGIN("maybe_work_and_unlock", 0); + pollset->vtable->maybe_work_and_unlock(exec_ctx, pollset, worker, + deadline, now); + GPR_TIMER_END("maybe_work_and_unlock", 0); + locked = 0; + gpr_tls_set(&g_current_thread_poller, 0); + } else { + pollset->kicked_without_pollers = 0; + } + /* Finished execution - start cleaning up. + Note that we may arrive here from outside the enclosing while() loop. + In that case we won't loop though as we haven't added worker to the + worker list, which means nobody could ask us to re-evaluate polling). */ + done: + if (!locked) { + queued_work |= grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->mu); + locked = 1; + } + /* If we're forced to re-evaluate polling (via grpc_pollset_kick with + GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force + a loop */ + if (worker->reevaluate_polling_on_wakeup) { + worker->reevaluate_polling_on_wakeup = 0; + pollset->kicked_without_pollers = 0; + if (queued_work || worker->kicked_specifically) { + /* If there's queued work on the list, then set the deadline to be + immediate so we get back out of the polling loop quickly */ + deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC); + } + keep_polling = 1; + } } -done: - grpc_wakeup_fd_destroy(&worker->wakeup_fd); if (added_worker) { remove_worker(pollset, worker); + gpr_tls_set(&g_current_thread_worker, 0); } + grpc_wakeup_fd_destroy(&worker->wakeup_fd); if (pollset->shutting_down) { if (grpc_pollset_has_workers(pollset)) { grpc_pollset_kick(pollset, NULL); } else if (!pollset->called_shutdown && pollset->in_flight_cbs == 0) { pollset->called_shutdown = 1; gpr_mu_unlock(&pollset->mu); - finish_shutdown(pollset); + finish_shutdown(exec_ctx, pollset); + grpc_exec_ctx_flush(exec_ctx); /* Continuing to access pollset here is safe -- it is the caller's * responsibility to not destroy when it has outstanding calls to * grpc_pollset_work. * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */ gpr_mu_lock(&pollset->mu); + } else if (!grpc_closure_list_empty(pollset->idle_jobs)) { + gpr_mu_unlock(&pollset->mu); + grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->mu); } } + GPR_TIMER_END("grpc_pollset_work", 0); } -void grpc_pollset_shutdown(grpc_pollset *pollset, - void (*shutdown_done)(void *arg), - void *shutdown_done_arg) { +void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { int call_shutdown = 0; gpr_mu_lock(&pollset->mu); GPR_ASSERT(!pollset->shutting_down); @@ -229,13 +358,15 @@ void grpc_pollset_shutdown(grpc_pollset *pollset, pollset->called_shutdown = 1; call_shutdown = 1; } - pollset->shutdown_done_cb = shutdown_done; - pollset->shutdown_done_arg = shutdown_done_arg; + if (!grpc_pollset_has_workers(pollset)) { + grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs); + } + pollset->shutdown_done = closure; grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); gpr_mu_unlock(&pollset->mu); if (call_shutdown) { - finish_shutdown(pollset); + finish_shutdown(exec_ctx, pollset); } } @@ -261,7 +392,7 @@ int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline, } timeout = gpr_time_sub(deadline, now); return gpr_time_to_millis(gpr_time_add( - timeout, gpr_time_from_nanos(GPR_NS_PER_SEC - 1, GPR_TIMESPAN))); + timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); } /* @@ -273,15 +404,14 @@ typedef struct grpc_unary_promote_args { const grpc_pollset_vtable *original_vtable; grpc_pollset *pollset; grpc_fd *fd; - grpc_iomgr_closure promotion_closure; + grpc_closure promotion_closure; } grpc_unary_promote_args; -static void basic_do_promote(void *args, int success) { +static void basic_do_promote(grpc_exec_ctx *exec_ctx, void *args, int success) { grpc_unary_promote_args *up_args = args; const grpc_pollset_vtable *original_vtable = up_args->original_vtable; grpc_pollset *pollset = up_args->pollset; grpc_fd *fd = up_args->fd; - int do_shutdown_cb = 0; /* * This is quite tricky. There are a number of cases to keep in mind here: @@ -294,12 +424,7 @@ static void basic_do_promote(void *args, int success) { gpr_mu_lock(&pollset->mu); /* First we need to ensure that nobody is polling concurrently */ - if (grpc_pollset_has_workers(pollset)) { - grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); - grpc_iomgr_add_callback(&up_args->promotion_closure); - gpr_mu_unlock(&pollset->mu); - return; - } + GPR_ASSERT(!grpc_pollset_has_workers(pollset)); gpr_free(up_args); /* At this point the pollset may no longer be a unary poller. In that case @@ -311,21 +436,21 @@ static void basic_do_promote(void *args, int success) { if (pollset->shutting_down) { /* We don't care about this pollset anymore. */ if (pollset->in_flight_cbs == 0 && !pollset->called_shutdown) { - GPR_ASSERT(!grpc_pollset_has_workers(pollset)); pollset->called_shutdown = 1; - do_shutdown_cb = 1; + finish_shutdown(exec_ctx, pollset); } } else if (grpc_fd_is_orphaned(fd)) { /* Don't try to add it to anything, we'll drop our ref on it below */ } else if (pollset->vtable != original_vtable) { - pollset->vtable->add_fd(pollset, fd, 0); + pollset->vtable->add_fd(exec_ctx, pollset, fd, 0); } else if (fd != pollset->data.ptr) { grpc_fd *fds[2]; fds[0] = pollset->data.ptr; fds[1] = fd; if (fds[0] && !grpc_fd_is_orphaned(fds[0])) { - grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds)); + grpc_platform_become_multipoller(exec_ctx, pollset, fds, + GPR_ARRAY_SIZE(fds)); GRPC_FD_UNREF(fds[0], "basicpoll"); } else { /* old fd is orphaned and we haven't cleaned it up until now, so remain a @@ -340,16 +465,12 @@ static void basic_do_promote(void *args, int success) { gpr_mu_unlock(&pollset->mu); - if (do_shutdown_cb) { - pollset->shutdown_done_cb(pollset->shutdown_done_arg); - } - /* Matching ref in basic_pollset_add_fd */ GRPC_FD_UNREF(fd, "basicpoll_add"); } -static void basic_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd, - int and_unlock_pollset) { +static void basic_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd, int and_unlock_pollset) { grpc_unary_promote_args *up_args; GPR_ASSERT(fd); if (fd == pollset->data.ptr) goto exit; @@ -366,7 +487,8 @@ static void basic_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd, pollset->data.ptr = fd; GRPC_FD_REF(fd, "basicpoll"); } else if (!grpc_fd_is_orphaned(fds[0])) { - grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds)); + grpc_platform_become_multipoller(exec_ctx, pollset, fds, + GPR_ARRAY_SIZE(fds)); GRPC_FD_UNREF(fds[0], "basicpoll"); } else { /* old fd is orphaned and we haven't cleaned it up until now, so remain a @@ -383,13 +505,13 @@ static void basic_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd, GRPC_FD_REF(fd, "basicpoll_add"); pollset->in_flight_cbs++; up_args = gpr_malloc(sizeof(*up_args)); - up_args->pollset = pollset; up_args->fd = fd; up_args->original_vtable = pollset->vtable; + up_args->pollset = pollset; up_args->promotion_closure.cb = basic_do_promote; up_args->promotion_closure.cb_arg = up_args; - grpc_iomgr_add_callback(&up_args->promotion_closure); + grpc_closure_list_add(&pollset->idle_jobs, &up_args->promotion_closure, 1); grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); exit: @@ -398,8 +520,8 @@ exit: } } -static void basic_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd, - int and_unlock_pollset) { +static void basic_pollset_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd, int and_unlock_pollset) { GPR_ASSERT(fd); if (fd == pollset->data.ptr) { GRPC_FD_UNREF(pollset->data.ptr, "basicpoll"); @@ -411,77 +533,85 @@ static void basic_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd, } } -static void basic_pollset_maybe_work(grpc_pollset *pollset, - grpc_pollset_worker *worker, - gpr_timespec deadline, gpr_timespec now, - int allow_synchronous_callback) { - struct pollfd pfd[2]; +static void basic_pollset_maybe_work_and_unlock(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, + grpc_pollset_worker *worker, + gpr_timespec deadline, + gpr_timespec now) { +#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR) +#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR) + + struct pollfd pfd[3]; grpc_fd *fd; grpc_fd_watcher fd_watcher; int timeout; int r; - int nfds; + nfds_t nfds; - if (pollset->in_flight_cbs) { - /* Give do_promote priority so we don't starve it out */ - gpr_mu_unlock(&pollset->mu); - gpr_mu_lock(&pollset->mu); - return; - } fd = pollset->data.ptr; if (fd && grpc_fd_is_orphaned(fd)) { GRPC_FD_UNREF(fd, "basicpoll"); fd = pollset->data.ptr = NULL; } timeout = grpc_poll_deadline_to_millis_timeout(deadline, now); - pfd[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd); + pfd[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd); pfd[0].events = POLLIN; pfd[0].revents = 0; - nfds = 1; + pfd[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd); + pfd[1].events = POLLIN; + pfd[1].revents = 0; + nfds = 2; if (fd) { - pfd[1].fd = fd->fd; - pfd[1].revents = 0; + pfd[2].fd = fd->fd; + pfd[2].revents = 0; + GRPC_FD_REF(fd, "basicpoll_begin"); gpr_mu_unlock(&pollset->mu); - pfd[1].events = - grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT, &fd_watcher); - if (pfd[1].events != 0) { + pfd[2].events = (short)grpc_fd_begin_poll(fd, pollset, worker, POLLIN, + POLLOUT, &fd_watcher); + if (pfd[2].events != 0) { nfds++; } } else { gpr_mu_unlock(&pollset->mu); } + /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid + even going into the blocking annotation if possible */ /* poll fd count (argument 2) is shortened by one if we have no events to poll on - such that it only includes the kicker */ + GPR_TIMER_BEGIN("poll", 0); + GRPC_SCHEDULING_START_BLOCKING_REGION; r = grpc_poll_function(pfd, nfds, timeout); - GRPC_TIMER_MARK(GRPC_PTAG_POLL_FINISHED, r); - - if (fd) { - grpc_fd_end_poll(&fd_watcher, pfd[1].revents & POLLIN, - pfd[1].revents & POLLOUT); - } + GRPC_SCHEDULING_END_BLOCKING_REGION; + GPR_TIMER_END("poll", 0); if (r < 0) { - if (errno != EINTR) { - gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); + gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); + if (fd) { + grpc_fd_end_poll(exec_ctx, &fd_watcher, 0, 0); } } else if (r == 0) { - /* do nothing */ + if (fd) { + grpc_fd_end_poll(exec_ctx, &fd_watcher, 0, 0); + } } else { - if (pfd[0].revents & POLLIN) { + if (pfd[0].revents & POLLIN_CHECK) { + grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd); + } + if (pfd[1].revents & POLLIN_CHECK) { grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd); } - if (nfds > 1) { - if (pfd[1].revents & (POLLIN | POLLHUP | POLLERR)) { - grpc_fd_become_readable(fd, allow_synchronous_callback); - } - if (pfd[1].revents & (POLLOUT | POLLHUP | POLLERR)) { - grpc_fd_become_writable(fd, allow_synchronous_callback); - } + if (nfds > 2) { + grpc_fd_end_poll(exec_ctx, &fd_watcher, pfd[2].revents & POLLIN_CHECK, + pfd[2].revents & POLLOUT_CHECK); + } else if (fd) { + grpc_fd_end_poll(exec_ctx, &fd_watcher, 0, 0); } } - gpr_mu_lock(&pollset->mu); + if (fd) { + GRPC_FD_UNREF(fd, "basicpoll_begin"); + } } static void basic_pollset_destroy(grpc_pollset *pollset) { @@ -492,8 +622,9 @@ static void basic_pollset_destroy(grpc_pollset *pollset) { } static const grpc_pollset_vtable basic_pollset = { - basic_pollset_add_fd, basic_pollset_del_fd, basic_pollset_maybe_work, - basic_pollset_destroy, basic_pollset_destroy}; + basic_pollset_add_fd, basic_pollset_del_fd, + basic_pollset_maybe_work_and_unlock, basic_pollset_destroy, + basic_pollset_destroy}; static void become_basic_pollset(grpc_pollset *pollset, grpc_fd *fd_or_null) { pollset->vtable = &basic_pollset; diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h index 69bd9cca8c..95ebeab1c2 100644 --- a/src/core/iomgr/pollset_posix.h +++ b/src/core/iomgr/pollset_posix.h @@ -37,6 +37,8 @@ #include <poll.h> #include <grpc/support/sync.h> +#include "src/core/iomgr/exec_ctx.h" +#include "src/core/iomgr/iomgr.h" #include "src/core/iomgr/wakeup_fd_posix.h" typedef struct grpc_pollset_vtable grpc_pollset_vtable; @@ -48,6 +50,8 @@ struct grpc_fd; typedef struct grpc_pollset_worker { grpc_wakeup_fd wakeup_fd; + int reevaluate_polling_on_wakeup; + int kicked_specifically; struct grpc_pollset_worker *next; struct grpc_pollset_worker *prev; } grpc_pollset_worker; @@ -64,8 +68,8 @@ typedef struct grpc_pollset { int shutting_down; int called_shutdown; int kicked_without_pollers; - void (*shutdown_done_cb)(void *arg); - void *shutdown_done_arg; + grpc_closure *shutdown_done; + grpc_closure_list idle_jobs; union { int fd; void *ptr; @@ -73,13 +77,13 @@ typedef struct grpc_pollset { } grpc_pollset; struct grpc_pollset_vtable { - void (*add_fd)(grpc_pollset *pollset, struct grpc_fd *fd, - int and_unlock_pollset); - void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd, - int and_unlock_pollset); - void (*maybe_work)(grpc_pollset *pollset, grpc_pollset_worker *worker, - gpr_timespec deadline, gpr_timespec now, - int allow_synchronous_callback); + void (*add_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + struct grpc_fd *fd, int and_unlock_pollset); + void (*del_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + struct grpc_fd *fd, int and_unlock_pollset); + void (*maybe_work_and_unlock)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *worker, + gpr_timespec deadline, gpr_timespec now); void (*finish_shutdown)(grpc_pollset *pollset); void (*destroy)(grpc_pollset *pollset); }; @@ -87,10 +91,12 @@ struct grpc_pollset_vtable { #define GRPC_POLLSET_MU(pollset) (&(pollset)->mu) /* Add an fd to a pollset */ -void grpc_pollset_add_fd(grpc_pollset *pollset, struct grpc_fd *fd); +void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + struct grpc_fd *fd); /* Force remove an fd from a pollset (normally they are removed on the next poll after an fd is orphaned) */ -void grpc_pollset_del_fd(grpc_pollset *pollset, struct grpc_fd *fd); +void grpc_pollset_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + struct grpc_fd *fd); /* Returns the fd to listen on for kicks */ int grpc_kick_read_fd(grpc_pollset *p); @@ -107,13 +113,25 @@ void grpc_kick_drain(grpc_pollset *p); int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline, gpr_timespec now); +/* Allow kick to wakeup the currently polling worker */ +#define GRPC_POLLSET_CAN_KICK_SELF 1 +/* Force the wakee to repoll when awoken */ +#define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2 +/* As per grpc_pollset_kick, with an extended set of flags (defined above) + -- mostly for fd_posix's use. */ +void grpc_pollset_kick_ext(grpc_pollset *p, + grpc_pollset_worker *specific_worker, + gpr_uint32 flags); + /* turn a pollset into a multipoller: platform specific */ -typedef void (*grpc_platform_become_multipoller_type)(grpc_pollset *pollset, +typedef void (*grpc_platform_become_multipoller_type)(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, struct grpc_fd **fds, size_t fd_count); extern grpc_platform_become_multipoller_type grpc_platform_become_multipoller; -void grpc_poll_become_multipoller(grpc_pollset *pollset, struct grpc_fd **fds, +void grpc_poll_become_multipoller(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, struct grpc_fd **fds, size_t fd_count); /* Return 1 if the pollset has active threads in grpc_pollset_work (pollset must @@ -123,5 +141,6 @@ int grpc_pollset_has_workers(grpc_pollset *pollset); /* override to allow tests to hook poll() usage */ typedef int (*grpc_poll_function_type)(struct pollfd *, nfds_t, int); extern grpc_poll_function_type grpc_poll_function; +extern grpc_wakeup_fd grpc_global_wakeup_fd; #endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_POSIX_H */ diff --git a/src/core/iomgr/pollset_set.h b/src/core/iomgr/pollset_set.h index 6d73951c70..0fdcba01a4 100644 --- a/src/core/iomgr/pollset_set.h +++ b/src/core/iomgr/pollset_set.h @@ -49,11 +49,13 @@ #include "src/core/iomgr/pollset_set_windows.h" #endif -void grpc_pollset_set_init(grpc_pollset_set *pollset_set); -void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set); -void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set, - grpc_pollset *pollset); -void grpc_pollset_set_del_pollset(grpc_pollset_set *pollset_set, - grpc_pollset *pollset); +void grpc_pollset_set_init(grpc_pollset_set* pollset_set); +void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set); +void grpc_pollset_set_add_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset); +void grpc_pollset_set_del_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset); #endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_H */ diff --git a/src/core/iomgr/pollset_set_posix.c b/src/core/iomgr/pollset_set_posix.c index 2076ac70ef..c86ed3d5da 100644 --- a/src/core/iomgr/pollset_set_posix.c +++ b/src/core/iomgr/pollset_set_posix.c @@ -58,7 +58,8 @@ void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set) { gpr_free(pollset_set->fds); } -void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set, +void grpc_pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_pollset *pollset) { size_t i, j; gpr_mu_lock(&pollset_set->mu); @@ -74,7 +75,7 @@ void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set, if (grpc_fd_is_orphaned(pollset_set->fds[i])) { GRPC_FD_UNREF(pollset_set->fds[i], "pollset"); } else { - grpc_pollset_add_fd(pollset, pollset_set->fds[i]); + grpc_pollset_add_fd(exec_ctx, pollset, pollset_set->fds[i]); pollset_set->fds[j++] = pollset_set->fds[i]; } } @@ -82,7 +83,8 @@ void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set, gpr_mu_unlock(&pollset_set->mu); } -void grpc_pollset_set_del_pollset(grpc_pollset_set *pollset_set, +void grpc_pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_pollset *pollset) { size_t i; gpr_mu_lock(&pollset_set->mu); @@ -97,7 +99,8 @@ void grpc_pollset_set_del_pollset(grpc_pollset_set *pollset_set, gpr_mu_unlock(&pollset_set->mu); } -void grpc_pollset_set_add_fd(grpc_pollset_set *pollset_set, grpc_fd *fd) { +void grpc_pollset_set_add_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd) { size_t i; gpr_mu_lock(&pollset_set->mu); if (pollset_set->fd_count == pollset_set->fd_capacity) { @@ -108,12 +111,13 @@ void grpc_pollset_set_add_fd(grpc_pollset_set *pollset_set, grpc_fd *fd) { GRPC_FD_REF(fd, "pollset_set"); pollset_set->fds[pollset_set->fd_count++] = fd; for (i = 0; i < pollset_set->pollset_count; i++) { - grpc_pollset_add_fd(pollset_set->pollsets[i], fd); + grpc_pollset_add_fd(exec_ctx, pollset_set->pollsets[i], fd); } gpr_mu_unlock(&pollset_set->mu); } -void grpc_pollset_set_del_fd(grpc_pollset_set *pollset_set, grpc_fd *fd) { +void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd) { size_t i; gpr_mu_lock(&pollset_set->mu); for (i = 0; i < pollset_set->fd_count; i++) { diff --git a/src/core/iomgr/pollset_set_posix.h b/src/core/iomgr/pollset_set_posix.h index e88740bde1..05234fb642 100644 --- a/src/core/iomgr/pollset_set_posix.h +++ b/src/core/iomgr/pollset_set_posix.h @@ -49,7 +49,9 @@ typedef struct grpc_pollset_set { grpc_fd **fds; } grpc_pollset_set; -void grpc_pollset_set_add_fd(grpc_pollset_set *pollset_set, grpc_fd *fd); -void grpc_pollset_set_del_fd(grpc_pollset_set *pollset_set, grpc_fd *fd); +void grpc_pollset_set_add_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd); +void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd); #endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H */ diff --git a/src/core/iomgr/pollset_set_windows.c b/src/core/iomgr/pollset_set_windows.c index b9c209cd2c..53d5d3dcd4 100644 --- a/src/core/iomgr/pollset_set_windows.c +++ b/src/core/iomgr/pollset_set_windows.c @@ -37,14 +37,16 @@ #include "src/core/iomgr/pollset_set.h" -void grpc_pollset_set_init(grpc_pollset_set *pollset_set) {} +void grpc_pollset_set_init(grpc_pollset_set* pollset_set) {} -void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set) {} +void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) {} -void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set, - grpc_pollset *pollset) {} +void grpc_pollset_set_add_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} -void grpc_pollset_set_del_pollset(grpc_pollset_set *pollset_set, - grpc_pollset *pollset) {} +void grpc_pollset_set_del_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} #endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/pollset_windows.c b/src/core/iomgr/pollset_windows.c index 07522c8a0c..9f74580273 100644 --- a/src/core/iomgr/pollset_windows.c +++ b/src/core/iomgr/pollset_windows.c @@ -37,40 +37,65 @@ #include <grpc/support/thd.h> -#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/timer_internal.h" #include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/iocp_windows.h" #include "src/core/iomgr/pollset.h" #include "src/core/iomgr/pollset_windows.h" -static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev->next = worker->next; - worker->next->prev = worker->prev; +gpr_mu grpc_polling_mu; +static grpc_pollset_worker *g_active_poller; +static grpc_pollset_worker g_global_root_worker; + +void grpc_pollset_global_init() { + gpr_mu_init(&grpc_polling_mu); + g_active_poller = NULL; + g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = + g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = + &g_global_root_worker; +} + +void grpc_pollset_global_shutdown() { gpr_mu_destroy(&grpc_polling_mu); } + +static void remove_worker(grpc_pollset_worker *worker, + grpc_pollset_worker_link_type type) { + worker->links[type].prev->links[type].next = worker->links[type].next; + worker->links[type].next->links[type].prev = worker->links[type].prev; + worker->links[type].next = worker->links[type].prev = worker; } -static int has_workers(grpc_pollset *p) { - return p->root_worker.next != &p->root_worker; +static int has_workers(grpc_pollset_worker *root, + grpc_pollset_worker_link_type type) { + return root->links[type].next != root; } -static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { - if (has_workers(p)) { - grpc_pollset_worker *w = p->root_worker.next; - remove_worker(p, w); +static grpc_pollset_worker *pop_front_worker( + grpc_pollset_worker *root, grpc_pollset_worker_link_type type) { + if (has_workers(root, type)) { + grpc_pollset_worker *w = root->links[type].next; + remove_worker(w, type); return w; } else { return NULL; } } -static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->next = &p->root_worker; - worker->prev = worker->next->prev; - worker->prev->next = worker->next->prev = worker; +static void push_back_worker(grpc_pollset_worker *root, + grpc_pollset_worker_link_type type, + grpc_pollset_worker *worker) { + worker->links[type].next = root; + worker->links[type].prev = worker->links[type].next->links[type].prev; + worker->links[type].prev->links[type].next = + worker->links[type].next->links[type].prev = worker; } -static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev = &p->root_worker; - worker->next = worker->prev->next; - worker->prev->next = worker->next->prev = worker; +static void push_front_worker(grpc_pollset_worker *root, + grpc_pollset_worker_link_type type, + grpc_pollset_worker *worker) { + worker->links[type].prev = root; + worker->links[type].next = worker->links[type].prev->links[type].next; + worker->links[type].prev->links[type].next = + worker->links[type].next->links[type].prev = worker; } /* There isn't really any such thing as a pollset under Windows, due to the @@ -80,71 +105,135 @@ static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { void grpc_pollset_init(grpc_pollset *pollset) { memset(pollset, 0, sizeof(*pollset)); - gpr_mu_init(&pollset->mu); - pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; - pollset->kicked_without_pollers = 0; + pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = + pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = + &pollset->root_worker; } -void grpc_pollset_shutdown(grpc_pollset *pollset, - void (*shutdown_done)(void *arg), - void *shutdown_done_arg) { - gpr_mu_lock(&pollset->mu); +void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + gpr_mu_lock(&grpc_polling_mu); pollset->shutting_down = 1; grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); - gpr_mu_unlock(&pollset->mu); - shutdown_done(shutdown_done_arg); + if (!pollset->is_iocp_worker) { + grpc_exec_ctx_enqueue(exec_ctx, closure, 1); + } else { + pollset->on_shutdown = closure; + } + gpr_mu_unlock(&grpc_polling_mu); } -void grpc_pollset_destroy(grpc_pollset *pollset) { - gpr_mu_destroy(&pollset->mu); -} +void grpc_pollset_destroy(grpc_pollset *pollset) {} -void grpc_pollset_work(grpc_pollset *pollset, grpc_pollset_worker *worker, - gpr_timespec now, gpr_timespec deadline) { +void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *worker, gpr_timespec now, + gpr_timespec deadline) { int added_worker = 0; - worker->next = worker->prev = NULL; + worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = + worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = + worker->links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = + worker->links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = NULL; + worker->kicked = 0; + worker->pollset = pollset; gpr_cv_init(&worker->cv); - if (grpc_maybe_call_delayed_callbacks(&pollset->mu, 1 /* GPR_TRUE */)) { - goto done; - } - if (grpc_alarm_check(&pollset->mu, now, &deadline)) { + if (grpc_timer_check(exec_ctx, now, &deadline)) { goto done; } if (!pollset->kicked_without_pollers && !pollset->shutting_down) { - push_front_worker(pollset, worker); + if (g_active_poller == NULL) { + grpc_pollset_worker *next_worker; + /* become poller */ + pollset->is_iocp_worker = 1; + g_active_poller = worker; + gpr_mu_unlock(&grpc_polling_mu); + grpc_iocp_work(exec_ctx, deadline); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&grpc_polling_mu); + pollset->is_iocp_worker = 0; + g_active_poller = NULL; + /* try to get a worker from this pollsets worker list */ + next_worker = pop_front_worker(&pollset->root_worker, + GRPC_POLLSET_WORKER_LINK_POLLSET); + if (next_worker == NULL) { + /* try to get a worker from the global list */ + next_worker = pop_front_worker(&g_global_root_worker, + GRPC_POLLSET_WORKER_LINK_GLOBAL); + } + if (next_worker != NULL) { + next_worker->kicked = 1; + gpr_cv_signal(&next_worker->cv); + } + + if (pollset->shutting_down && pollset->on_shutdown != NULL) { + grpc_exec_ctx_enqueue(exec_ctx, pollset->on_shutdown, 1); + pollset->on_shutdown = NULL; + } + goto done; + } + push_front_worker(&g_global_root_worker, GRPC_POLLSET_WORKER_LINK_GLOBAL, + worker); + push_front_worker(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET, + worker); added_worker = 1; - gpr_cv_wait(&worker->cv, &pollset->mu, deadline); + while (!worker->kicked) { + if (gpr_cv_wait(&worker->cv, &grpc_polling_mu, deadline)) { + break; + } + } } else { pollset->kicked_without_pollers = 0; } done: - gpr_cv_destroy(&worker->cv); + if (!grpc_closure_list_empty(exec_ctx->closure_list)) { + gpr_mu_unlock(&grpc_polling_mu); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&grpc_polling_mu); + } if (added_worker) { - remove_worker(pollset, worker); + remove_worker(worker, GRPC_POLLSET_WORKER_LINK_GLOBAL); + remove_worker(worker, GRPC_POLLSET_WORKER_LINK_POLLSET); } + gpr_cv_destroy(&worker->cv); } void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) { if (specific_worker != NULL) { if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { - for (specific_worker = p->root_worker.next; + for (specific_worker = + p->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next; specific_worker != &p->root_worker; - specific_worker = specific_worker->next) { + specific_worker = + specific_worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next) { + specific_worker->kicked = 1; gpr_cv_signal(&specific_worker->cv); } p->kicked_without_pollers = 1; + if (p->is_iocp_worker) { + grpc_iocp_kick(); + } } else { - gpr_cv_signal(&specific_worker->cv); + if (p->is_iocp_worker) { + if (g_active_poller == specific_worker) { + grpc_iocp_kick(); + } + } else { + specific_worker->kicked = 1; + gpr_cv_signal(&specific_worker->cv); + } } } else { - specific_worker = pop_front_worker(p); + specific_worker = + pop_front_worker(&p->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET); if (specific_worker != NULL) { - push_back_worker(p, specific_worker); - gpr_cv_signal(&specific_worker->cv); + grpc_pollset_kick(p, specific_worker); + } else if (p->is_iocp_worker) { + grpc_iocp_kick(); } else { p->kicked_without_pollers = 1; } } } +void grpc_kick_poller(void) { grpc_iocp_kick(); } + #endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/pollset_windows.h b/src/core/iomgr/pollset_windows.h index 4efa5a1717..65ba80619b 100644 --- a/src/core/iomgr/pollset_windows.h +++ b/src/core/iomgr/pollset_windows.h @@ -43,19 +43,37 @@ used to synchronize with the IOCP, and workers are condition variables used to block threads until work is ready. */ -typedef struct grpc_pollset_worker { - gpr_cv cv; +typedef enum { + GRPC_POLLSET_WORKER_LINK_POLLSET = 0, + GRPC_POLLSET_WORKER_LINK_GLOBAL, + GRPC_POLLSET_WORKER_LINK_TYPES +} grpc_pollset_worker_link_type; + +typedef struct grpc_pollset_worker_link { struct grpc_pollset_worker *next; struct grpc_pollset_worker *prev; +} grpc_pollset_worker_link; + +struct grpc_pollset; +typedef struct grpc_pollset grpc_pollset; + +typedef struct grpc_pollset_worker { + gpr_cv cv; + int kicked; + struct grpc_pollset *pollset; + grpc_pollset_worker_link links[GRPC_POLLSET_WORKER_LINK_TYPES]; } grpc_pollset_worker; -typedef struct grpc_pollset { - gpr_mu mu; +struct grpc_pollset { int shutting_down; int kicked_without_pollers; + int is_iocp_worker; grpc_pollset_worker root_worker; -} grpc_pollset; + grpc_closure *on_shutdown; +}; + +extern gpr_mu grpc_polling_mu; -#define GRPC_POLLSET_MU(pollset) (&(pollset)->mu) +#define GRPC_POLLSET_MU(pollset) (&grpc_polling_mu) #endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H */ diff --git a/src/core/iomgr/resolve_address.h b/src/core/iomgr/resolve_address.h index cc1bd428b0..01eedffa88 100644 --- a/src/core/iomgr/resolve_address.h +++ b/src/core/iomgr/resolve_address.h @@ -35,12 +35,14 @@ #define GRPC_INTERNAL_CORE_IOMGR_RESOLVE_ADDRESS_H #include <stddef.h> +#include "src/core/iomgr/exec_ctx.h" +#include "src/core/iomgr/iomgr.h" #define GRPC_MAX_SOCKADDR_SIZE 128 typedef struct { char addr[GRPC_MAX_SOCKADDR_SIZE]; - int len; + size_t len; } grpc_resolved_address; typedef struct { @@ -52,7 +54,8 @@ typedef struct { On success: addresses is the result, and the callee must call grpc_resolved_addresses_destroy when it's done with them On failure: addresses is NULL */ -typedef void (*grpc_resolve_cb)(void *arg, grpc_resolved_addresses *addresses); +typedef void (*grpc_resolve_cb)(grpc_exec_ctx *exec_ctx, void *arg, + grpc_resolved_addresses *addresses); /* Asynchronously resolve addr. Use default_port if a port isn't designated in addr, otherwise use the port in addr. */ /* TODO(ctiller): add a timeout here */ diff --git a/src/core/iomgr/resolve_address_posix.c b/src/core/iomgr/resolve_address_posix.c index ce6972b797..555c74ce7e 100644 --- a/src/core/iomgr/resolve_address_posix.c +++ b/src/core/iomgr/resolve_address_posix.c @@ -41,8 +41,10 @@ #include <sys/un.h> #include <string.h> +#include "src/core/iomgr/executor.h" #include "src/core/iomgr/iomgr_internal.h" #include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/support/block_annotate.h" #include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/host_port.h> @@ -50,13 +52,14 @@ #include <grpc/support/string_util.h> #include <grpc/support/thd.h> #include <grpc/support/time.h> +#include <grpc/support/useful.h> typedef struct { char *name; char *default_port; grpc_resolve_cb cb; + grpc_closure request_closure; void *arg; - grpc_iomgr_object iomgr_object; } request; grpc_resolved_addresses *grpc_blocking_resolve_address( @@ -102,14 +105,18 @@ grpc_resolved_addresses *grpc_blocking_resolve_address( hints.ai_socktype = SOCK_STREAM; /* stream socket */ hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + GRPC_SCHEDULING_START_BLOCKING_REGION; s = getaddrinfo(host, port, &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; + if (s != 0) { /* Retry if well-known service name is recognized */ char *svc[][2] = {{"http", "80"}, {"https", "443"}}; - int i; - for (i = 0; i < (int)(sizeof(svc) / sizeof(svc[0])); i++) { + for (i = 0; i < GPR_ARRAY_SIZE(svc); i++) { if (strcmp(port, svc[i][0]) == 0) { + GRPC_SCHEDULING_START_BLOCKING_REGION; s = getaddrinfo(host, svc[i][1], &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; break; } } @@ -143,8 +150,9 @@ done: return addrs; } -/* Thread function to asynch-ify grpc_blocking_resolve_address */ -static void do_request(void *rp) { +/* Callback to be passed to grpc_executor to asynch-ify + * grpc_blocking_resolve_address */ +static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, int success) { request *r = rp; grpc_resolved_addresses *resolved = grpc_blocking_resolve_address(r->name, r->default_port); @@ -152,8 +160,7 @@ static void do_request(void *rp) { grpc_resolve_cb cb = r->cb; gpr_free(r->name); gpr_free(r->default_port); - cb(arg, resolved); - grpc_iomgr_unregister_object(&r->iomgr_object); + cb(exec_ctx, arg, resolved); gpr_free(r); } @@ -165,17 +172,12 @@ void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { void grpc_resolve_address(const char *name, const char *default_port, grpc_resolve_cb cb, void *arg) { request *r = gpr_malloc(sizeof(request)); - gpr_thd_id id; - char *tmp; - gpr_asprintf(&tmp, "resolve_address:name='%s':default_port='%s'", name, - default_port); - grpc_iomgr_register_object(&r->iomgr_object, tmp); - gpr_free(tmp); + grpc_closure_init(&r->request_closure, do_request_thread, r); r->name = gpr_strdup(name); r->default_port = gpr_strdup(default_port); r->cb = cb; r->arg = arg; - gpr_thd_new(&id, do_request, r, NULL); + grpc_executor_enqueue(&r->request_closure, 1); } #endif diff --git a/src/core/iomgr/resolve_address_windows.c b/src/core/iomgr/resolve_address_windows.c index fb5fd0d4f6..007c855d10 100644 --- a/src/core/iomgr/resolve_address_windows.c +++ b/src/core/iomgr/resolve_address_windows.c @@ -40,12 +40,15 @@ #include <sys/types.h> #include <string.h> +#include "src/core/iomgr/executor.h" #include "src/core/iomgr/iomgr_internal.h" #include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/support/block_annotate.h" #include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/host_port.h> #include <grpc/support/log.h> +#include <grpc/support/log_win32.h> #include <grpc/support/string_util.h> #include <grpc/support/thd.h> #include <grpc/support/time.h> @@ -54,8 +57,8 @@ typedef struct { char *name; char *default_port; grpc_resolve_cb cb; + grpc_closure request_closure; void *arg; - grpc_iomgr_object iomgr_object; } request; grpc_resolved_addresses *grpc_blocking_resolve_address( @@ -88,9 +91,13 @@ grpc_resolved_addresses *grpc_blocking_resolve_address( hints.ai_socktype = SOCK_STREAM; /* stream socket */ hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + GRPC_SCHEDULING_START_BLOCKING_REGION; s = getaddrinfo(host, port, &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; if (s != 0) { - gpr_log(GPR_ERROR, "getaddrinfo: %s", gai_strerror(s)); + char *error_message = gpr_format_message(s); + gpr_log(GPR_ERROR, "getaddrinfo: %s", error_message); + gpr_free(error_message); goto done; } @@ -126,8 +133,9 @@ done: return addrs; } -/* Thread function to asynch-ify grpc_blocking_resolve_address */ -static void do_request(void *rp) { +/* Callback to be passed to grpc_executor to asynch-ify + * grpc_blocking_resolve_address */ +static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, int success) { request *r = rp; grpc_resolved_addresses *resolved = grpc_blocking_resolve_address(r->name, r->default_port); @@ -135,9 +143,8 @@ static void do_request(void *rp) { grpc_resolve_cb cb = r->cb; gpr_free(r->name); gpr_free(r->default_port); - grpc_iomgr_unregister_object(&r->iomgr_object); + cb(exec_ctx, arg, resolved); gpr_free(r); - cb(arg, resolved); } void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { @@ -148,16 +155,12 @@ void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { void grpc_resolve_address(const char *name, const char *default_port, grpc_resolve_cb cb, void *arg) { request *r = gpr_malloc(sizeof(request)); - gpr_thd_id id; - const char *label; - gpr_asprintf(&label, "resolve:%s", name); - grpc_iomgr_register_object(&r->iomgr_object, label); - gpr_free(label); + grpc_closure_init(&r->request_closure, do_request_thread, r); r->name = gpr_strdup(name); r->default_port = gpr_strdup(default_port); r->cb = cb; r->arg = arg; - gpr_thd_new(&id, do_request, r, NULL); + grpc_executor_enqueue(&r->request_closure, 1); } #endif diff --git a/src/core/iomgr/sockaddr_utils.c b/src/core/iomgr/sockaddr_utils.c index efdc480365..511a5d5c59 100644 --- a/src/core/iomgr/sockaddr_utils.c +++ b/src/core/iomgr/sockaddr_utils.c @@ -123,15 +123,17 @@ void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out, } void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out) { + GPR_ASSERT(port >= 0 && port < 65536); memset(wild_out, 0, sizeof(*wild_out)); wild_out->sin_family = AF_INET; - wild_out->sin_port = htons(port); + wild_out->sin_port = htons((gpr_uint16)port); } void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out) { + GPR_ASSERT(port >= 0 && port < 65536); memset(wild_out, 0, sizeof(*wild_out)); wild_out->sin6_family = AF_INET6; - wild_out->sin6_port = htons(port); + wild_out->sin6_port = htons((gpr_uint16)port); } int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, @@ -156,8 +158,10 @@ int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, ip = &addr6->sin6_addr; port = ntohs(addr6->sin6_port); } + /* Windows inet_ntop wants a mutable ip pointer */ if (ip != NULL && - inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) { + inet_ntop(addr->sa_family, (void *)ip, ntop_buf, sizeof(ntop_buf)) != + NULL) { ret = gpr_join_host_port(out, ntop_buf, port); } else { ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family); @@ -215,10 +219,12 @@ int grpc_sockaddr_get_port(const struct sockaddr *addr) { int grpc_sockaddr_set_port(const struct sockaddr *addr, int port) { switch (addr->sa_family) { case AF_INET: - ((struct sockaddr_in *)addr)->sin_port = htons(port); + GPR_ASSERT(port >= 0 && port < 65536); + ((struct sockaddr_in *)addr)->sin_port = htons((gpr_uint16)port); return 1; case AF_INET6: - ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); + GPR_ASSERT(port >= 0 && port < 65536); + ((struct sockaddr_in6 *)addr)->sin6_port = htons((gpr_uint16)port); return 1; default: gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_set_port", diff --git a/src/core/iomgr/socket_windows.c b/src/core/iomgr/socket_windows.c index 7d8421376b..fafb7b6622 100644 --- a/src/core/iomgr/socket_windows.c +++ b/src/core/iomgr/socket_windows.c @@ -35,8 +35,12 @@ #ifdef GPR_WINSOCK_SOCKET +#include <winsock2.h> +#include <mswsock.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include <grpc/support/log_win32.h> #include <grpc/support/string_util.h> #include "src/core/iomgr/iocp_windows.h" @@ -62,46 +66,31 @@ grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) { operations to abort them. We need to do that this way because of the various callsites of that function, which happens to be in various mutex hold states, and that'd be unsafe to call them directly. */ -int grpc_winsocket_shutdown(grpc_winsocket *winsocket) { - int callbacks_set = 0; - SOCKET socket; - gpr_mu_lock(&winsocket->state_mu); - socket = winsocket->socket; - if (winsocket->read_info.cb) { - callbacks_set++; - grpc_iomgr_closure_init(&winsocket->shutdown_closure, - winsocket->read_info.cb, - winsocket->read_info.opaque); - grpc_iomgr_add_delayed_callback(&winsocket->shutdown_closure, 0); - } - if (winsocket->write_info.cb) { - callbacks_set++; - grpc_iomgr_closure_init(&winsocket->shutdown_closure, - winsocket->write_info.cb, - winsocket->write_info.opaque); - grpc_iomgr_add_delayed_callback(&winsocket->shutdown_closure, 0); - } - gpr_mu_unlock(&winsocket->state_mu); - closesocket(socket); - return callbacks_set; -} +void grpc_winsocket_shutdown(grpc_winsocket *winsocket) { + /* Grab the function pointer for DisconnectEx for that specific socket. + It may change depending on the interface. */ + int status; + GUID guid = WSAID_DISCONNECTEX; + LPFN_DISCONNECTEX DisconnectEx; + DWORD ioctl_num_bytes; -/* Abandons a socket. Either we're going to queue it up for garbage collecting - from the IO Completion Port thread, or destroy it immediately. Note that this - mechanisms assumes that we're either always waiting for an operation, or we - explicitly know that we don't. If there is a future case where we can have - an "idle" socket which is neither trying to read or write, we'd start leaking - both memory and sockets. */ -void grpc_winsocket_orphan(grpc_winsocket *winsocket) { - grpc_iomgr_unregister_object(&winsocket->iomgr_object); - if (winsocket->read_info.outstanding || winsocket->write_info.outstanding) { - grpc_iocp_socket_orphan(winsocket); + status = WSAIoctl(winsocket->socket, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, sizeof(guid), &DisconnectEx, sizeof(DisconnectEx), + &ioctl_num_bytes, NULL, NULL); + + if (status == 0) { + DisconnectEx(winsocket->socket, NULL, 0, 0); } else { - grpc_winsocket_destroy(winsocket); + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "Unable to retrieve DisconnectEx pointer : %s", + utf8_message); + gpr_free(utf8_message); } + closesocket(winsocket->socket); } void grpc_winsocket_destroy(grpc_winsocket *winsocket) { + grpc_iomgr_unregister_object(&winsocket->iomgr_object); gpr_mu_destroy(&winsocket->state_mu); gpr_free(winsocket); } diff --git a/src/core/iomgr/socket_windows.h b/src/core/iomgr/socket_windows.h index ecf2530173..dfbfabe1f9 100644 --- a/src/core/iomgr/socket_windows.h +++ b/src/core/iomgr/socket_windows.h @@ -41,6 +41,7 @@ #include <grpc/support/atm.h> #include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/exec_ctx.h" /* This holds the data for an outstanding read or write on a socket. The mutex to protect the concurrent access to that data is the one @@ -54,8 +55,7 @@ typedef struct grpc_winsocket_callback_info { OVERLAPPED overlapped; /* The callback information for the pending operation. May be empty if the caller hasn't registered a callback yet. */ - void (*cb)(void *opaque, int success); - void *opaque; + grpc_closure *closure; /* A boolean to describe if the IO Completion Port got a notification for that operation. This will happen if the operation completed before the called had time to register a callback. We could avoid that behavior @@ -68,8 +68,6 @@ typedef struct grpc_winsocket_callback_info { /* The results of the overlapped operation. */ DWORD bytes_transfered; int wsa_error; - /* A boolean indicating that we started an operation. */ - int outstanding; } grpc_winsocket_callback_info; /* This is a wrapper to a Windows socket. A socket can have one outstanding @@ -92,12 +90,8 @@ typedef struct grpc_winsocket { /* You can't add the same socket twice to the same IO Completion Port. This prevents that. */ int added_to_iocp; - /* A boolean to indicate that the caller has abandoned that socket, but - there is a pending operation that the IO Completion Port will have to - wait for. The socket will be collected at that time. */ - int orphan; - grpc_iomgr_closure shutdown_closure; + grpc_closure shutdown_closure; /* A label for iomgr to track outstanding objects */ grpc_iomgr_object iomgr_object; @@ -108,14 +102,10 @@ typedef struct grpc_winsocket { grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name); /* Initiate an asynchronous shutdown of the socket. Will call off any pending - operation to cancel them. Returns the number of callbacks that got setup. */ -int grpc_winsocket_shutdown(grpc_winsocket *socket); + operation to cancel them. */ +void grpc_winsocket_shutdown(grpc_winsocket *socket); -/* Abandon a socket. */ -void grpc_winsocket_orphan(grpc_winsocket *socket); - -/* Destroy a socket. Should only be called by the IO Completion Port thread, - or by grpc_winsocket_orphan if there's no pending operation. */ +/* Destroy a socket. Should only be called if there's no pending operation. */ void grpc_winsocket_destroy(grpc_winsocket *socket); #endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H */ diff --git a/src/core/iomgr/tcp_client.h b/src/core/iomgr/tcp_client.h index 8ad9b818e1..5e18e71ca2 100644 --- a/src/core/iomgr/tcp_client.h +++ b/src/core/iomgr/tcp_client.h @@ -44,9 +44,10 @@ NULL on failure). interested_parties points to a set of pollsets that would be interested in this connection being established (in order to continue their work) */ -void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp), - void *arg, grpc_pollset_set *interested_parties, - const struct sockaddr *addr, int addr_len, +void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_connect, + grpc_endpoint **endpoint, + grpc_pollset_set *interested_parties, + const struct sockaddr *addr, size_t addr_len, gpr_timespec deadline); #endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_CLIENT_H */ diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c index 66027f87a0..abd6315ca1 100644 --- a/src/core/iomgr/tcp_client_posix.c +++ b/src/core/iomgr/tcp_client_posix.c @@ -42,7 +42,7 @@ #include <string.h> #include <unistd.h> -#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/timer.h" #include "src/core/iomgr/iomgr_posix.h" #include "src/core/iomgr/pollset_posix.h" #include "src/core/iomgr/sockaddr_utils.h" @@ -54,17 +54,19 @@ #include <grpc/support/string_util.h> #include <grpc/support/time.h> +extern int grpc_tcp_trace; + typedef struct { - void (*cb)(void *arg, grpc_endpoint *tcp); - void *cb_arg; gpr_mu mu; grpc_fd *fd; gpr_timespec deadline; - grpc_alarm alarm; + grpc_timer alarm; int refs; - grpc_iomgr_closure write_closure; + grpc_closure write_closure; grpc_pollset_set *interested_parties; char *addr_str; + grpc_endpoint **ep; + grpc_closure *closure; } async_connect; static int prepare_socket(const struct sockaddr *addr, int fd) { @@ -89,12 +91,16 @@ error: return 0; } -static void tc_on_alarm(void *acp, int success) { +static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, int success) { int done; async_connect *ac = acp; + if (grpc_tcp_trace) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: success=%d", ac->addr_str, + success); + } gpr_mu_lock(&ac->mu); if (ac->fd != NULL) { - grpc_fd_shutdown(ac->fd); + grpc_fd_shutdown(exec_ctx, ac->fd); } done = (--ac->refs == 0); gpr_mu_unlock(&ac->mu); @@ -105,24 +111,28 @@ static void tc_on_alarm(void *acp, int success) { } } -static void on_writable(void *acp, int success) { +static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, int success) { async_connect *ac = acp; int so_error = 0; socklen_t so_error_size; int err; int done; - grpc_endpoint *ep = NULL; - void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb; - void *cb_arg = ac->cb_arg; + grpc_endpoint **ep = ac->ep; + grpc_closure *closure = ac->closure; grpc_fd *fd; + if (grpc_tcp_trace) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: success=%d", + ac->addr_str, success); + } + gpr_mu_lock(&ac->mu); GPR_ASSERT(ac->fd); fd = ac->fd; ac->fd = NULL; gpr_mu_unlock(&ac->mu); - grpc_alarm_cancel(&ac->alarm); + grpc_timer_cancel(exec_ctx, &ac->alarm); gpr_mu_lock(&ac->mu); if (success) { @@ -131,7 +141,8 @@ static void on_writable(void *acp, int success) { err = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &so_error, &so_error_size); } while (err < 0 && errno == EINTR); if (err < 0) { - gpr_log(GPR_ERROR, "getsockopt(ERROR): %s", strerror(errno)); + gpr_log(GPR_ERROR, "failed to connect to '%s': getsockopt(ERROR): %s", + ac->addr_str, strerror(errno)); goto finish; } else if (so_error != 0) { if (so_error == ENOBUFS) { @@ -151,36 +162,41 @@ static void on_writable(void *acp, int success) { don't do that! */ gpr_log(GPR_ERROR, "kernel out of buffers"); gpr_mu_unlock(&ac->mu); - grpc_fd_notify_on_write(fd, &ac->write_closure); + grpc_fd_notify_on_write(exec_ctx, fd, &ac->write_closure); return; } else { switch (so_error) { case ECONNREFUSED: - gpr_log(GPR_ERROR, "socket error: connection refused"); + gpr_log( + GPR_ERROR, + "failed to connect to '%s': socket error: connection refused", + ac->addr_str); break; default: - gpr_log(GPR_ERROR, "socket error: %d", so_error); + gpr_log(GPR_ERROR, "failed to connect to '%s': socket error: %d", + ac->addr_str, so_error); break; } goto finish; } } else { - grpc_pollset_set_del_fd(ac->interested_parties, fd); - ep = grpc_tcp_create(fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, ac->addr_str); + grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); + *ep = grpc_tcp_create(fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, ac->addr_str); fd = NULL; goto finish; } } else { - gpr_log(GPR_ERROR, "on_writable failed during connect"); + gpr_log(GPR_ERROR, "failed to connect to '%s': timeout occurred", + ac->addr_str); goto finish; } - abort(); + GPR_UNREACHABLE_CODE(return ); finish: if (fd != NULL) { - grpc_pollset_set_del_fd(ac->interested_parties, fd); - grpc_fd_orphan(fd, NULL, "tcp_client_orphan"); + grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); + grpc_fd_orphan(exec_ctx, fd, NULL, "tcp_client_orphan"); fd = NULL; } done = (--ac->refs == 0); @@ -190,12 +206,13 @@ finish: gpr_free(ac->addr_str); gpr_free(ac); } - cb(cb_arg, ep); + grpc_exec_ctx_enqueue(exec_ctx, closure, *ep != NULL); } -void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), - void *arg, grpc_pollset_set *interested_parties, - const struct sockaddr *addr, int addr_len, +void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_endpoint **ep, + grpc_pollset_set *interested_parties, + const struct sockaddr *addr, size_t addr_len, gpr_timespec deadline) { int fd; grpc_dualstack_mode dsmode; @@ -207,6 +224,8 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), char *name; char *addr_str; + *ep = NULL; + /* Use dualstack sockets where available. */ if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { addr = (const struct sockaddr *)&addr6_v4mapped; @@ -224,12 +243,13 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), addr_len = sizeof(addr4_copy); } if (!prepare_socket(addr, fd)) { - cb(arg, NULL); + grpc_exec_ctx_enqueue(exec_ctx, closure, 0); return; } do { - err = connect(fd, addr, addr_len); + GPR_ASSERT(addr_len < ~(socklen_t)0); + err = connect(fd, addr, (socklen_t)addr_len); } while (err < 0 && errno == EINTR); addr_str = grpc_sockaddr_to_uri(addr); @@ -238,22 +258,23 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), fdobj = grpc_fd_create(fd, name); if (err >= 0) { - cb(arg, grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str)); + *ep = grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str); + grpc_exec_ctx_enqueue(exec_ctx, closure, 1); goto done; } if (errno != EWOULDBLOCK && errno != EINPROGRESS) { gpr_log(GPR_ERROR, "connect error to '%s': %s", addr_str, strerror(errno)); - grpc_fd_orphan(fdobj, NULL, "tcp_client_connect_error"); - cb(arg, NULL); + grpc_fd_orphan(exec_ctx, fdobj, NULL, "tcp_client_connect_error"); + grpc_exec_ctx_enqueue(exec_ctx, closure, 0); goto done; } - grpc_pollset_set_add_fd(interested_parties, fdobj); + grpc_pollset_set_add_fd(exec_ctx, interested_parties, fdobj); ac = gpr_malloc(sizeof(async_connect)); - ac->cb = cb; - ac->cb_arg = arg; + ac->closure = closure; + ac->ep = ep; ac->fd = fdobj; ac->interested_parties = interested_parties; ac->addr_str = addr_str; @@ -263,11 +284,16 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), ac->write_closure.cb = on_writable; ac->write_closure.cb_arg = ac; + if (grpc_tcp_trace) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting", + ac->addr_str); + } + gpr_mu_lock(&ac->mu); - grpc_alarm_init(&ac->alarm, + grpc_timer_init(exec_ctx, &ac->alarm, gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), tc_on_alarm, ac, gpr_now(GPR_CLOCK_MONOTONIC)); - grpc_fd_notify_on_write(ac->fd, &ac->write_closure); + grpc_fd_notify_on_write(exec_ctx, ac->fd, &ac->write_closure); gpr_mu_unlock(&ac->mu); done: diff --git a/src/core/iomgr/tcp_client_windows.c b/src/core/iomgr/tcp_client_windows.c index 79a58fe2af..e5691b7e12 100644 --- a/src/core/iomgr/tcp_client_windows.c +++ b/src/core/iomgr/tcp_client_windows.c @@ -43,7 +43,7 @@ #include <grpc/support/slice_buffer.h> #include <grpc/support/useful.h> -#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/timer.h" #include "src/core/iomgr/iocp_windows.h" #include "src/core/iomgr/tcp_client.h" #include "src/core/iomgr/tcp_windows.h" @@ -52,96 +52,77 @@ #include "src/core/iomgr/socket_windows.h" typedef struct { - void (*cb)(void *arg, grpc_endpoint *tcp); - void *cb_arg; + grpc_closure *on_done; gpr_mu mu; grpc_winsocket *socket; gpr_timespec deadline; - grpc_alarm alarm; + grpc_timer alarm; char *addr_name; int refs; - int aborted; + grpc_closure on_connect; + grpc_endpoint **endpoint; } async_connect; -static void async_connect_cleanup(async_connect *ac) { +static void async_connect_unlock_and_cleanup(async_connect *ac) { int done = (--ac->refs == 0); gpr_mu_unlock(&ac->mu); if (done) { + if (ac->socket != NULL) grpc_winsocket_destroy(ac->socket); gpr_mu_destroy(&ac->mu); gpr_free(ac->addr_name); gpr_free(ac); } } -static void on_alarm(void *acp, int occured) { +static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, int occured) { async_connect *ac = acp; gpr_mu_lock(&ac->mu); /* If the alarm didn't occur, it got cancelled. */ if (ac->socket != NULL && occured) { grpc_winsocket_shutdown(ac->socket); } - async_connect_cleanup(ac); + async_connect_unlock_and_cleanup(ac); } -static void on_connect(void *acp, int from_iocp) { +static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, int from_iocp) { async_connect *ac = acp; SOCKET sock = ac->socket->socket; - grpc_endpoint *ep = NULL; + grpc_endpoint **ep = ac->endpoint; grpc_winsocket_callback_info *info = &ac->socket->write_info; - void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb; - void *cb_arg = ac->cb_arg; - int aborted; + grpc_closure *on_done = ac->on_done; - grpc_alarm_cancel(&ac->alarm); + grpc_timer_cancel(exec_ctx, &ac->alarm); gpr_mu_lock(&ac->mu); - aborted = ac->aborted; if (from_iocp) { DWORD transfered_bytes = 0; DWORD flags; BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, &transfered_bytes, FALSE, &flags); - info->outstanding = 0; GPR_ASSERT(transfered_bytes == 0); if (!wsa_success) { char *utf8_message = gpr_format_message(WSAGetLastError()); gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); gpr_free(utf8_message); - } else if (!aborted) { - ep = grpc_tcp_create(ac->socket, ac->addr_name); + } else { + *ep = grpc_tcp_create(ac->socket, ac->addr_name); + ac->socket = NULL; } - } else { - gpr_log(GPR_ERROR, "on_connect is shutting down"); - /* If the connection timeouts, we will still get a notification from - the IOCP whatever happens. So we're just going to flag that connection - as being in the process of being aborted, and wait for the IOCP. We - can't just orphan the socket now, because the IOCP might already have - gotten a successful connection, which is our worst-case scenario. - We need to call our callback now to respect the deadline. */ - ac->aborted = 1; - gpr_mu_unlock(&ac->mu); - cb(cb_arg, NULL); - return; } - ac->socket->write_info.outstanding = 0; - - /* If we don't have an endpoint, it means the connection failed, - so it doesn't matter if it aborted or failed. We need to orphan - that socket. */ - if (!ep || aborted) grpc_winsocket_orphan(ac->socket); - async_connect_cleanup(ac); + async_connect_unlock_and_cleanup(ac); /* If the connection was aborted, the callback was already called when the deadline was met. */ - if (!aborted) cb(cb_arg, ep); + on_done->cb(exec_ctx, on_done->cb_arg, *ep != NULL); } /* Tries to issue one async connection, then schedules both an IOCP notification request for the connection, and one timeout alert. */ -void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp), - void *arg, grpc_pollset_set *interested_parties, - const struct sockaddr *addr, int addr_len, +void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done, + grpc_endpoint **endpoint, + grpc_pollset_set *interested_parties, + const struct sockaddr *addr, size_t addr_len, gpr_timespec deadline) { SOCKET sock = INVALID_SOCKET; BOOL success; @@ -157,6 +138,8 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp), char *utf8_message; grpc_winsocket_callback_info *info; + *endpoint = NULL; + /* Use dualstack sockets where available. */ if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { addr = (const struct sockaddr *)&addr6_v4mapped; @@ -196,8 +179,8 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp), socket = grpc_winsocket_create(sock, "client"); info = &socket->write_info; - info->outstanding = 1; - success = ConnectEx(sock, addr, addr_len, NULL, 0, NULL, &info->overlapped); + success = + ConnectEx(sock, addr, (int)addr_len, NULL, 0, NULL, &info->overlapped); /* It wouldn't be unusual to get a success immediately. But we'll still get an IOCP notification, so let's ignore it. */ @@ -210,30 +193,29 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp), } ac = gpr_malloc(sizeof(async_connect)); - ac->cb = cb; - ac->cb_arg = arg; + ac->on_done = on_done; ac->socket = socket; gpr_mu_init(&ac->mu); ac->refs = 2; ac->addr_name = grpc_sockaddr_to_uri(addr); - ac->aborted = 0; + ac->endpoint = endpoint; + grpc_closure_init(&ac->on_connect, on_connect, ac); - grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, + grpc_timer_init(exec_ctx, &ac->alarm, deadline, on_alarm, ac, gpr_now(GPR_CLOCK_MONOTONIC)); - socket->write_info.outstanding = 1; - grpc_socket_notify_on_write(socket, on_connect, ac); + grpc_socket_notify_on_write(exec_ctx, socket, &ac->on_connect); return; failure: utf8_message = gpr_format_message(WSAGetLastError()); gpr_log(GPR_ERROR, message, utf8_message); gpr_free(utf8_message); - if (socket) { - grpc_winsocket_orphan(socket); + if (socket != NULL) { + grpc_winsocket_destroy(socket); } else if (sock != INVALID_SOCKET) { closesocket(sock); } - cb(arg, NULL); + grpc_exec_ctx_enqueue(exec_ctx, on_done, 0); } #endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c index 360e6ebd8c..915553d509 100644 --- a/src/core/iomgr/tcp_posix.c +++ b/src/core/iomgr/tcp_posix.c @@ -61,292 +61,135 @@ #define SENDMSG_FLAGS 0 #endif -/* Holds a slice array and associated state. */ -typedef struct grpc_tcp_slice_state { - gpr_slice *slices; /* Array of slices */ - size_t nslices; /* Size of slices array. */ - ssize_t first_slice; /* First valid slice in array */ - ssize_t last_slice; /* Last valid slice in array */ - gpr_slice working_slice; /* pointer to original final slice */ - int working_slice_valid; /* True if there is a working slice */ - int memory_owned; /* True if slices array is owned */ -} grpc_tcp_slice_state; +#ifdef GPR_MSG_IOVLEN_TYPE +typedef GPR_MSG_IOVLEN_TYPE msg_iovlen_type; +#else +typedef size_t msg_iovlen_type; +#endif int grpc_tcp_trace = 0; -static void slice_state_init(grpc_tcp_slice_state *state, gpr_slice *slices, - size_t nslices, size_t valid_slices) { - state->slices = slices; - state->nslices = nslices; - if (valid_slices == 0) { - state->first_slice = -1; - } else { - state->first_slice = 0; - } - state->last_slice = valid_slices - 1; - state->working_slice_valid = 0; - state->memory_owned = 0; -} - -/* Returns true if there is still available data */ -static int slice_state_has_available(grpc_tcp_slice_state *state) { - return state->first_slice != -1 && state->last_slice >= state->first_slice; -} - -static ssize_t slice_state_slices_allocated(grpc_tcp_slice_state *state) { - if (state->first_slice == -1) { - return 0; - } else { - return state->last_slice - state->first_slice + 1; - } -} - -static void slice_state_realloc(grpc_tcp_slice_state *state, size_t new_size) { - /* TODO(klempner): use realloc instead when first_slice is 0 */ - /* TODO(klempner): Avoid a realloc in cases where it is unnecessary */ - gpr_slice *slices = state->slices; - size_t original_size = slice_state_slices_allocated(state); - size_t i; - gpr_slice *new_slices = gpr_malloc(sizeof(gpr_slice) * new_size); - - for (i = 0; i < original_size; ++i) { - new_slices[i] = slices[i + state->first_slice]; - } - - state->slices = new_slices; - state->last_slice = original_size - 1; - if (original_size > 0) { - state->first_slice = 0; - } else { - state->first_slice = -1; - } - state->nslices = new_size; - - if (state->memory_owned) { - gpr_free(slices); - } - state->memory_owned = 1; -} - -static void slice_state_remove_prefix(grpc_tcp_slice_state *state, - size_t prefix_bytes) { - gpr_slice *current_slice = &state->slices[state->first_slice]; - size_t current_slice_size; - - while (slice_state_has_available(state)) { - current_slice_size = GPR_SLICE_LENGTH(*current_slice); - if (current_slice_size > prefix_bytes) { - /* TODO(klempner): Get rid of the extra refcount created here by adding a - native "trim the first N bytes" operation to splice */ - /* TODO(klempner): This really shouldn't be modifying the current slice - unless we own the slices array. */ - gpr_slice tail; - tail = gpr_slice_split_tail(current_slice, prefix_bytes); - gpr_slice_unref(*current_slice); - *current_slice = tail; - return; - } else { - gpr_slice_unref(*current_slice); - ++state->first_slice; - ++current_slice; - prefix_bytes -= current_slice_size; - } - } -} - -static void slice_state_destroy(grpc_tcp_slice_state *state) { - while (slice_state_has_available(state)) { - gpr_slice_unref(state->slices[state->first_slice]); - ++state->first_slice; - } - - if (state->memory_owned) { - gpr_free(state->slices); - state->memory_owned = 0; - } -} - -void slice_state_transfer_ownership(grpc_tcp_slice_state *state, - gpr_slice **slices, size_t *nslices) { - *slices = state->slices + state->first_slice; - *nslices = state->last_slice - state->first_slice + 1; - - state->first_slice = -1; - state->last_slice = -1; -} - -/* Fills iov with the first min(iov_size, available) slices, returns number - filled */ -static size_t slice_state_to_iovec(grpc_tcp_slice_state *state, - struct iovec *iov, size_t iov_size) { - size_t nslices = state->last_slice - state->first_slice + 1; - gpr_slice *slices = state->slices + state->first_slice; - size_t i; - if (nslices < iov_size) { - iov_size = nslices; - } - - for (i = 0; i < iov_size; ++i) { - iov[i].iov_base = GPR_SLICE_START_PTR(slices[i]); - iov[i].iov_len = GPR_SLICE_LENGTH(slices[i]); - } - return iov_size; -} - -/* Makes n blocks available at the end of state, writes them into iov, and - returns the number of bytes allocated */ -static size_t slice_state_append_blocks_into_iovec(grpc_tcp_slice_state *state, - struct iovec *iov, size_t n, - size_t slice_size) { - size_t target_size; - size_t i; - size_t allocated_bytes; - ssize_t allocated_slices = slice_state_slices_allocated(state); - - if (n - state->working_slice_valid >= state->nslices - state->last_slice) { - /* Need to grow the slice array */ - target_size = state->nslices; - do { - target_size = target_size * 2; - } while (target_size < allocated_slices + n - state->working_slice_valid); - /* TODO(klempner): If this ever needs to support both prefix removal and - append, we should be smarter about the growth logic here */ - slice_state_realloc(state, target_size); - } - - i = 0; - allocated_bytes = 0; - - if (state->working_slice_valid) { - iov[0].iov_base = GPR_SLICE_END_PTR(state->slices[state->last_slice]); - iov[0].iov_len = GPR_SLICE_LENGTH(state->working_slice) - - GPR_SLICE_LENGTH(state->slices[state->last_slice]); - allocated_bytes += iov[0].iov_len; - ++i; - state->slices[state->last_slice] = state->working_slice; - state->working_slice_valid = 0; - } - - for (; i < n; ++i) { - ++state->last_slice; - state->slices[state->last_slice] = gpr_slice_malloc(slice_size); - iov[i].iov_base = GPR_SLICE_START_PTR(state->slices[state->last_slice]); - iov[i].iov_len = slice_size; - allocated_bytes += slice_size; - } - if (state->first_slice == -1) { - state->first_slice = 0; - } - return allocated_bytes; -} - -/* Remove the last n bytes from state */ -/* TODO(klempner): Consider having this defer actual deletion until later */ -static void slice_state_remove_last(grpc_tcp_slice_state *state, size_t bytes) { - while (bytes > 0 && slice_state_has_available(state)) { - if (GPR_SLICE_LENGTH(state->slices[state->last_slice]) > bytes) { - state->working_slice = state->slices[state->last_slice]; - state->working_slice_valid = 1; - /* TODO(klempner): Combine these into a single operation that doesn't need - to refcount */ - gpr_slice_unref(gpr_slice_split_tail( - &state->slices[state->last_slice], - GPR_SLICE_LENGTH(state->slices[state->last_slice]) - bytes)); - bytes = 0; - } else { - bytes -= GPR_SLICE_LENGTH(state->slices[state->last_slice]); - gpr_slice_unref(state->slices[state->last_slice]); - --state->last_slice; - if (state->last_slice == -1) { - state->first_slice = -1; - } - } - } -} - typedef struct { grpc_endpoint base; grpc_fd *em_fd; int fd; - int iov_size; /* Number of slices to allocate per read attempt */ int finished_edge; + msg_iovlen_type iov_size; /* Number of slices to allocate per read attempt */ size_t slice_size; gpr_refcount refcount; - grpc_endpoint_read_cb read_cb; - void *read_user_data; - grpc_endpoint_write_cb write_cb; - void *write_user_data; + /* garbage after the last read */ + gpr_slice_buffer last_read_buffer; - grpc_tcp_slice_state write_state; + gpr_slice_buffer *incoming_buffer; + gpr_slice_buffer *outgoing_buffer; + /** slice within outgoing_buffer to write next */ + size_t outgoing_slice_idx; + /** byte within outgoing_buffer->slices[outgoing_slice_idx] to write next */ + size_t outgoing_byte_idx; - grpc_iomgr_closure read_closure; - grpc_iomgr_closure write_closure; + grpc_closure *read_cb; + grpc_closure *write_cb; - grpc_iomgr_closure handle_read_closure; + grpc_closure read_closure; + grpc_closure write_closure; char *peer_string; } grpc_tcp; -static void grpc_tcp_handle_read(void *arg /* grpc_tcp */, int success); -static void grpc_tcp_handle_write(void *arg /* grpc_tcp */, int success); +static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + int success); +static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + int success); -static void grpc_tcp_shutdown(grpc_endpoint *ep) { +static void tcp_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_fd_shutdown(tcp->em_fd); + grpc_fd_shutdown(exec_ctx, tcp->em_fd); } -static void grpc_tcp_unref(grpc_tcp *tcp) { - int refcount_zero = gpr_unref(&tcp->refcount); - if (refcount_zero) { - grpc_fd_orphan(tcp->em_fd, NULL, "tcp_unref_orphan"); - gpr_free(tcp->peer_string); - gpr_free(tcp); +static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + grpc_fd_orphan(exec_ctx, tcp->em_fd, NULL, "tcp_unref_orphan"); + gpr_slice_buffer_destroy(&tcp->last_read_buffer); + gpr_free(tcp->peer_string); + gpr_free(tcp); +} + +/*#define GRPC_TCP_REFCOUNT_DEBUG*/ +#ifdef GRPC_TCP_REFCOUNT_DEBUG +#define TCP_UNREF(cl, tcp, reason) \ + tcp_unref((cl), (tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, + const char *reason, const char *file, int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP unref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count - 1); + if (gpr_unref(&tcp->refcount)) { + tcp_free(exec_ctx, tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP ref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count + 1); + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(cl, tcp, reason) tcp_unref((cl), (tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(exec_ctx, tcp); } } -static void grpc_tcp_destroy(grpc_endpoint *ep) { +static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +#endif + +static void tcp_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_tcp_unref(tcp); + TCP_UNREF(exec_ctx, tcp, "destroy"); } -static void call_read_cb(grpc_tcp *tcp, gpr_slice *slices, size_t nslices, - grpc_endpoint_cb_status status) { - grpc_endpoint_read_cb cb = tcp->read_cb; +static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, int success) { + grpc_closure *cb = tcp->read_cb; if (grpc_tcp_trace) { size_t i; - gpr_log(GPR_DEBUG, "read: status=%d", status); - for (i = 0; i < nslices; i++) { - char *dump = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "read: success=%d", success); + for (i = 0; i < tcp->incoming_buffer->count; i++) { + char *dump = gpr_dump_slice(tcp->incoming_buffer->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "READ %p: %s", tcp, dump); gpr_free(dump); } } tcp->read_cb = NULL; - cb(tcp->read_user_data, slices, nslices, status); + tcp->incoming_buffer = NULL; + cb->cb(exec_ctx, cb->cb_arg, success); } -#define INLINE_SLICE_BUFFER_SIZE 8 #define MAX_READ_IOVEC 4 -static void grpc_tcp_continue_read(grpc_tcp *tcp) { - gpr_slice static_read_slices[INLINE_SLICE_BUFFER_SIZE]; +static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { struct msghdr msg; struct iovec iov[MAX_READ_IOVEC]; ssize_t read_bytes; - ssize_t allocated_bytes; - struct grpc_tcp_slice_state read_state; - gpr_slice *final_slices; - size_t final_nslices; + size_t i; GPR_ASSERT(!tcp->finished_edge); - GRPC_TIMER_BEGIN(GRPC_PTAG_HANDLE_READ, 0); - slice_state_init(&read_state, static_read_slices, INLINE_SLICE_BUFFER_SIZE, - 0); + GPR_ASSERT(tcp->iov_size <= MAX_READ_IOVEC); + GPR_ASSERT(tcp->incoming_buffer->count <= MAX_READ_IOVEC); + GPR_TIMER_BEGIN("tcp_continue_read", 0); - allocated_bytes = slice_state_append_blocks_into_iovec( - &read_state, iov, tcp->iov_size, tcp->slice_size); + while (tcp->incoming_buffer->count < (size_t)tcp->iov_size) { + gpr_slice_buffer_add_indexed(tcp->incoming_buffer, + gpr_slice_malloc(tcp->slice_size)); + } + for (i = 0; i < tcp->incoming_buffer->count; i++) { + iov[i].iov_base = GPR_SLICE_START_PTR(tcp->incoming_buffer->slices[i]); + iov[i].iov_len = GPR_SLICE_LENGTH(tcp->incoming_buffer->slices[i]); + } msg.msg_name = NULL; msg.msg_namelen = 0; @@ -356,112 +199,114 @@ static void grpc_tcp_continue_read(grpc_tcp *tcp) { msg.msg_controllen = 0; msg.msg_flags = 0; - GRPC_TIMER_BEGIN(GRPC_PTAG_RECVMSG, 0); + GPR_TIMER_BEGIN("recvmsg", 1); do { read_bytes = recvmsg(tcp->fd, &msg, 0); } while (read_bytes < 0 && errno == EINTR); - GRPC_TIMER_END(GRPC_PTAG_RECVMSG, 0); - - if (read_bytes < allocated_bytes) { - /* TODO(klempner): Consider a second read first, in hopes of getting a - * quick EAGAIN and saving a bunch of allocations. */ - slice_state_remove_last(&read_state, read_bytes < 0 - ? allocated_bytes - : allocated_bytes - read_bytes); - } + GPR_TIMER_END("recvmsg", 0); if (read_bytes < 0) { - /* NB: After calling the user_cb a parallel call of the read handler may + /* NB: After calling call_read_cb a parallel call of the read handler may * be running. */ if (errno == EAGAIN) { if (tcp->iov_size > 1) { tcp->iov_size /= 2; } - if (slice_state_has_available(&read_state)) { - /* TODO(klempner): We should probably do the call into the application - without all this junk on the stack */ - /* FIXME(klempner): Refcount properly */ - slice_state_transfer_ownership(&read_state, &final_slices, - &final_nslices); - tcp->finished_edge = 1; - call_read_cb(tcp, final_slices, final_nslices, GRPC_ENDPOINT_CB_OK); - slice_state_destroy(&read_state); - grpc_tcp_unref(tcp); - } else { - /* We've consumed the edge, request a new one */ - slice_state_destroy(&read_state); - grpc_fd_notify_on_read(tcp->em_fd, &tcp->read_closure); - } + /* We've consumed the edge, request a new one */ + grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure); } else { /* TODO(klempner): Log interesting errors */ - call_read_cb(tcp, NULL, 0, GRPC_ENDPOINT_CB_ERROR); - slice_state_destroy(&read_state); - grpc_tcp_unref(tcp); + gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer); + call_read_cb(exec_ctx, tcp, 0); + TCP_UNREF(exec_ctx, tcp, "read"); } } else if (read_bytes == 0) { /* 0 read size ==> end of stream */ - if (slice_state_has_available(&read_state)) { - /* there were bytes already read: pass them up to the application */ - slice_state_transfer_ownership(&read_state, &final_slices, - &final_nslices); - call_read_cb(tcp, final_slices, final_nslices, GRPC_ENDPOINT_CB_EOF); - } else { - call_read_cb(tcp, NULL, 0, GRPC_ENDPOINT_CB_EOF); - } - slice_state_destroy(&read_state); - grpc_tcp_unref(tcp); + gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer); + call_read_cb(exec_ctx, tcp, 0); + TCP_UNREF(exec_ctx, tcp, "read"); } else { - if (tcp->iov_size < MAX_READ_IOVEC) { + GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length); + if ((size_t)read_bytes < tcp->incoming_buffer->length) { + gpr_slice_buffer_trim_end( + tcp->incoming_buffer, + tcp->incoming_buffer->length - (size_t)read_bytes, + &tcp->last_read_buffer); + } else if (tcp->iov_size < MAX_READ_IOVEC) { ++tcp->iov_size; } - GPR_ASSERT(slice_state_has_available(&read_state)); - slice_state_transfer_ownership(&read_state, &final_slices, &final_nslices); - call_read_cb(tcp, final_slices, final_nslices, GRPC_ENDPOINT_CB_OK); - slice_state_destroy(&read_state); - grpc_tcp_unref(tcp); + GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length); + call_read_cb(exec_ctx, tcp, 1); + TCP_UNREF(exec_ctx, tcp, "read"); } - GRPC_TIMER_END(GRPC_PTAG_HANDLE_READ, 0); + GPR_TIMER_END("tcp_continue_read", 0); } -static void grpc_tcp_handle_read(void *arg /* grpc_tcp */, int success) { +static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + int success) { grpc_tcp *tcp = (grpc_tcp *)arg; GPR_ASSERT(!tcp->finished_edge); if (!success) { - call_read_cb(tcp, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN); - grpc_tcp_unref(tcp); + gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer); + call_read_cb(exec_ctx, tcp, 0); + TCP_UNREF(exec_ctx, tcp, "read"); } else { - grpc_tcp_continue_read(tcp); + tcp_continue_read(exec_ctx, tcp); } } -static void grpc_tcp_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, - void *user_data) { +static void tcp_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *incoming_buffer, grpc_closure *cb) { grpc_tcp *tcp = (grpc_tcp *)ep; GPR_ASSERT(tcp->read_cb == NULL); tcp->read_cb = cb; - tcp->read_user_data = user_data; - gpr_ref(&tcp->refcount); + tcp->incoming_buffer = incoming_buffer; + gpr_slice_buffer_reset_and_unref(incoming_buffer); + gpr_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer); + TCP_REF(tcp, "read"); if (tcp->finished_edge) { tcp->finished_edge = 0; - grpc_fd_notify_on_read(tcp->em_fd, &tcp->read_closure); + grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure); } else { - tcp->handle_read_closure.cb_arg = tcp; - grpc_iomgr_add_delayed_callback(&tcp->handle_read_closure, 1); + grpc_exec_ctx_enqueue(exec_ctx, &tcp->read_closure, 1); } } +typedef enum { FLUSH_DONE, FLUSH_PENDING, FLUSH_ERROR } flush_result; + #define MAX_WRITE_IOVEC 16 -static grpc_endpoint_write_status grpc_tcp_flush(grpc_tcp *tcp) { +static flush_result tcp_flush(grpc_tcp *tcp) { struct msghdr msg; struct iovec iov[MAX_WRITE_IOVEC]; - int iov_size; + msg_iovlen_type iov_size; ssize_t sent_length; - grpc_tcp_slice_state *state = &tcp->write_state; + size_t sending_length; + size_t trailing; + size_t unwind_slice_idx; + size_t unwind_byte_idx; for (;;) { - iov_size = slice_state_to_iovec(state, iov, MAX_WRITE_IOVEC); + sending_length = 0; + unwind_slice_idx = tcp->outgoing_slice_idx; + unwind_byte_idx = tcp->outgoing_byte_idx; + for (iov_size = 0; tcp->outgoing_slice_idx != tcp->outgoing_buffer->count && + iov_size != MAX_WRITE_IOVEC; + iov_size++) { + iov[iov_size].iov_base = + GPR_SLICE_START_PTR( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) + + tcp->outgoing_byte_idx; + iov[iov_size].iov_len = + GPR_SLICE_LENGTH( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) - + tcp->outgoing_byte_idx; + sending_length += iov[iov_size].iov_len; + tcp->outgoing_slice_idx++; + tcp->outgoing_byte_idx = 0; + } + GPR_ASSERT(iov_size > 0); msg.msg_name = NULL; msg.msg_namelen = 0; @@ -471,123 +316,133 @@ static grpc_endpoint_write_status grpc_tcp_flush(grpc_tcp *tcp) { msg.msg_controllen = 0; msg.msg_flags = 0; - GRPC_TIMER_BEGIN(GRPC_PTAG_SENDMSG, 0); + GPR_TIMER_BEGIN("sendmsg", 1); do { /* TODO(klempner): Cork if this is a partial write */ sent_length = sendmsg(tcp->fd, &msg, SENDMSG_FLAGS); } while (sent_length < 0 && errno == EINTR); - GRPC_TIMER_END(GRPC_PTAG_SENDMSG, 0); + GPR_TIMER_END("sendmsg", 0); if (sent_length < 0) { if (errno == EAGAIN) { - return GRPC_ENDPOINT_WRITE_PENDING; + tcp->outgoing_slice_idx = unwind_slice_idx; + tcp->outgoing_byte_idx = unwind_byte_idx; + return FLUSH_PENDING; } else { /* TODO(klempner): Log some of these */ - slice_state_destroy(state); - return GRPC_ENDPOINT_WRITE_ERROR; + return FLUSH_ERROR; } } - /* TODO(klempner): Probably better to batch this after we finish flushing */ - slice_state_remove_prefix(state, sent_length); + GPR_ASSERT(tcp->outgoing_byte_idx == 0); + trailing = sending_length - (size_t)sent_length; + while (trailing > 0) { + size_t slice_length; + + tcp->outgoing_slice_idx--; + slice_length = GPR_SLICE_LENGTH( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]); + if (slice_length > trailing) { + tcp->outgoing_byte_idx = slice_length - trailing; + break; + } else { + trailing -= slice_length; + } + } - if (!slice_state_has_available(state)) { - return GRPC_ENDPOINT_WRITE_DONE; + if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) { + return FLUSH_DONE; } }; } -static void grpc_tcp_handle_write(void *arg /* grpc_tcp */, int success) { +static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + int success) { grpc_tcp *tcp = (grpc_tcp *)arg; - grpc_endpoint_write_status write_status; - grpc_endpoint_cb_status cb_status; - grpc_endpoint_write_cb cb; + flush_result status; + grpc_closure *cb; if (!success) { - slice_state_destroy(&tcp->write_state); cb = tcp->write_cb; tcp->write_cb = NULL; - cb(tcp->write_user_data, GRPC_ENDPOINT_CB_SHUTDOWN); - grpc_tcp_unref(tcp); + cb->cb(exec_ctx, cb->cb_arg, 0); + TCP_UNREF(exec_ctx, tcp, "write"); return; } - GRPC_TIMER_BEGIN(GRPC_PTAG_TCP_CB_WRITE, 0); - write_status = grpc_tcp_flush(tcp); - if (write_status == GRPC_ENDPOINT_WRITE_PENDING) { - grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_closure); + status = tcp_flush(tcp); + if (status == FLUSH_PENDING) { + grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure); } else { - slice_state_destroy(&tcp->write_state); - if (write_status == GRPC_ENDPOINT_WRITE_DONE) { - cb_status = GRPC_ENDPOINT_CB_OK; - } else { - cb_status = GRPC_ENDPOINT_CB_ERROR; - } cb = tcp->write_cb; tcp->write_cb = NULL; - cb(tcp->write_user_data, cb_status); - grpc_tcp_unref(tcp); + GPR_TIMER_BEGIN("tcp_handle_write.cb", 0); + cb->cb(exec_ctx, cb->cb_arg, status == FLUSH_DONE); + GPR_TIMER_END("tcp_handle_write.cb", 0); + TCP_UNREF(exec_ctx, tcp, "write"); } - GRPC_TIMER_END(GRPC_PTAG_TCP_CB_WRITE, 0); } -static grpc_endpoint_write_status grpc_tcp_write(grpc_endpoint *ep, - gpr_slice *slices, - size_t nslices, - grpc_endpoint_write_cb cb, - void *user_data) { +static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *buf, grpc_closure *cb) { grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_endpoint_write_status status; + flush_result status; if (grpc_tcp_trace) { size_t i; - for (i = 0; i < nslices; i++) { - char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + for (i = 0; i < buf->count; i++) { + char *data = + gpr_dump_slice(buf->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "WRITE %p: %s", tcp, data); gpr_free(data); } } - GRPC_TIMER_BEGIN(GRPC_PTAG_TCP_WRITE, 0); + GPR_TIMER_BEGIN("tcp_write", 0); GPR_ASSERT(tcp->write_cb == NULL); - slice_state_init(&tcp->write_state, slices, nslices, nslices); - status = grpc_tcp_flush(tcp); - if (status == GRPC_ENDPOINT_WRITE_PENDING) { - /* TODO(klempner): Consider inlining rather than malloc for small nslices */ - slice_state_realloc(&tcp->write_state, nslices); - gpr_ref(&tcp->refcount); + if (buf->length == 0) { + GPR_TIMER_END("tcp_write", 0); + grpc_exec_ctx_enqueue(exec_ctx, cb, 1); + return; + } + tcp->outgoing_buffer = buf; + tcp->outgoing_slice_idx = 0; + tcp->outgoing_byte_idx = 0; + + status = tcp_flush(tcp); + if (status == FLUSH_PENDING) { + TCP_REF(tcp, "write"); tcp->write_cb = cb; - tcp->write_user_data = user_data; - grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_closure); + grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure); + } else { + grpc_exec_ctx_enqueue(exec_ctx, cb, status == FLUSH_DONE); } - GRPC_TIMER_END(GRPC_PTAG_TCP_WRITE, 0); - return status; + GPR_TIMER_END("tcp_write", 0); } -static void grpc_tcp_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) { +static void tcp_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *pollset) { grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_pollset_add_fd(pollset, tcp->em_fd); + grpc_pollset_add_fd(exec_ctx, pollset, tcp->em_fd); } -static void grpc_tcp_add_to_pollset_set(grpc_endpoint *ep, - grpc_pollset_set *pollset_set) { +static void tcp_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset_set *pollset_set) { grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_pollset_set_add_fd(pollset_set, tcp->em_fd); + grpc_pollset_set_add_fd(exec_ctx, pollset_set, tcp->em_fd); } -static char *grpc_tcp_get_peer(grpc_endpoint *ep) { +static char *tcp_get_peer(grpc_endpoint *ep) { grpc_tcp *tcp = (grpc_tcp *)ep; return gpr_strdup(tcp->peer_string); } static const grpc_endpoint_vtable vtable = { - grpc_tcp_notify_on_read, grpc_tcp_write, - grpc_tcp_add_to_pollset, grpc_tcp_add_to_pollset_set, - grpc_tcp_shutdown, grpc_tcp_destroy, - grpc_tcp_get_peer}; + tcp_read, tcp_write, tcp_add_to_pollset, tcp_add_to_pollset_set, + tcp_shutdown, tcp_destroy, tcp_get_peer}; grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size, const char *peer_string) { @@ -597,21 +452,19 @@ grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size, tcp->fd = em_fd->fd; tcp->read_cb = NULL; tcp->write_cb = NULL; - tcp->read_user_data = NULL; - tcp->write_user_data = NULL; + tcp->incoming_buffer = NULL; tcp->slice_size = slice_size; tcp->iov_size = 1; tcp->finished_edge = 1; - slice_state_init(&tcp->write_state, NULL, 0, 0); /* paired with unref in grpc_tcp_destroy */ gpr_ref_init(&tcp->refcount, 1); tcp->em_fd = em_fd; - tcp->read_closure.cb = grpc_tcp_handle_read; + tcp->read_closure.cb = tcp_handle_read; tcp->read_closure.cb_arg = tcp; - tcp->write_closure.cb = grpc_tcp_handle_write; + tcp->write_closure.cb = tcp_handle_write; tcp->write_closure.cb_arg = tcp; + gpr_slice_buffer_init(&tcp->last_read_buffer); - tcp->handle_read_closure.cb = grpc_tcp_handle_read; return &tcp->base; } diff --git a/src/core/iomgr/tcp_server.h b/src/core/iomgr/tcp_server.h index 66bb3ef701..882635f638 100644 --- a/src/core/iomgr/tcp_server.h +++ b/src/core/iomgr/tcp_server.h @@ -39,16 +39,17 @@ /* Forward decl of grpc_tcp_server */ typedef struct grpc_tcp_server grpc_tcp_server; -/* New server callback: tcp is the newly connected tcp connection */ -typedef void (*grpc_tcp_server_cb)(void *arg, grpc_endpoint *ep); +/* Called for newly connected TCP connections. */ +typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *ep); /* Create a server, initially not bound to any ports */ grpc_tcp_server *grpc_tcp_server_create(void); /* Start listening to bound ports */ -void grpc_tcp_server_start(grpc_tcp_server *server, grpc_pollset **pollsets, - size_t pollset_count, grpc_tcp_server_cb cb, - void *cb_arg); +void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server, + grpc_pollset **pollsets, size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void *cb_arg); /* Add a port to the server, returning port number on success, or negative on failure. @@ -62,7 +63,7 @@ void grpc_tcp_server_start(grpc_tcp_server *server, grpc_pollset **pollsets, /* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle all of the multiple socket port matching logic in one place */ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, - int addr_len); + size_t addr_len); /* Returns the file descriptor of the Nth listening socket on this server, or -1 if the index is out of bounds. @@ -71,8 +72,7 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, up when grpc_tcp_server_destroy is called. */ int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index); -void grpc_tcp_server_destroy(grpc_tcp_server *server, - void (*shutdown_done)(void *shutdown_done_arg), - void *shutdown_done_arg); +void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server, + grpc_closure *closure); #endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_SERVER_H */ diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c index 6399aaadb9..99c76dcbe9 100644 --- a/src/core/iomgr/tcp_server_posix.c +++ b/src/core/iomgr/tcp_server_posix.c @@ -83,9 +83,9 @@ typedef struct { struct sockaddr sockaddr; struct sockaddr_un un; } addr; - int addr_len; - grpc_iomgr_closure read_closure; - grpc_iomgr_closure destroyed_closure; + size_t addr_len; + grpc_closure read_closure; + grpc_closure destroyed_closure; } server_port; static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) { @@ -98,8 +98,9 @@ static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) { /* the overall server */ struct grpc_tcp_server { - grpc_tcp_server_cb cb; - void *cb_arg; + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void *on_accept_cb_arg; gpr_mu mu; @@ -117,8 +118,7 @@ struct grpc_tcp_server { size_t port_capacity; /* shutdown callback */ - void (*shutdown_complete)(void *); - void *shutdown_complete_arg; + grpc_closure *shutdown_complete; /* all pollsets interested in new connections */ grpc_pollset **pollsets; @@ -132,17 +132,16 @@ grpc_tcp_server *grpc_tcp_server_create(void) { s->active_ports = 0; s->destroyed_ports = 0; s->shutdown = 0; - s->cb = NULL; - s->cb_arg = NULL; + s->on_accept_cb = NULL; + s->on_accept_cb_arg = NULL; s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); s->nports = 0; s->port_capacity = INIT_PORT_CAP; return s; } -static void finish_shutdown(grpc_tcp_server *s) { - s->shutdown_complete(s->shutdown_complete_arg); - s->shutdown_complete = NULL; +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, 1); gpr_mu_destroy(&s->mu); @@ -150,25 +149,23 @@ static void finish_shutdown(grpc_tcp_server *s) { gpr_free(s); } -static void destroyed_port(void *server, int success) { +static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, int success) { grpc_tcp_server *s = server; gpr_mu_lock(&s->mu); s->destroyed_ports++; if (s->destroyed_ports == s->nports) { gpr_mu_unlock(&s->mu); - finish_shutdown(s); + finish_shutdown(exec_ctx, s); } else { GPR_ASSERT(s->destroyed_ports < s->nports); gpr_mu_unlock(&s->mu); } } -static void dont_care_about_shutdown_completion(void *ignored) {} - /* called when all listening endpoints have been shutdown, so no further events will be received on them - at this point it's safe to destroy things */ -static void deactivated_all_ports(grpc_tcp_server *s) { +static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { size_t i; /* delete ALL the things */ @@ -187,38 +184,35 @@ static void deactivated_all_ports(grpc_tcp_server *s) { } sp->destroyed_closure.cb = destroyed_port; sp->destroyed_closure.cb_arg = s; - grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, "tcp_listener_shutdown"); + grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, + "tcp_listener_shutdown"); } gpr_mu_unlock(&s->mu); } else { gpr_mu_unlock(&s->mu); - finish_shutdown(s); + finish_shutdown(exec_ctx, s); } } -void grpc_tcp_server_destroy( - grpc_tcp_server *s, void (*shutdown_complete)(void *shutdown_complete_arg), - void *shutdown_complete_arg) { +void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, + grpc_closure *closure) { size_t i; gpr_mu_lock(&s->mu); GPR_ASSERT(!s->shutdown); s->shutdown = 1; - s->shutdown_complete = shutdown_complete - ? shutdown_complete - : dont_care_about_shutdown_completion; - s->shutdown_complete_arg = shutdown_complete_arg; + s->shutdown_complete = closure; /* shutdown all fd's */ if (s->active_ports) { for (i = 0; i < s->nports; i++) { - grpc_fd_shutdown(s->ports[i].emfd); + grpc_fd_shutdown(exec_ctx, s->ports[i].emfd); } gpr_mu_unlock(&s->mu); } else { gpr_mu_unlock(&s->mu); - deactivated_all_ports(s); + deactivated_all_ports(exec_ctx, s); } } @@ -236,7 +230,7 @@ static void init_max_accept_queue_size(void) { char *end; long i = strtol(buf, &end, 10); if (i > 0 && i <= INT_MAX && end && *end == 0) { - n = i; + n = (int)i; } } fclose(fp); @@ -256,7 +250,8 @@ static int get_max_accept_queue_size(void) { } /* Prepare a recently-created socket for listening. */ -static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { +static int prepare_socket(int fd, const struct sockaddr *addr, + size_t addr_len) { struct sockaddr_storage sockname_temp; socklen_t sockname_len; @@ -273,7 +268,8 @@ static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { goto error; } - if (bind(fd, addr, addr_len) < 0) { + GPR_ASSERT(addr_len < ~(socklen_t)0); + if (bind(fd, addr, (socklen_t)addr_len) < 0) { char *addr_str; grpc_sockaddr_to_string(&addr_str, addr, 0); gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno)); @@ -301,7 +297,7 @@ error: } /* event manager callback when reads are ready */ -static void on_read(void *arg, int success) { +static void on_read(grpc_exec_ctx *exec_ctx, void *arg, int success) { server_port *sp = arg; grpc_fd *fdobj; size_t i; @@ -317,14 +313,14 @@ static void on_read(void *arg, int success) { char *addr_str; char *name; /* Note: If we ever decide to return this address to the user, remember to - strip off the ::ffff:0.0.0.0/96 prefix first. */ + strip off the ::ffff:0.0.0.0/96 prefix first. */ int fd = grpc_accept4(sp->fd, (struct sockaddr *)&addr, &addrlen, 1, 1); if (fd < 0) { switch (errno) { case EINTR: continue; case EAGAIN: - grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); return; default: gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno)); @@ -337,35 +333,39 @@ static void on_read(void *arg, int success) { addr_str = grpc_sockaddr_to_uri((struct sockaddr *)&addr); gpr_asprintf(&name, "tcp-server-connection:%s", addr_str); + if (grpc_tcp_trace) { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: incoming connection: %s", addr_str); + } + fdobj = grpc_fd_create(fd, name); /* TODO(ctiller): revise this when we have server-side sharding of channels -- we certainly should not be automatically adding every incoming channel to every pollset owned by the server */ for (i = 0; i < sp->server->pollset_count; i++) { - grpc_pollset_add_fd(sp->server->pollsets[i], fdobj); + grpc_pollset_add_fd(exec_ctx, sp->server->pollsets[i], fdobj); } - sp->server->cb( - sp->server->cb_arg, + sp->server->on_accept_cb( + exec_ctx, sp->server->on_accept_cb_arg, grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str)); gpr_free(name); gpr_free(addr_str); } - abort(); + GPR_UNREACHABLE_CODE(return ); error: gpr_mu_lock(&sp->server->mu); if (0 == --sp->server->active_ports) { gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(sp->server); + deactivated_all_ports(exec_ctx, sp->server); } else { gpr_mu_unlock(&sp->server->mu); } } static int add_socket_to_server(grpc_tcp_server *s, int fd, - const struct sockaddr *addr, int addr_len) { + const struct sockaddr *addr, size_t addr_len) { server_port *sp; int port; char *addr_str; @@ -376,7 +376,7 @@ static int add_socket_to_server(grpc_tcp_server *s, int fd, grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1); gpr_asprintf(&name, "tcp-server-listener:%s", addr_str); gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->cb && "must add ports before starting server"); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); /* append it to the list under a lock */ if (s->nports == s->port_capacity) { s->port_capacity *= 2; @@ -398,7 +398,7 @@ static int add_socket_to_server(grpc_tcp_server *s, int fd, } int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, - int addr_len) { + size_t addr_len) { int allocated_port1 = -1; int allocated_port2 = -1; unsigned i; @@ -478,29 +478,31 @@ done: return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; } -int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index) { - return (index < s->nports) ? s->ports[index].fd : -1; +int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned port_index) { + return (port_index < s->nports) ? s->ports[port_index].fd : -1; } -void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset **pollsets, - size_t pollset_count, grpc_tcp_server_cb cb, - void *cb_arg) { +void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, + grpc_pollset **pollsets, size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void *on_accept_cb_arg) { size_t i, j; - GPR_ASSERT(cb); + GPR_ASSERT(on_accept_cb); gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->cb); + GPR_ASSERT(!s->on_accept_cb); GPR_ASSERT(s->active_ports == 0); - s->cb = cb; - s->cb_arg = cb_arg; + s->on_accept_cb = on_accept_cb; + s->on_accept_cb_arg = on_accept_cb_arg; s->pollsets = pollsets; s->pollset_count = pollset_count; for (i = 0; i < s->nports; i++) { for (j = 0; j < pollset_count; j++) { - grpc_pollset_add_fd(pollsets[j], s->ports[i].emfd); + grpc_pollset_add_fd(exec_ctx, pollsets[j], s->ports[i].emfd); } s->ports[i].read_closure.cb = on_read; s->ports[i].read_closure.cb_arg = &s->ports[i]; - grpc_fd_notify_on_read(s->ports[i].emfd, &s->ports[i].read_closure); + grpc_fd_notify_on_read(exec_ctx, s->ports[i].emfd, + &s->ports[i].read_closure); s->active_ports++; } gpr_mu_unlock(&s->mu); diff --git a/src/core/iomgr/tcp_server_windows.c b/src/core/iomgr/tcp_server_windows.c index d0478d3604..3fea8b5b35 100644 --- a/src/core/iomgr/tcp_server_windows.c +++ b/src/core/iomgr/tcp_server_windows.c @@ -67,26 +67,28 @@ typedef struct server_port { /* The cached AcceptEx for that port. */ LPFN_ACCEPTEX AcceptEx; int shutting_down; + /* closure for socket notification of accept being ready */ + grpc_closure on_accept; } server_port; /* the overall server */ struct grpc_tcp_server { - grpc_tcp_server_cb cb; - void *cb_arg; + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void *on_accept_cb_arg; gpr_mu mu; - gpr_cv cv; /* active port count: how many ports are actually still listening */ int active_ports; - /* number of iomgr callbacks that have been explicitly scheduled during - * shutdown */ - int iomgr_callbacks_pending; /* all listening ports */ server_port *ports; size_t nports; size_t port_capacity; + + /* shutdown callback */ + grpc_closure *shutdown_complete; }; /* Public function. Allocates the proper data structures to hold a @@ -94,54 +96,63 @@ struct grpc_tcp_server { grpc_tcp_server *grpc_tcp_server_create(void) { grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); gpr_mu_init(&s->mu); - gpr_cv_init(&s->cv); s->active_ports = 0; - s->iomgr_callbacks_pending = 0; - s->cb = NULL; - s->cb_arg = NULL; + s->on_accept_cb = NULL; + s->on_accept_cb_arg = NULL; s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); s->nports = 0; s->port_capacity = INIT_PORT_CAP; + s->shutdown_complete = NULL; return s; } -/* Public function. Stops and destroys a grpc_tcp_server. */ -void grpc_tcp_server_destroy(grpc_tcp_server *s, - void (*shutdown_done)(void *shutdown_done_arg), - void *shutdown_done_arg) { +static void dont_care_about_shutdown_completion(void *arg) {} + +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { size_t i; - gpr_mu_lock(&s->mu); - /* First, shutdown all fd's. This will queue abortion calls for all - of the pending accepts due to the normal operation mechanism. */ - for (i = 0; i < s->nports; i++) { - server_port *sp = &s->ports[i]; - sp->shutting_down = 1; - s->iomgr_callbacks_pending += grpc_winsocket_shutdown(sp->socket); - } - /* This happens asynchronously. Wait while that happens. */ - while (s->active_ports || s->iomgr_callbacks_pending) { - gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future(GPR_CLOCK_REALTIME)); - } - gpr_mu_unlock(&s->mu); + + grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, 1); /* Now that the accepts have been aborted, we can destroy the sockets. The IOCP won't get notified on these, so we can flag them as already closed by the system. */ for (i = 0; i < s->nports; i++) { server_port *sp = &s->ports[i]; - grpc_winsocket_orphan(sp->socket); + grpc_winsocket_destroy(sp->socket); } gpr_free(s->ports); gpr_free(s); +} - if (shutdown_done) { - shutdown_done(shutdown_done_arg); +/* Public function. Stops and destroys a grpc_tcp_server. */ +void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, + grpc_closure *shutdown_complete) { + size_t i; + int immediately_done = 0; + gpr_mu_lock(&s->mu); + + s->shutdown_complete = shutdown_complete; + + /* First, shutdown all fd's. This will queue abortion calls for all + of the pending accepts due to the normal operation mechanism. */ + if (s->active_ports == 0) { + immediately_done = 1; + } + for (i = 0; i < s->nports; i++) { + server_port *sp = &s->ports[i]; + sp->shutting_down = 1; + grpc_winsocket_shutdown(sp->socket); + } + gpr_mu_unlock(&s->mu); + + if (immediately_done) { + finish_shutdown(exec_ctx, s); } } /* Prepare (bind) a recently-created socket for listening. */ static int prepare_socket(SOCKET sock, const struct sockaddr *addr, - int addr_len) { + size_t addr_len) { struct sockaddr_storage sockname_temp; socklen_t sockname_len; @@ -154,7 +165,7 @@ static int prepare_socket(SOCKET sock, const struct sockaddr *addr, goto error; } - if (bind(sock, addr, addr_len) == SOCKET_ERROR) { + if (bind(sock, addr, (int)addr_len) == SOCKET_ERROR) { char *addr_str; char *utf8_message = gpr_format_message(WSAGetLastError()); grpc_sockaddr_to_string(&addr_str, addr, 0); @@ -187,23 +198,25 @@ error: return -1; } -static void decrement_active_ports_and_notify(server_port *sp) { +static void decrement_active_ports_and_notify(grpc_exec_ctx *exec_ctx, + server_port *sp) { + int notify = 0; sp->shutting_down = 0; - sp->socket->read_info.outstanding = 0; gpr_mu_lock(&sp->server->mu); GPR_ASSERT(sp->server->active_ports > 0); - if (0 == --sp->server->active_ports) { - gpr_cv_broadcast(&sp->server->cv); + if (0 == --sp->server->active_ports && + sp->server->shutdown_complete != NULL) { + notify = 1; } gpr_mu_unlock(&sp->server->mu); + if (notify) { + finish_shutdown(exec_ctx, sp->server); + } } -/* start_accept will reference that for the IOCP notification request. */ -static void on_accept(void *arg, int from_iocp); - /* In order to do an async accept, we need to create a socket first which will be the one assigned to the new incoming connection. */ -static void start_accept(server_port *port) { +static void start_accept(grpc_exec_ctx *exec_ctx, server_port *port) { SOCKET sock = INVALID_SOCKET; char *message; char *utf8_message; @@ -242,7 +255,7 @@ static void start_accept(server_port *port) { /* We're ready to do the accept. Calling grpc_socket_notify_on_read may immediately process an accept that happened in the meantime. */ port->new_socket = sock; - grpc_socket_notify_on_read(port->socket, on_accept, port); + grpc_socket_notify_on_read(exec_ctx, port->socket, &port->on_accept); return; failure: @@ -252,7 +265,7 @@ failure: change is not seen by on_accept and we proceed to trying new accept, but we fail there because the listening port has been closed in the meantime. */ - decrement_active_ports_and_notify(port); + decrement_active_ports_and_notify(exec_ctx, port); return; } utf8_message = gpr_format_message(WSAGetLastError()); @@ -262,7 +275,7 @@ failure: } /* Event manager callback when reads are ready. */ -static void on_accept(void *arg, int from_iocp) { +static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, int from_iocp) { server_port *sp = arg; SOCKET sock = sp->new_socket; grpc_winsocket_callback_info *info = &sp->socket->read_info; @@ -280,17 +293,11 @@ static void on_accept(void *arg, int from_iocp) { this is necessary in the read/write case, it's useless for the accept case. We only need to adjust the pending callback count */ if (!from_iocp) { - gpr_mu_lock(&sp->server->mu); - GPR_ASSERT(sp->server->iomgr_callbacks_pending > 0); - if (0 == --sp->server->iomgr_callbacks_pending) { - gpr_cv_broadcast(&sp->server->cv); - } - gpr_mu_unlock(&sp->server->mu); return; } /* The IOCP notified us of a completed operation. Let's grab the results, - and act accordingly. */ + and act accordingly. */ transfered_bytes = 0; wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, &transfered_bytes, FALSE, &flags); @@ -298,7 +305,7 @@ static void on_accept(void *arg, int from_iocp) { if (sp->shutting_down) { /* During the shutdown case, we ARE expecting an error. So that's well, and we can wake up the shutdown thread. */ - decrement_active_ports_and_notify(sp); + decrement_active_ports_and_notify(exec_ctx, sp); return; } else { char *utf8_message = gpr_format_message(WSAGetLastError()); @@ -329,21 +336,23 @@ static void on_accept(void *arg, int from_iocp) { peer_name_string); gpr_free(fd_name); gpr_free(peer_name_string); + } else { + closesocket(sock); } } /* The only time we should call our callback, is where we successfully managed to accept a connection, and created an endpoint. */ - if (ep) sp->server->cb(sp->server->cb_arg, ep); + if (ep) sp->server->on_accept_cb(exec_ctx, sp->server->on_accept_cb_arg, ep); /* As we were notified from the IOCP of one and exactly one accept, the former socked we created has now either been destroy or assigned to the new connection. We need to create a new one for the next connection. */ - start_accept(sp); + start_accept(exec_ctx, sp); } static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock, - const struct sockaddr *addr, int addr_len) { + const struct sockaddr *addr, size_t addr_len) { server_port *sp; int port; int status; @@ -370,11 +379,12 @@ static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock, port = prepare_socket(sock, addr, addr_len); if (port >= 0) { gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->cb && "must add ports before starting server"); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); /* append it to the list under a lock */ if (s->nports == s->port_capacity) { - s->port_capacity *= 2; - s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity); + /* too many ports, and we need to store their address in a closure */ + /* TODO(ctiller): make server_port a linked list */ + abort(); } sp = &s->ports[s->nports++]; sp->server = s; @@ -382,6 +392,7 @@ static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock, sp->shutting_down = 0; sp->AcceptEx = AcceptEx; sp->new_socket = INVALID_SOCKET; + grpc_closure_init(&sp->on_accept, on_accept, sp); GPR_ASSERT(sp->socket); gpr_mu_unlock(&s->mu); } @@ -390,7 +401,7 @@ static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock, } int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, - int addr_len) { + size_t addr_len) { int allocated_port = -1; unsigned i; SOCKET sock; @@ -447,23 +458,24 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, return allocated_port; } -SOCKET grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) { +SOCKET +grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) { return (index < s->nports) ? s->ports[index].socket->socket : INVALID_SOCKET; } -void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset **pollset, - size_t pollset_count, grpc_tcp_server_cb cb, - void *cb_arg) { +void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, + grpc_pollset **pollset, size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void *on_accept_cb_arg) { size_t i; - GPR_ASSERT(cb); + GPR_ASSERT(on_accept_cb); gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->cb); + GPR_ASSERT(!s->on_accept_cb); GPR_ASSERT(s->active_ports == 0); - s->cb = cb; - s->cb_arg = cb_arg; + s->on_accept_cb = on_accept_cb; + s->on_accept_cb_arg = on_accept_cb_arg; for (i = 0; i < s->nports; i++) { - s->ports[i].socket->read_info.outstanding = 1; - start_accept(s->ports + i); + start_accept(exec_ctx, s->ports + i); s->active_ports++; } gpr_mu_unlock(&s->mu); diff --git a/src/core/iomgr/tcp_windows.c b/src/core/iomgr/tcp_windows.c index 901793ec43..5ff78231bd 100644 --- a/src/core/iomgr/tcp_windows.c +++ b/src/core/iomgr/tcp_windows.c @@ -44,7 +44,7 @@ #include <grpc/support/string_util.h> #include <grpc/support/useful.h> -#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/timer.h" #include "src/core/iomgr/iocp_windows.h" #include "src/core/iomgr/sockaddr.h" #include "src/core/iomgr/sockaddr_utils.h" @@ -82,13 +82,14 @@ typedef struct grpc_tcp { /* Refcounting how many operations are in progress. */ gpr_refcount refcount; - grpc_endpoint_read_cb read_cb; - void *read_user_data; - gpr_slice read_slice; + grpc_closure on_read; + grpc_closure on_write; - grpc_endpoint_write_cb write_cb; - void *write_user_data; - gpr_slice_buffer write_slices; + grpc_closure *read_cb; + grpc_closure *write_cb; + gpr_slice read_slice; + gpr_slice_buffer *write_slices; + gpr_slice_buffer *read_slices; /* The IO Completion Port runs from another thread. We need some mechanism to protect ourselves when requesting a shutdown. */ @@ -98,82 +99,85 @@ typedef struct grpc_tcp { char *peer_string; } grpc_tcp; -static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +static void tcp_free(grpc_tcp *tcp) { + grpc_winsocket_destroy(tcp->socket); + gpr_mu_destroy(&tcp->mu); + gpr_free(tcp->peer_string); + gpr_free(tcp); +} +/*#define GRPC_TCP_REFCOUNT_DEBUG*/ +#ifdef GRPC_TCP_REFCOUNT_DEBUG +#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP unref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count - 1); + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP ref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count + 1); + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(tcp, reason) tcp_unref((tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) static void tcp_unref(grpc_tcp *tcp) { if (gpr_unref(&tcp->refcount)) { - gpr_slice_buffer_destroy(&tcp->write_slices); - grpc_winsocket_orphan(tcp->socket); - gpr_mu_destroy(&tcp->mu); - gpr_free(tcp->peer_string); - gpr_free(tcp); + tcp_free(tcp); } } +static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +#endif + /* Asynchronous callback from the IOCP, or the background thread. */ -static void on_read(void *tcpp, int from_iocp) { - grpc_tcp *tcp = (grpc_tcp *)tcpp; +static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, int success) { + grpc_tcp *tcp = tcpp; + grpc_closure *cb = tcp->read_cb; grpc_winsocket *socket = tcp->socket; gpr_slice sub; gpr_slice *slice = NULL; size_t nslices = 0; - grpc_endpoint_cb_status status; - grpc_endpoint_read_cb cb; grpc_winsocket_callback_info *info = &socket->read_info; - void *opaque = tcp->read_user_data; int do_abort = 0; - gpr_mu_lock(&tcp->mu); - cb = tcp->read_cb; - tcp->read_cb = NULL; - if (!from_iocp || tcp->shutting_down) { - /* If we are here with from_iocp set to true, it means we got raced to - shutting down the endpoint. No actual abort callback will happen - though, so we're going to do it from here. */ - do_abort = 1; - } - gpr_mu_unlock(&tcp->mu); - - if (do_abort) { - if (from_iocp) { - tcp->socket->read_info.outstanding = 0; + if (success) { + if (socket->read_info.wsa_error != 0 && !tcp->shutting_down) { + if (socket->read_info.wsa_error != WSAECONNRESET) { + char *utf8_message = gpr_format_message(info->wsa_error); + gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message); + gpr_free(utf8_message); + } + success = 0; gpr_slice_unref(tcp->read_slice); - } - tcp_unref(tcp); - if (cb) cb(opaque, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN); - return; - } - - GPR_ASSERT(tcp->socket->read_info.outstanding); - - if (socket->read_info.wsa_error != 0) { - if (socket->read_info.wsa_error != WSAECONNRESET) { - char *utf8_message = gpr_format_message(info->wsa_error); - gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message); - gpr_free(utf8_message); - } - gpr_slice_unref(tcp->read_slice); - status = GRPC_ENDPOINT_CB_ERROR; - } else { - if (info->bytes_transfered != 0) { - sub = gpr_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered); - status = GRPC_ENDPOINT_CB_OK; - slice = ⊂ - nslices = 1; } else { - gpr_slice_unref(tcp->read_slice); - status = GRPC_ENDPOINT_CB_EOF; + if (info->bytes_transfered != 0 && !tcp->shutting_down) { + sub = gpr_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered); + gpr_slice_buffer_add(tcp->read_slices, sub); + success = 1; + } else { + gpr_slice_unref(tcp->read_slice); + success = 0; + } } } - tcp->socket->read_info.outstanding = 0; - - tcp_unref(tcp); - cb(opaque, slice, nslices, status); + tcp->read_cb = NULL; + TCP_UNREF(tcp, "read"); + if (cb) { + cb->cb(exec_ctx, cb->cb_arg, success); + } } -static void win_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, - void *arg) { +static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *read_slices, grpc_closure *cb) { grpc_tcp *tcp = (grpc_tcp *)ep; grpc_winsocket *handle = tcp->socket; grpc_winsocket_callback_info *info = &handle->read_info; @@ -182,21 +186,22 @@ static void win_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, DWORD flags = 0; WSABUF buffer; - GPR_ASSERT(!tcp->socket->read_info.outstanding); if (tcp->shutting_down) { - cb(arg, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN); + grpc_exec_ctx_enqueue(exec_ctx, cb, 0); return; } - tcp_ref(tcp); - tcp->socket->read_info.outstanding = 1; + tcp->read_cb = cb; - tcp->read_user_data = arg; + tcp->read_slices = read_slices; + gpr_slice_buffer_reset_and_unref(read_slices); tcp->read_slice = gpr_slice_malloc(8192); buffer.len = GPR_SLICE_LENGTH(tcp->read_slice); buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice); + TCP_REF(tcp, "read"); + /* First let's try a synchronous, non-blocking read. */ status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL); @@ -205,8 +210,7 @@ static void win_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, /* Did we get data immediately ? Yay. */ if (info->wsa_error != WSAEWOULDBLOCK) { info->bytes_transfered = bytes_read; - /* This might heavily recurse. */ - on_read(tcp, 1); + grpc_exec_ctx_enqueue(exec_ctx, &tcp->on_read, 1); return; } @@ -219,70 +223,47 @@ static void win_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb, int wsa_error = WSAGetLastError(); if (wsa_error != WSA_IO_PENDING) { info->wsa_error = wsa_error; - on_read(tcp, 1); + grpc_exec_ctx_enqueue(exec_ctx, &tcp->on_read, 0); return; } } - grpc_socket_notify_on_read(tcp->socket, on_read, tcp); + grpc_socket_notify_on_read(exec_ctx, tcp->socket, &tcp->on_read); } /* Asynchronous callback from the IOCP, or the background thread. */ -static void on_write(void *tcpp, int from_iocp) { +static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, int success) { grpc_tcp *tcp = (grpc_tcp *)tcpp; grpc_winsocket *handle = tcp->socket; grpc_winsocket_callback_info *info = &handle->write_info; - grpc_endpoint_cb_status status = GRPC_ENDPOINT_CB_OK; - grpc_endpoint_write_cb cb; - void *opaque = tcp->write_user_data; + grpc_closure *cb; int do_abort = 0; gpr_mu_lock(&tcp->mu); cb = tcp->write_cb; tcp->write_cb = NULL; - if (!from_iocp || tcp->shutting_down) { - /* If we are here with from_iocp set to true, it means we got raced to - shutting down the endpoint. No actual abort callback will happen - though, so we're going to do it from here. */ - do_abort = 1; - } gpr_mu_unlock(&tcp->mu); - if (do_abort) { - if (from_iocp) { - tcp->socket->write_info.outstanding = 0; - gpr_slice_buffer_reset_and_unref(&tcp->write_slices); - } - tcp_unref(tcp); - if (cb) cb(opaque, GRPC_ENDPOINT_CB_SHUTDOWN); - return; - } - - GPR_ASSERT(tcp->socket->write_info.outstanding); - - if (info->wsa_error != 0) { - if (info->wsa_error != WSAECONNRESET) { - char *utf8_message = gpr_format_message(info->wsa_error); - gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message); - gpr_free(utf8_message); + if (success) { + if (info->wsa_error != 0) { + if (info->wsa_error != WSAECONNRESET) { + char *utf8_message = gpr_format_message(info->wsa_error); + gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message); + gpr_free(utf8_message); + } + success = 0; + } else { + GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length); } - status = GRPC_ENDPOINT_CB_ERROR; - } else { - GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length); } - gpr_slice_buffer_reset_and_unref(&tcp->write_slices); - tcp->socket->write_info.outstanding = 0; - - tcp_unref(tcp); - cb(opaque, status); + TCP_UNREF(tcp, "write"); + cb->cb(exec_ctx, cb->cb_arg, success); } /* Initiates a write. */ -static grpc_endpoint_write_status win_write(grpc_endpoint *ep, - gpr_slice *slices, size_t nslices, - grpc_endpoint_write_cb cb, - void *arg) { +static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *slices, grpc_closure *cb) { grpc_tcp *tcp = (grpc_tcp *)ep; grpc_winsocket *socket = tcp->socket; grpc_winsocket_callback_info *info = &socket->write_info; @@ -293,30 +274,26 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep, WSABUF *allocated = NULL; WSABUF *buffers = local_buffers; - GPR_ASSERT(!tcp->socket->write_info.outstanding); if (tcp->shutting_down) { - return GRPC_ENDPOINT_WRITE_ERROR; + grpc_exec_ctx_enqueue(exec_ctx, cb, 0); + return; } - tcp_ref(tcp); - tcp->socket->write_info.outstanding = 1; tcp->write_cb = cb; - tcp->write_user_data = arg; - - gpr_slice_buffer_addn(&tcp->write_slices, slices, nslices); + tcp->write_slices = slices; - if (tcp->write_slices.count > GPR_ARRAY_SIZE(local_buffers)) { - buffers = (WSABUF *)gpr_malloc(sizeof(WSABUF) * tcp->write_slices.count); + if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) { + buffers = (WSABUF *)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count); allocated = buffers; } - for (i = 0; i < tcp->write_slices.count; i++) { - buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices.slices[i]); - buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices.slices[i]); + for (i = 0; i < tcp->write_slices->count; i++) { + buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices->slices[i]); + buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices->slices[i]); } /* First, let's try a synchronous, non-blocking write. */ - status = WSASend(socket->socket, buffers, tcp->write_slices.count, + status = WSASend(socket->socket, buffers, tcp->write_slices->count, &bytes_sent, 0, NULL, NULL); info->wsa_error = status == 0 ? 0 : WSAGetLastError(); @@ -324,10 +301,10 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep, connection that has its send queue filled up. But if we don't, then we can avoid doing an async write operation at all. */ if (info->wsa_error != WSAEWOULDBLOCK) { - grpc_endpoint_write_status ret = GRPC_ENDPOINT_WRITE_ERROR; + int ok = 0; if (status == 0) { - ret = GRPC_ENDPOINT_WRITE_DONE; - GPR_ASSERT(bytes_sent == tcp->write_slices.length); + ok = 1; + GPR_ASSERT(bytes_sent == tcp->write_slices->length); } else { if (socket->read_info.wsa_error != WSAECONNRESET) { char *utf8_message = gpr_format_message(info->wsa_error); @@ -336,43 +313,43 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep, } } if (allocated) gpr_free(allocated); - gpr_slice_buffer_reset_and_unref(&tcp->write_slices); - tcp->socket->write_info.outstanding = 0; - tcp_unref(tcp); - return ret; + grpc_exec_ctx_enqueue(exec_ctx, cb, ok); + return; } + TCP_REF(tcp, "write"); + /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same operation, this time asynchronously. */ memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED)); - status = WSASend(socket->socket, buffers, tcp->write_slices.count, + status = WSASend(socket->socket, buffers, tcp->write_slices->count, &bytes_sent, 0, &socket->write_info.overlapped, NULL); if (allocated) gpr_free(allocated); if (status != 0) { int wsa_error = WSAGetLastError(); if (wsa_error != WSA_IO_PENDING) { - gpr_slice_buffer_reset_and_unref(&tcp->write_slices); - tcp->socket->write_info.outstanding = 0; - tcp_unref(tcp); - return GRPC_ENDPOINT_WRITE_ERROR; + TCP_UNREF(tcp, "write"); + grpc_exec_ctx_enqueue(exec_ctx, cb, 0); + return; } } /* As all is now setup, we can now ask for the IOCP notification. It may trigger the callback immediately however, but no matter. */ - grpc_socket_notify_on_write(socket, on_write, tcp); - return GRPC_ENDPOINT_WRITE_PENDING; + grpc_socket_notify_on_write(exec_ctx, socket, &tcp->on_write); } -static void win_add_to_pollset(grpc_endpoint *ep, grpc_pollset *ps) { +static void win_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *ps) { grpc_tcp *tcp; (void)ps; tcp = (grpc_tcp *)ep; grpc_iocp_add_socket(tcp->socket); } -static void win_add_to_pollset_set(grpc_endpoint *ep, grpc_pollset_set *pss) { +static void win_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset_set *pss) { grpc_tcp *tcp; (void)pss; tcp = (grpc_tcp *)ep; @@ -385,21 +362,19 @@ static void win_add_to_pollset_set(grpc_endpoint *ep, grpc_pollset_set *pss) { we're not going to protect against these. However the IO Completion Port callback will happen from another thread, so we need to protect against concurrent access of the data structure in that regard. */ -static void win_shutdown(grpc_endpoint *ep) { +static void win_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { grpc_tcp *tcp = (grpc_tcp *)ep; - int extra_refs = 0; gpr_mu_lock(&tcp->mu); /* At that point, what may happen is that we're already inside the IOCP callback. See the comments in on_read and on_write. */ tcp->shutting_down = 1; - extra_refs = grpc_winsocket_shutdown(tcp->socket); - while (extra_refs--) tcp_ref(tcp); + grpc_winsocket_shutdown(tcp->socket); gpr_mu_unlock(&tcp->mu); } -static void win_destroy(grpc_endpoint *ep) { +static void win_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { grpc_tcp *tcp = (grpc_tcp *)ep; - tcp_unref(tcp); + TCP_UNREF(tcp, "destroy"); } static char *win_get_peer(grpc_endpoint *ep) { @@ -407,9 +382,9 @@ static char *win_get_peer(grpc_endpoint *ep) { return gpr_strdup(tcp->peer_string); } -static grpc_endpoint_vtable vtable = { - win_notify_on_read, win_write, win_add_to_pollset, win_add_to_pollset_set, - win_shutdown, win_destroy, win_get_peer}; +static grpc_endpoint_vtable vtable = {win_read, win_write, win_add_to_pollset, + win_add_to_pollset_set, win_shutdown, + win_destroy, win_get_peer}; grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string) { grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); @@ -417,8 +392,9 @@ grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string) { tcp->base.vtable = &vtable; tcp->socket = socket; gpr_mu_init(&tcp->mu); - gpr_slice_buffer_init(&tcp->write_slices); gpr_ref_init(&tcp->refcount, 1); + grpc_closure_init(&tcp->on_read, on_read, tcp); + grpc_closure_init(&tcp->on_write, on_write, tcp); tcp->peer_string = gpr_strdup(peer_string); return &tcp->base; } diff --git a/src/core/iomgr/time_averaged_stats.c b/src/core/iomgr/time_averaged_stats.c index f881dde9fc..e075db4373 100644 --- a/src/core/iomgr/time_averaged_stats.c +++ b/src/core/iomgr/time_averaged_stats.c @@ -33,7 +33,7 @@ #include "src/core/iomgr/time_averaged_stats.h" -void grpc_time_averaged_stats_init(grpc_time_averaged_stats *stats, +void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats, double init_avg, double regress_weight, double persistence_factor) { stats->init_avg = init_avg; @@ -45,14 +45,14 @@ void grpc_time_averaged_stats_init(grpc_time_averaged_stats *stats, stats->aggregate_weighted_avg = init_avg; } -void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats *stats, +void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats, double value) { stats->batch_total_value += value; ++stats->batch_num_samples; } double grpc_time_averaged_stats_update_average( - grpc_time_averaged_stats *stats) { + grpc_time_averaged_stats* stats) { /* Start with the current batch: */ double weighted_sum = stats->batch_total_value; double total_weight = stats->batch_num_samples; diff --git a/src/core/iomgr/time_averaged_stats.h b/src/core/iomgr/time_averaged_stats.h index e6dec1b4cd..4e9e3956c2 100644 --- a/src/core/iomgr/time_averaged_stats.h +++ b/src/core/iomgr/time_averaged_stats.h @@ -75,14 +75,14 @@ typedef struct { /* See the comments on the members above for an explanation of init_avg, regress_weight, and persistence_factor. */ -void grpc_time_averaged_stats_init(grpc_time_averaged_stats *stats, +void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats, double init_avg, double regress_weight, double persistence_factor); /* Add a sample to the current batch. */ -void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats *stats, +void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats, double value); /* Complete a batch and compute the new estimate of the average sample value. */ -double grpc_time_averaged_stats_update_average(grpc_time_averaged_stats *stats); +double grpc_time_averaged_stats_update_average(grpc_time_averaged_stats* stats); #endif /* GRPC_INTERNAL_CORE_IOMGR_TIME_AVERAGED_STATS_H */ diff --git a/src/core/iomgr/alarm.c b/src/core/iomgr/timer.c index ddb30dc4bb..66fafe75ad 100644 --- a/src/core/iomgr/alarm.c +++ b/src/core/iomgr/timer.c @@ -31,10 +31,10 @@ * */ -#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/timer.h" -#include "src/core/iomgr/alarm_heap.h" -#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/timer_heap.h" +#include "src/core/iomgr/timer_internal.h" #include "src/core/iomgr/time_averaged_stats.h" #include <grpc/support/log.h> #include <grpc/support/sync.h> @@ -44,7 +44,6 @@ #define LOG2_NUM_SHARDS 5 #define NUM_SHARDS (1 << LOG2_NUM_SHARDS) -#define MAX_ALARMS_PER_CHECK 128 #define ADD_DEADLINE_SCALE 0.33 #define MIN_QUEUE_WINDOW_DURATION 0.01 #define MAX_QUEUE_WINDOW_DURATION 1 @@ -52,38 +51,38 @@ typedef struct { gpr_mu mu; grpc_time_averaged_stats stats; - /* All and only alarms with deadlines <= this will be in the heap. */ + /* All and only timers with deadlines <= this will be in the heap. */ gpr_timespec queue_deadline_cap; gpr_timespec min_deadline; /* Index in the g_shard_queue */ gpr_uint32 shard_queue_index; - /* This holds all alarms with deadlines < queue_deadline_cap. Alarms in this + /* This holds all timers with deadlines < queue_deadline_cap. Timers in this list have the top bit of their deadline set to 0. */ - grpc_alarm_heap heap; - /* This holds alarms whose deadline is >= queue_deadline_cap. */ - grpc_alarm list; + grpc_timer_heap heap; + /* This holds timers whose deadline is >= queue_deadline_cap. */ + grpc_timer list; } shard_type; /* Protects g_shard_queue */ static gpr_mu g_mu; -/* Allow only one run_some_expired_alarms at once */ +/* Allow only one run_some_expired_timers at once */ static gpr_mu g_checker_mu; static gpr_clock_type g_clock_type; static shard_type g_shards[NUM_SHARDS]; /* Protected by g_mu */ static shard_type *g_shard_queue[NUM_SHARDS]; -static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now, +static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now, gpr_timespec *next, int success); static gpr_timespec compute_min_deadline(shard_type *shard) { - return grpc_alarm_heap_is_empty(&shard->heap) + return grpc_timer_heap_is_empty(&shard->heap) ? shard->queue_deadline_cap - : grpc_alarm_heap_top(&shard->heap)->deadline; + : grpc_timer_heap_top(&shard->heap)->deadline; } -void grpc_alarm_list_init(gpr_timespec now) { - int i; +void grpc_timer_list_init(gpr_timespec now) { + gpr_uint32 i; gpr_mu_init(&g_mu); gpr_mu_init(&g_checker_mu); @@ -96,56 +95,55 @@ void grpc_alarm_list_init(gpr_timespec now) { 0.5); shard->queue_deadline_cap = now; shard->shard_queue_index = i; - grpc_alarm_heap_init(&shard->heap); + grpc_timer_heap_init(&shard->heap); shard->list.next = shard->list.prev = &shard->list; shard->min_deadline = compute_min_deadline(shard); g_shard_queue[i] = shard; } } -void grpc_alarm_list_shutdown(void) { +void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) { int i; - while (run_some_expired_alarms(NULL, gpr_inf_future(g_clock_type), NULL, 0)) - ; + run_some_expired_timers(exec_ctx, gpr_inf_future(g_clock_type), NULL, 0); for (i = 0; i < NUM_SHARDS; i++) { shard_type *shard = &g_shards[i]; gpr_mu_destroy(&shard->mu); - grpc_alarm_heap_destroy(&shard->heap); + grpc_timer_heap_destroy(&shard->heap); } gpr_mu_destroy(&g_mu); gpr_mu_destroy(&g_checker_mu); } /* This is a cheap, but good enough, pointer hash for sharding the tasks: */ -static size_t shard_idx(const grpc_alarm *info) { +static size_t shard_idx(const grpc_timer *info) { size_t x = (size_t)info; return ((x >> 4) ^ (x >> 9) ^ (x >> 14)) & (NUM_SHARDS - 1); } static double ts_to_dbl(gpr_timespec ts) { - return ts.tv_sec + 1e-9 * ts.tv_nsec; + return (double)ts.tv_sec + 1e-9 * ts.tv_nsec; } static gpr_timespec dbl_to_ts(double d) { gpr_timespec ts; - ts.tv_sec = d; - ts.tv_nsec = 1e9 * (d - ts.tv_sec); + ts.tv_sec = (time_t)d; + ts.tv_nsec = (int)(1e9 * (d - (double)ts.tv_sec)); ts.clock_type = GPR_TIMESPAN; return ts; } -static void list_join(grpc_alarm *head, grpc_alarm *alarm) { - alarm->next = head; - alarm->prev = head->prev; - alarm->next->prev = alarm->prev->next = alarm; +static void list_join(grpc_timer *head, grpc_timer *timer) { + timer->next = head; + timer->prev = head->prev; + timer->next->prev = timer->prev->next = timer; } -static void list_remove(grpc_alarm *alarm) { - alarm->next->prev = alarm->prev; - alarm->prev->next = alarm->next; +static void list_remove(grpc_timer *timer) { + timer->next->prev = timer->prev; + timer->prev->next = timer->next; } -static void swap_adjacent_shards_in_queue(size_t first_shard_queue_index) { +static void swap_adjacent_shards_in_queue(gpr_uint32 first_shard_queue_index) { shard_type *temp; temp = g_shard_queue[first_shard_queue_index]; g_shard_queue[first_shard_queue_index] = @@ -172,17 +170,16 @@ static void note_deadline_change(shard_type *shard) { } } -void grpc_alarm_init(grpc_alarm *alarm, gpr_timespec deadline, - grpc_iomgr_cb_func alarm_cb, void *alarm_cb_arg, - gpr_timespec now) { - int is_first_alarm = 0; - shard_type *shard = &g_shards[shard_idx(alarm)]; +void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, + gpr_timespec deadline, grpc_iomgr_cb_func timer_cb, + void *timer_cb_arg, gpr_timespec now) { + int is_first_timer = 0; + shard_type *shard = &g_shards[shard_idx(timer)]; GPR_ASSERT(deadline.clock_type == g_clock_type); GPR_ASSERT(now.clock_type == g_clock_type); - alarm->cb = alarm_cb; - alarm->cb_arg = alarm_cb_arg; - alarm->deadline = deadline; - alarm->triggered = 0; + grpc_closure_init(&timer->closure, timer_cb, timer_cb_arg); + timer->deadline = deadline; + timer->triggered = 0; /* TODO(ctiller): check deadline expired */ @@ -190,25 +187,25 @@ void grpc_alarm_init(grpc_alarm *alarm, gpr_timespec deadline, grpc_time_averaged_stats_add_sample(&shard->stats, ts_to_dbl(gpr_time_sub(deadline, now))); if (gpr_time_cmp(deadline, shard->queue_deadline_cap) < 0) { - is_first_alarm = grpc_alarm_heap_add(&shard->heap, alarm); + is_first_timer = grpc_timer_heap_add(&shard->heap, timer); } else { - alarm->heap_index = INVALID_HEAP_INDEX; - list_join(&shard->list, alarm); + timer->heap_index = INVALID_HEAP_INDEX; + list_join(&shard->list, timer); } gpr_mu_unlock(&shard->mu); /* Deadline may have decreased, we need to adjust the master queue. Note that there is a potential racy unlocked region here. There could be a - reordering of multiple grpc_alarm_init calls, at this point, but the < test + reordering of multiple grpc_timer_init calls, at this point, but the < test below should ensure that we err on the side of caution. There could - also be a race with grpc_alarm_check, which might beat us to the lock. In - that case, it is possible that the alarm that we added will have already + also be a race with grpc_timer_check, which might beat us to the lock. In + that case, it is possible that the timer that we added will have already run by the time we hold the lock, but that too is a safe error. - Finally, it's possible that the grpc_alarm_check that intervened failed to - trigger the new alarm because the min_deadline hadn't yet been reduced. - In that case, the alarm will simply have to wait for the next - grpc_alarm_check. */ - if (is_first_alarm) { + Finally, it's possible that the grpc_timer_check that intervened failed to + trigger the new timer because the min_deadline hadn't yet been reduced. + In that case, the timer will simply have to wait for the next + grpc_timer_check. */ + if (is_first_timer) { gpr_mu_lock(&g_mu); if (gpr_time_cmp(deadline, shard->min_deadline) < 0) { gpr_timespec old_min_deadline = g_shard_queue[0]->min_deadline; @@ -223,29 +220,24 @@ void grpc_alarm_init(grpc_alarm *alarm, gpr_timespec deadline, } } -void grpc_alarm_cancel(grpc_alarm *alarm) { - shard_type *shard = &g_shards[shard_idx(alarm)]; - int triggered = 0; +void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) { + shard_type *shard = &g_shards[shard_idx(timer)]; gpr_mu_lock(&shard->mu); - if (!alarm->triggered) { - triggered = 1; - alarm->triggered = 1; - if (alarm->heap_index == INVALID_HEAP_INDEX) { - list_remove(alarm); + if (!timer->triggered) { + grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, 0); + timer->triggered = 1; + if (timer->heap_index == INVALID_HEAP_INDEX) { + list_remove(timer); } else { - grpc_alarm_heap_remove(&shard->heap, alarm); + grpc_timer_heap_remove(&shard->heap, timer); } } gpr_mu_unlock(&shard->mu); - - if (triggered) { - alarm->cb(alarm->cb_arg, 0); - } } /* This is called when the queue is empty and "now" has reached the queue_deadline_cap. We compute a new queue deadline and then scan the map - for alarms that fall at or under it. Returns true if the queue is no + for timers that fall at or under it. Returns true if the queue is no longer empty. REQUIRES: shard->mu locked */ static int refill_queue(shard_type *shard, gpr_timespec now) { @@ -256,80 +248,78 @@ static int refill_queue(shard_type *shard, gpr_timespec now) { double deadline_delta = GPR_CLAMP(computed_deadline_delta, MIN_QUEUE_WINDOW_DURATION, MAX_QUEUE_WINDOW_DURATION); - grpc_alarm *alarm, *next; + grpc_timer *timer, *next; - /* Compute the new cap and put all alarms under it into the queue: */ + /* Compute the new cap and put all timers under it into the queue: */ shard->queue_deadline_cap = gpr_time_add( gpr_time_max(now, shard->queue_deadline_cap), dbl_to_ts(deadline_delta)); - for (alarm = shard->list.next; alarm != &shard->list; alarm = next) { - next = alarm->next; + for (timer = shard->list.next; timer != &shard->list; timer = next) { + next = timer->next; - if (gpr_time_cmp(alarm->deadline, shard->queue_deadline_cap) < 0) { - list_remove(alarm); - grpc_alarm_heap_add(&shard->heap, alarm); + if (gpr_time_cmp(timer->deadline, shard->queue_deadline_cap) < 0) { + list_remove(timer); + grpc_timer_heap_add(&shard->heap, timer); } } - return !grpc_alarm_heap_is_empty(&shard->heap); + return !grpc_timer_heap_is_empty(&shard->heap); } -/* This pops the next non-cancelled alarm with deadline <= now from the queue, +/* This pops the next non-cancelled timer with deadline <= now from the queue, or returns NULL if there isn't one. REQUIRES: shard->mu locked */ -static grpc_alarm *pop_one(shard_type *shard, gpr_timespec now) { - grpc_alarm *alarm; +static grpc_timer *pop_one(shard_type *shard, gpr_timespec now) { + grpc_timer *timer; for (;;) { - if (grpc_alarm_heap_is_empty(&shard->heap)) { + if (grpc_timer_heap_is_empty(&shard->heap)) { if (gpr_time_cmp(now, shard->queue_deadline_cap) < 0) return NULL; if (!refill_queue(shard, now)) return NULL; } - alarm = grpc_alarm_heap_top(&shard->heap); - if (gpr_time_cmp(alarm->deadline, now) > 0) return NULL; - alarm->triggered = 1; - grpc_alarm_heap_pop(&shard->heap); - return alarm; + timer = grpc_timer_heap_top(&shard->heap); + if (gpr_time_cmp(timer->deadline, now) > 0) return NULL; + timer->triggered = 1; + grpc_timer_heap_pop(&shard->heap); + return timer; } } /* REQUIRES: shard->mu unlocked */ -static size_t pop_alarms(shard_type *shard, gpr_timespec now, - grpc_alarm **alarms, size_t max_alarms, - gpr_timespec *new_min_deadline) { +static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard, + gpr_timespec now, gpr_timespec *new_min_deadline, + int success) { size_t n = 0; - grpc_alarm *alarm; + grpc_timer *timer; gpr_mu_lock(&shard->mu); - while (n < max_alarms && (alarm = pop_one(shard, now))) { - alarms[n++] = alarm; + while ((timer = pop_one(shard, now))) { + grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, success); + n++; } *new_min_deadline = compute_min_deadline(shard); gpr_mu_unlock(&shard->mu); return n; } -static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now, +static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now, gpr_timespec *next, int success) { size_t n = 0; - size_t i; - grpc_alarm *alarms[MAX_ALARMS_PER_CHECK]; - /* TODO(ctiller): verify that there are any alarms (atomically) here */ + /* TODO(ctiller): verify that there are any timers (atomically) here */ if (gpr_mu_trylock(&g_checker_mu)) { gpr_mu_lock(&g_mu); - while (n < MAX_ALARMS_PER_CHECK && - gpr_time_cmp(g_shard_queue[0]->min_deadline, now) < 0) { + while (gpr_time_cmp(g_shard_queue[0]->min_deadline, now) < 0) { gpr_timespec new_min_deadline; - /* For efficiency, we pop as many available alarms as we can from the - shard. This may violate perfect alarm deadline ordering, but that + /* For efficiency, we pop as many available timers as we can from the + shard. This may violate perfect timer deadline ordering, but that shouldn't be a big deal because we don't make ordering guarantees. */ - n += pop_alarms(g_shard_queue[0], now, alarms + n, - MAX_ALARMS_PER_CHECK - n, &new_min_deadline); + n += pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline, + success); - /* An grpc_alarm_init() on the shard could intervene here, adding a new - alarm that is earlier than new_min_deadline. However, - grpc_alarm_init() will block on the master_lock before it can call - set_min_deadline, so this one will complete first and then the AddAlarm + /* An grpc_timer_init() on the shard could intervene here, adding a new + timer that is earlier than new_min_deadline. However, + grpc_timer_init() will block on the master_lock before it can call + set_min_deadline, so this one will complete first and then the Addtimer will reduce the min_deadline (perhaps unnecessarily). */ g_shard_queue[0]->min_deadline = new_min_deadline; note_deadline_change(g_shard_queue[0]); @@ -343,29 +333,18 @@ static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now, gpr_mu_unlock(&g_checker_mu); } - if (n && drop_mu) { - gpr_mu_unlock(drop_mu); - } - - for (i = 0; i < n; i++) { - alarms[i]->cb(alarms[i]->cb_arg, success); - } - - if (n && drop_mu) { - gpr_mu_lock(drop_mu); - } - - return n; + return (int)n; } -int grpc_alarm_check(gpr_mu *drop_mu, gpr_timespec now, gpr_timespec *next) { +int grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now, + gpr_timespec *next) { GPR_ASSERT(now.clock_type == g_clock_type); - return run_some_expired_alarms( - drop_mu, now, next, + return run_some_expired_timers( + exec_ctx, now, next, gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0); } -gpr_timespec grpc_alarm_list_next_timeout(void) { +gpr_timespec grpc_timer_list_next_timeout(void) { gpr_timespec out; gpr_mu_lock(&g_mu); out = g_shard_queue[0]->min_deadline; diff --git a/src/core/iomgr/alarm.h b/src/core/iomgr/timer.h index 4a13527e64..9abe58133d 100644 --- a/src/core/iomgr/alarm.h +++ b/src/core/iomgr/timer.h @@ -31,59 +31,59 @@ * */ -#ifndef GRPC_INTERNAL_CORE_IOMGR_ALARM_H -#define GRPC_INTERNAL_CORE_IOMGR_ALARM_H +#ifndef GRPC_INTERNAL_CORE_IOMGR_TIMER_H +#define GRPC_INTERNAL_CORE_IOMGR_TIMER_H #include "src/core/iomgr/iomgr.h" +#include "src/core/iomgr/exec_ctx.h" #include <grpc/support/port_platform.h> #include <grpc/support/time.h> -typedef struct grpc_alarm { +typedef struct grpc_timer { gpr_timespec deadline; gpr_uint32 heap_index; /* INVALID_HEAP_INDEX if not in heap */ int triggered; - struct grpc_alarm *next; - struct grpc_alarm *prev; - grpc_iomgr_cb_func cb; - void *cb_arg; -} grpc_alarm; + struct grpc_timer *next; + struct grpc_timer *prev; + grpc_closure closure; +} grpc_timer; -/* Initialize *alarm. When expired or canceled, alarm_cb will be called with - *alarm_cb_arg and status to indicate if it expired (SUCCESS) or was - canceled (CANCELLED). alarm_cb is guaranteed to be called exactly once, +/* Initialize *timer. When expired or canceled, timer_cb will be called with + *timer_cb_arg and status to indicate if it expired (SUCCESS) or was + canceled (CANCELLED). timer_cb is guaranteed to be called exactly once, and application code should check the status to determine how it was invoked. The application callback is also responsible for maintaining information about when to free up any user-level state. */ -void grpc_alarm_init(grpc_alarm *alarm, gpr_timespec deadline, - grpc_iomgr_cb_func alarm_cb, void *alarm_cb_arg, - gpr_timespec now); +void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, + gpr_timespec deadline, grpc_iomgr_cb_func timer_cb, + void *timer_cb_arg, gpr_timespec now); -/* Note that there is no alarm destroy function. This is because the - alarm is a one-time occurrence with a guarantee that the callback will +/* Note that there is no timer destroy function. This is because the + timer is a one-time occurrence with a guarantee that the callback will be called exactly once, either at expiration or cancellation. Thus, all - the internal alarm event management state is destroyed just before + the internal timer event management state is destroyed just before that callback is invoked. If the user has additional state associated with - the alarm, the user is responsible for determining when it is safe to + the timer, the user is responsible for determining when it is safe to destroy that state. */ -/* Cancel an *alarm. +/* Cancel an *timer. There are three cases: - 1. We normally cancel the alarm - 2. The alarm has already run - 3. We can't cancel the alarm because it is "in flight". + 1. We normally cancel the timer + 2. The timer has already run + 3. We can't cancel the timer because it is "in flight". In all of these cases, the cancellation is still considered successful. - They are essentially distinguished in that the alarm_cb will be run + They are essentially distinguished in that the timer_cb will be run exactly once from either the cancellation (with status CANCELLED) or from the activation (with status SUCCESS) Note carefully that the callback function MAY occur in the same callstack - as grpc_alarm_cancel. It's expected that most alarms will be cancelled (their + as grpc_timer_cancel. It's expected that most timers will be cancelled (their primary use is to implement deadlines), and so this code is optimized such that cancellation costs as little as possible. Making callbacks run inline matches this aim. - Requires: cancel() must happen after add() on a given alarm */ -void grpc_alarm_cancel(grpc_alarm *alarm); + Requires: cancel() must happen after add() on a given timer */ +void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer); -#endif /* GRPC_INTERNAL_CORE_IOMGR_ALARM_H */ +#endif /* GRPC_INTERNAL_CORE_IOMGR_TIMER_H */ diff --git a/src/core/iomgr/alarm_heap.c b/src/core/iomgr/timer_heap.c index daed251982..31d41d6750 100644 --- a/src/core/iomgr/alarm_heap.c +++ b/src/core/iomgr/timer_heap.c @@ -31,7 +31,7 @@ * */ -#include "src/core/iomgr/alarm_heap.h" +#include "src/core/iomgr/timer_heap.h" #include <string.h> @@ -43,9 +43,9 @@ position. This functor is called each time immediately after modifying a value in the underlying container, with the offset of the modified element as its argument. */ -static void adjust_upwards(grpc_alarm **first, int i, grpc_alarm *t) { +static void adjust_upwards(grpc_timer **first, gpr_uint32 i, grpc_timer *t) { while (i > 0) { - int parent = (i - 1) / 2; + gpr_uint32 parent = (gpr_uint32)(((int)i - 1) / 2); if (gpr_time_cmp(first[parent]->deadline, t->deadline) >= 0) break; first[i] = first[parent]; first[i]->heap_index = i; @@ -58,12 +58,12 @@ static void adjust_upwards(grpc_alarm **first, int i, grpc_alarm *t) { /* Adjusts a heap so as to move a hole at position i farther away from the root, until a suitable position is found for element t. Then, copies t into that position. */ -static void adjust_downwards(grpc_alarm **first, int i, int length, - grpc_alarm *t) { +static void adjust_downwards(grpc_timer **first, gpr_uint32 i, + gpr_uint32 length, grpc_timer *t) { for (;;) { - int left_child = 1 + 2 * i; - int right_child; - int next_i; + gpr_uint32 left_child = 1u + 2u * i; + gpr_uint32 right_child; + gpr_uint32 next_i; if (left_child >= length) break; right_child = left_child + 1; next_i = right_child < length && @@ -83,66 +83,66 @@ static void adjust_downwards(grpc_alarm **first, int i, int length, #define SHRINK_MIN_ELEMS 8 #define SHRINK_FULLNESS_FACTOR 2 -static void maybe_shrink(grpc_alarm_heap *heap) { - if (heap->alarm_count >= 8 && - heap->alarm_count <= heap->alarm_capacity / SHRINK_FULLNESS_FACTOR / 2) { - heap->alarm_capacity = heap->alarm_count * SHRINK_FULLNESS_FACTOR; - heap->alarms = - gpr_realloc(heap->alarms, heap->alarm_capacity * sizeof(grpc_alarm *)); +static void maybe_shrink(grpc_timer_heap *heap) { + if (heap->timer_count >= 8 && + heap->timer_count <= heap->timer_capacity / SHRINK_FULLNESS_FACTOR / 2) { + heap->timer_capacity = heap->timer_count * SHRINK_FULLNESS_FACTOR; + heap->timers = + gpr_realloc(heap->timers, heap->timer_capacity * sizeof(grpc_timer *)); } } -static void note_changed_priority(grpc_alarm_heap *heap, grpc_alarm *alarm) { - int i = alarm->heap_index; - int parent = (i - 1) / 2; - if (gpr_time_cmp(heap->alarms[parent]->deadline, alarm->deadline) < 0) { - adjust_upwards(heap->alarms, i, alarm); +static void note_changed_priority(grpc_timer_heap *heap, grpc_timer *timer) { + gpr_uint32 i = timer->heap_index; + gpr_uint32 parent = (gpr_uint32)(((int)i - 1) / 2); + if (gpr_time_cmp(heap->timers[parent]->deadline, timer->deadline) < 0) { + adjust_upwards(heap->timers, i, timer); } else { - adjust_downwards(heap->alarms, i, heap->alarm_count, alarm); + adjust_downwards(heap->timers, i, heap->timer_count, timer); } } -void grpc_alarm_heap_init(grpc_alarm_heap *heap) { +void grpc_timer_heap_init(grpc_timer_heap *heap) { memset(heap, 0, sizeof(*heap)); } -void grpc_alarm_heap_destroy(grpc_alarm_heap *heap) { gpr_free(heap->alarms); } +void grpc_timer_heap_destroy(grpc_timer_heap *heap) { gpr_free(heap->timers); } -int grpc_alarm_heap_add(grpc_alarm_heap *heap, grpc_alarm *alarm) { - if (heap->alarm_count == heap->alarm_capacity) { - heap->alarm_capacity = - GPR_MAX(heap->alarm_capacity + 1, heap->alarm_capacity * 3 / 2); - heap->alarms = - gpr_realloc(heap->alarms, heap->alarm_capacity * sizeof(grpc_alarm *)); +int grpc_timer_heap_add(grpc_timer_heap *heap, grpc_timer *timer) { + if (heap->timer_count == heap->timer_capacity) { + heap->timer_capacity = + GPR_MAX(heap->timer_capacity + 1, heap->timer_capacity * 3 / 2); + heap->timers = + gpr_realloc(heap->timers, heap->timer_capacity * sizeof(grpc_timer *)); } - alarm->heap_index = heap->alarm_count; - adjust_upwards(heap->alarms, heap->alarm_count, alarm); - heap->alarm_count++; - return alarm->heap_index == 0; + timer->heap_index = heap->timer_count; + adjust_upwards(heap->timers, heap->timer_count, timer); + heap->timer_count++; + return timer->heap_index == 0; } -void grpc_alarm_heap_remove(grpc_alarm_heap *heap, grpc_alarm *alarm) { - int i = alarm->heap_index; - if (i == heap->alarm_count - 1) { - heap->alarm_count--; +void grpc_timer_heap_remove(grpc_timer_heap *heap, grpc_timer *timer) { + gpr_uint32 i = timer->heap_index; + if (i == heap->timer_count - 1) { + heap->timer_count--; maybe_shrink(heap); return; } - heap->alarms[i] = heap->alarms[heap->alarm_count - 1]; - heap->alarms[i]->heap_index = i; - heap->alarm_count--; + heap->timers[i] = heap->timers[heap->timer_count - 1]; + heap->timers[i]->heap_index = i; + heap->timer_count--; maybe_shrink(heap); - note_changed_priority(heap, heap->alarms[i]); + note_changed_priority(heap, heap->timers[i]); } -int grpc_alarm_heap_is_empty(grpc_alarm_heap *heap) { - return heap->alarm_count == 0; +int grpc_timer_heap_is_empty(grpc_timer_heap *heap) { + return heap->timer_count == 0; } -grpc_alarm *grpc_alarm_heap_top(grpc_alarm_heap *heap) { - return heap->alarms[0]; +grpc_timer *grpc_timer_heap_top(grpc_timer_heap *heap) { + return heap->timers[0]; } -void grpc_alarm_heap_pop(grpc_alarm_heap *heap) { - grpc_alarm_heap_remove(heap, grpc_alarm_heap_top(heap)); +void grpc_timer_heap_pop(grpc_timer_heap *heap) { + grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap)); } diff --git a/src/core/iomgr/alarm_heap.h b/src/core/iomgr/timer_heap.h index 60db6c991b..cd5258f93e 100644 --- a/src/core/iomgr/alarm_heap.h +++ b/src/core/iomgr/timer_heap.h @@ -31,27 +31,27 @@ * */ -#ifndef GRPC_INTERNAL_CORE_IOMGR_ALARM_HEAP_H -#define GRPC_INTERNAL_CORE_IOMGR_ALARM_HEAP_H +#ifndef GRPC_INTERNAL_CORE_IOMGR_TIMER_HEAP_H +#define GRPC_INTERNAL_CORE_IOMGR_TIMER_HEAP_H -#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/timer.h" typedef struct { - grpc_alarm **alarms; - int alarm_count; - int alarm_capacity; -} grpc_alarm_heap; + grpc_timer **timers; + gpr_uint32 timer_count; + gpr_uint32 timer_capacity; +} grpc_timer_heap; -/* return 1 if the new alarm is the first alarm in the heap */ -int grpc_alarm_heap_add(grpc_alarm_heap *heap, grpc_alarm *alarm); +/* return 1 if the new timer is the first timer in the heap */ +int grpc_timer_heap_add(grpc_timer_heap *heap, grpc_timer *timer); -void grpc_alarm_heap_init(grpc_alarm_heap *heap); -void grpc_alarm_heap_destroy(grpc_alarm_heap *heap); +void grpc_timer_heap_init(grpc_timer_heap *heap); +void grpc_timer_heap_destroy(grpc_timer_heap *heap); -void grpc_alarm_heap_remove(grpc_alarm_heap *heap, grpc_alarm *alarm); -grpc_alarm *grpc_alarm_heap_top(grpc_alarm_heap *heap); -void grpc_alarm_heap_pop(grpc_alarm_heap *heap); +void grpc_timer_heap_remove(grpc_timer_heap *heap, grpc_timer *timer); +grpc_timer *grpc_timer_heap_top(grpc_timer_heap *heap); +void grpc_timer_heap_pop(grpc_timer_heap *heap); -int grpc_alarm_heap_is_empty(grpc_alarm_heap *heap); +int grpc_timer_heap_is_empty(grpc_timer_heap *heap); -#endif /* GRPC_INTERNAL_CORE_IOMGR_ALARM_HEAP_H */ +#endif /* GRPC_INTERNAL_CORE_IOMGR_TIMER_HEAP_H */ diff --git a/src/core/iomgr/alarm_internal.h b/src/core/iomgr/timer_internal.h index e9f98a3444..f180eca36e 100644 --- a/src/core/iomgr/alarm_internal.h +++ b/src/core/iomgr/timer_internal.h @@ -31,32 +31,33 @@ * */ -#ifndef GRPC_INTERNAL_CORE_IOMGR_ALARM_INTERNAL_H -#define GRPC_INTERNAL_CORE_IOMGR_ALARM_INTERNAL_H +#ifndef GRPC_INTERNAL_CORE_IOMGR_TIMER_INTERNAL_H +#define GRPC_INTERNAL_CORE_IOMGR_TIMER_INTERNAL_H +#include "src/core/iomgr/exec_ctx.h" #include <grpc/support/sync.h> #include <grpc/support/time.h> -/* iomgr internal api for dealing with alarms */ +/* iomgr internal api for dealing with timers */ -/* Check for alarms to be run, and run them. - Return non zero if alarm callbacks were executed. +/* Check for timers to be run, and run them. + Return non zero if timer callbacks were executed. Drops drop_mu if it is non-null before executing callbacks. - If next is non-null, TRY to update *next with the next running alarm - IF that alarm occurs before *next current value. + If next is non-null, TRY to update *next with the next running timer + IF that timer occurs before *next current value. *next is never guaranteed to be updated on any given execution; however, with high probability at least one thread in the system will see an update at any time slice. */ -int grpc_alarm_check(gpr_mu *drop_mu, gpr_timespec now, gpr_timespec *next); +int grpc_timer_check(grpc_exec_ctx* exec_ctx, gpr_timespec now, + gpr_timespec* next); +void grpc_timer_list_init(gpr_timespec now); +void grpc_timer_list_shutdown(grpc_exec_ctx* exec_ctx); -void grpc_alarm_list_init(gpr_timespec now); -void grpc_alarm_list_shutdown(void); - -gpr_timespec grpc_alarm_list_next_timeout(void); +gpr_timespec grpc_timer_list_next_timeout(void); /* the following must be implemented by each iomgr implementation */ void grpc_kick_poller(void); -#endif /* GRPC_INTERNAL_CORE_IOMGR_ALARM_INTERNAL_H */ +#endif /* GRPC_INTERNAL_CORE_IOMGR_TIMER_INTERNAL_H */ diff --git a/src/core/iomgr/udp_server.c b/src/core/iomgr/udp_server.c index 6429c38b28..9903e970e6 100644 --- a/src/core/iomgr/udp_server.c +++ b/src/core/iomgr/udp_server.c @@ -78,9 +78,9 @@ typedef struct { struct sockaddr sockaddr; struct sockaddr_un un; } addr; - int addr_len; - grpc_iomgr_closure read_closure; - grpc_iomgr_closure destroyed_closure; + size_t addr_len; + grpc_closure read_closure; + grpc_closure destroyed_closure; grpc_udp_server_read_cb read_cb; } server_port; @@ -94,9 +94,6 @@ static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) { /* the overall server */ struct grpc_udp_server { - grpc_udp_server_cb cb; - void *cb_arg; - gpr_mu mu; gpr_cv cv; @@ -114,13 +111,14 @@ struct grpc_udp_server { size_t port_capacity; /* shutdown callback */ - void (*shutdown_complete)(void *); - void *shutdown_complete_arg; + grpc_closure *shutdown_complete; /* all pollsets interested in new connections */ grpc_pollset **pollsets; /* number of pollsets in the pollsets array */ size_t pollset_count; + /* The parent grpc server */ + grpc_server *grpc_server; }; grpc_udp_server *grpc_udp_server_create(void) { @@ -130,8 +128,6 @@ grpc_udp_server *grpc_udp_server_create(void) { s->active_ports = 0; s->destroyed_ports = 0; s->shutdown = 0; - s->cb = NULL; - s->cb_arg = NULL; s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); s->nports = 0; s->port_capacity = INIT_PORT_CAP; @@ -139,8 +135,8 @@ grpc_udp_server *grpc_udp_server_create(void) { return s; } -static void finish_shutdown(grpc_udp_server *s) { - s->shutdown_complete(s->shutdown_complete_arg); +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { + grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, 1); gpr_mu_destroy(&s->mu); gpr_cv_destroy(&s->cv); @@ -149,24 +145,22 @@ static void finish_shutdown(grpc_udp_server *s) { gpr_free(s); } -static void destroyed_port(void *server, int success) { +static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, int success) { grpc_udp_server *s = server; gpr_mu_lock(&s->mu); s->destroyed_ports++; if (s->destroyed_ports == s->nports) { gpr_mu_unlock(&s->mu); - finish_shutdown(s); + finish_shutdown(exec_ctx, s); } else { gpr_mu_unlock(&s->mu); } } -static void dont_care_about_shutdown_completion(void *ignored) {} - /* called when all listening endpoints have been shutdown, so no further events will be received on them - at this point it's safe to destroy things */ -static void deactivated_all_ports(grpc_udp_server *s) { +static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { size_t i; /* delete ALL the things */ @@ -185,43 +179,41 @@ static void deactivated_all_ports(grpc_udp_server *s) { } sp->destroyed_closure.cb = destroyed_port; sp->destroyed_closure.cb_arg = s; - grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, "udp_listener_shutdown"); + grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, + "udp_listener_shutdown"); } gpr_mu_unlock(&s->mu); } else { gpr_mu_unlock(&s->mu); - finish_shutdown(s); + finish_shutdown(exec_ctx, s); } } -void grpc_udp_server_destroy( - grpc_udp_server *s, void (*shutdown_complete)(void *shutdown_complete_arg), - void *shutdown_complete_arg) { +void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, + grpc_closure *on_done) { size_t i; gpr_mu_lock(&s->mu); GPR_ASSERT(!s->shutdown); s->shutdown = 1; - s->shutdown_complete = shutdown_complete - ? shutdown_complete - : dont_care_about_shutdown_completion; - s->shutdown_complete_arg = shutdown_complete_arg; + s->shutdown_complete = on_done; /* shutdown all fd's */ if (s->active_ports) { for (i = 0; i < s->nports; i++) { - grpc_fd_shutdown(s->ports[i].emfd); + grpc_fd_shutdown(exec_ctx, s->ports[i].emfd); } gpr_mu_unlock(&s->mu); } else { gpr_mu_unlock(&s->mu); - deactivated_all_ports(s); + deactivated_all_ports(exec_ctx, s); } } /* Prepare a recently-created socket for listening. */ -static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { +static int prepare_socket(int fd, const struct sockaddr *addr, + size_t addr_len) { struct sockaddr_storage sockname_temp; socklen_t sockname_len; int get_local_ip; @@ -231,17 +223,23 @@ static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { goto error; } + if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1)) { + gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd, + strerror(errno)); + } + get_local_ip = 1; rc = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip, sizeof(get_local_ip)); if (rc == 0 && addr->sa_family == AF_INET6) { -#if !TARGET_OS_IPHONE +#if !defined(__APPLE__) rc = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip, sizeof(get_local_ip)); #endif } - if (bind(fd, addr, addr_len) < 0) { + GPR_ASSERT(addr_len < ~(socklen_t)0); + if (bind(fd, addr, (socklen_t)addr_len) < 0) { char *addr_str; grpc_sockaddr_to_string(&addr_str, addr, 0); gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno)); @@ -264,14 +262,14 @@ error: } /* event manager callback when reads are ready */ -static void on_read(void *arg, int success) { +static void on_read(grpc_exec_ctx *exec_ctx, void *arg, int success) { server_port *sp = arg; if (success == 0) { gpr_mu_lock(&sp->server->mu); if (0 == --sp->server->active_ports) { gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(sp->server); + deactivated_all_ports(exec_ctx, sp->server); } else { gpr_mu_unlock(&sp->server->mu); } @@ -280,14 +278,14 @@ static void on_read(void *arg, int success) { /* Tell the registered callback that data is available to read. */ GPR_ASSERT(sp->read_cb); - sp->read_cb(sp->fd, sp->server->cb, sp->server->cb_arg); + sp->read_cb(exec_ctx, sp->emfd, sp->server->grpc_server); /* Re-arm the notification event so we get another chance to read. */ - grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); } static int add_socket_to_server(grpc_udp_server *s, int fd, - const struct sockaddr *addr, int addr_len, + const struct sockaddr *addr, size_t addr_len, grpc_udp_server_read_cb read_cb) { server_port *sp; int port; @@ -298,8 +296,8 @@ static int add_socket_to_server(grpc_udp_server *s, int fd, if (port >= 0) { grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1); gpr_asprintf(&name, "udp-server-listener:%s", addr_str); + gpr_free(addr_str); gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->cb && "must add ports before starting server"); /* append it to the list under a lock */ if (s->nports == s->port_capacity) { s->port_capacity *= 2; @@ -314,13 +312,14 @@ static int add_socket_to_server(grpc_udp_server *s, int fd, sp->read_cb = read_cb; GPR_ASSERT(sp->emfd); gpr_mu_unlock(&s->mu); + gpr_free(name); } return port; } -int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, int addr_len, - grpc_udp_server_read_cb read_cb) { +int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, + size_t addr_len, grpc_udp_server_read_cb read_cb) { int allocated_port1 = -1; int allocated_port2 = -1; unsigned i; @@ -400,28 +399,26 @@ done: return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; } -int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned index) { - return (index < s->nports) ? s->ports[index].fd : -1; +int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned port_index) { + return (port_index < s->nports) ? s->ports[port_index].fd : -1; } -void grpc_udp_server_start(grpc_udp_server *s, grpc_pollset **pollsets, - size_t pollset_count, - grpc_udp_server_cb new_transport_cb, void *cb_arg) { +void grpc_udp_server_start(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, + grpc_pollset **pollsets, size_t pollset_count, + grpc_server *server) { size_t i, j; - GPR_ASSERT(new_transport_cb); gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->cb); GPR_ASSERT(s->active_ports == 0); - s->cb = new_transport_cb; - s->cb_arg = cb_arg; s->pollsets = pollsets; + s->grpc_server = server; for (i = 0; i < s->nports; i++) { for (j = 0; j < pollset_count; j++) { - grpc_pollset_add_fd(pollsets[j], s->ports[i].emfd); + grpc_pollset_add_fd(exec_ctx, pollsets[j], s->ports[i].emfd); } s->ports[i].read_closure.cb = on_read; s->ports[i].read_closure.cb_arg = &s->ports[i]; - grpc_fd_notify_on_read(s->ports[i].emfd, &s->ports[i].read_closure); + grpc_fd_notify_on_read(exec_ctx, s->ports[i].emfd, + &s->ports[i].read_closure); s->active_ports++; } gpr_mu_unlock(&s->mu); @@ -430,7 +427,7 @@ void grpc_udp_server_start(grpc_udp_server *s, grpc_pollset **pollsets, /* TODO(rjshade): Add a test for this method. */ void grpc_udp_server_write(server_port *sp, const char *buffer, size_t buf_len, const struct sockaddr *peer_address) { - int rc; + ssize_t rc; rc = sendto(sp->fd, buffer, buf_len, 0, peer_address, sizeof(peer_address)); if (rc < 0) { gpr_log(GPR_ERROR, "Unable to send data: %s", strerror(errno)); diff --git a/src/core/iomgr/udp_server.h b/src/core/iomgr/udp_server.h index fcc4ba6e97..de5736c426 100644 --- a/src/core/iomgr/udp_server.h +++ b/src/core/iomgr/udp_server.h @@ -36,24 +36,23 @@ #include "src/core/iomgr/endpoint.h" +/* Forward decl of grpc_server */ +typedef struct grpc_server grpc_server; + /* Forward decl of grpc_udp_server */ typedef struct grpc_udp_server grpc_udp_server; -/* New server callback: ep is the newly connected connection */ -typedef void (*grpc_udp_server_cb)(void *arg, grpc_endpoint *ep); - /* Called when data is available to read from the socket. */ -typedef void (*grpc_udp_server_read_cb)(int fd, - grpc_udp_server_cb new_transport_cb, - void *cb_arg); +typedef void (*grpc_udp_server_read_cb)(grpc_exec_ctx *exec_ctx, grpc_fd *emfd, + grpc_server *server); /* Create a server, initially not bound to any ports */ grpc_udp_server *grpc_udp_server_create(void); /* Start listening to bound ports */ -void grpc_udp_server_start(grpc_udp_server *server, grpc_pollset **pollsets, - size_t pollset_count, grpc_udp_server_cb cb, - void *cb_arg); +void grpc_udp_server_start(grpc_exec_ctx *exec_ctx, grpc_udp_server *udp_server, + grpc_pollset **pollsets, size_t pollset_count, + grpc_server *server); int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned index); @@ -67,12 +66,11 @@ int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned index); /* TODO(ctiller): deprecate this, and make grpc_udp_server_add_ports to handle all of the multiple socket port matching logic in one place */ -int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, int addr_len, - grpc_udp_server_read_cb read_cb); +int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, + size_t addr_len, grpc_udp_server_read_cb read_cb); -void grpc_udp_server_destroy(grpc_udp_server *server, - void (*shutdown_done)(void *shutdown_done_arg), - void *shutdown_done_arg); +void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *server, + grpc_closure *on_done); /* Write the contents of buffer to the underlying UDP socket. */ /* diff --git a/src/core/iomgr/wakeup_fd_eventfd.c b/src/core/iomgr/wakeup_fd_eventfd.c index 08fdc74f17..f67379e4fc 100644 --- a/src/core/iomgr/wakeup_fd_eventfd.c +++ b/src/core/iomgr/wakeup_fd_eventfd.c @@ -39,10 +39,12 @@ #include <sys/eventfd.h> #include <unistd.h> -#include "src/core/iomgr/wakeup_fd_posix.h" #include <grpc/support/log.h> -static void eventfd_create(grpc_wakeup_fd *fd_info) { +#include "src/core/iomgr/wakeup_fd_posix.h" +#include "src/core/profiling/timers.h" + +static void eventfd_create(grpc_wakeup_fd* fd_info) { int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); /* TODO(klempner): Handle failure more gracefully */ GPR_ASSERT(efd >= 0); @@ -50,7 +52,7 @@ static void eventfd_create(grpc_wakeup_fd *fd_info) { fd_info->write_fd = -1; } -static void eventfd_consume(grpc_wakeup_fd *fd_info) { +static void eventfd_consume(grpc_wakeup_fd* fd_info) { eventfd_t value; int err; do { @@ -58,15 +60,17 @@ static void eventfd_consume(grpc_wakeup_fd *fd_info) { } while (err < 0 && errno == EINTR); } -static void eventfd_wakeup(grpc_wakeup_fd *fd_info) { +static void eventfd_wakeup(grpc_wakeup_fd* fd_info) { int err; + GPR_TIMER_BEGIN("eventfd_wakeup", 0); do { err = eventfd_write(fd_info->read_fd, 1); } while (err < 0 && errno == EINTR); + GPR_TIMER_END("eventfd_wakeup", 0); } -static void eventfd_destroy(grpc_wakeup_fd *fd_info) { - close(fd_info->read_fd); +static void eventfd_destroy(grpc_wakeup_fd* fd_info) { + if (fd_info->read_fd != 0) close(fd_info->read_fd); } static int eventfd_check_availability(void) { diff --git a/src/core/iomgr/wakeup_fd_pipe.c b/src/core/iomgr/wakeup_fd_pipe.c index bd643e8061..80de181d9d 100644 --- a/src/core/iomgr/wakeup_fd_pipe.c +++ b/src/core/iomgr/wakeup_fd_pipe.c @@ -44,7 +44,7 @@ #include "src/core/iomgr/socket_utils_posix.h" #include <grpc/support/log.h> -static void pipe_init(grpc_wakeup_fd *fd_info) { +static void pipe_init(grpc_wakeup_fd* fd_info) { int pipefd[2]; /* TODO(klempner): Make this nonfatal */ GPR_ASSERT(0 == pipe(pipefd)); @@ -54,9 +54,9 @@ static void pipe_init(grpc_wakeup_fd *fd_info) { fd_info->write_fd = pipefd[1]; } -static void pipe_consume(grpc_wakeup_fd *fd_info) { +static void pipe_consume(grpc_wakeup_fd* fd_info) { char buf[128]; - int r; + ssize_t r; for (;;) { r = read(fd_info->read_fd, buf, sizeof(buf)); @@ -74,15 +74,15 @@ static void pipe_consume(grpc_wakeup_fd *fd_info) { } } -static void pipe_wakeup(grpc_wakeup_fd *fd_info) { +static void pipe_wakeup(grpc_wakeup_fd* fd_info) { char c = 0; while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR) ; } -static void pipe_destroy(grpc_wakeup_fd *fd_info) { - close(fd_info->read_fd); - close(fd_info->write_fd); +static void pipe_destroy(grpc_wakeup_fd* fd_info) { + if (fd_info->read_fd != 0) close(fd_info->read_fd); + if (fd_info->write_fd != 0) close(fd_info->write_fd); } static int pipe_check_availability(void) { diff --git a/src/core/iomgr/wakeup_fd_posix.h b/src/core/iomgr/wakeup_fd_posix.h index b6c086900d..fe71b5abe9 100644 --- a/src/core/iomgr/wakeup_fd_posix.h +++ b/src/core/iomgr/wakeup_fd_posix.h @@ -72,10 +72,10 @@ void grpc_wakeup_fd_global_init_force_fallback(void); typedef struct grpc_wakeup_fd grpc_wakeup_fd; typedef struct grpc_wakeup_fd_vtable { - void (*init)(grpc_wakeup_fd *fd_info); - void (*consume)(grpc_wakeup_fd *fd_info); - void (*wakeup)(grpc_wakeup_fd *fd_info); - void (*destroy)(grpc_wakeup_fd *fd_info); + void (*init)(grpc_wakeup_fd* fd_info); + void (*consume)(grpc_wakeup_fd* fd_info); + void (*wakeup)(grpc_wakeup_fd* fd_info); + void (*destroy)(grpc_wakeup_fd* fd_info); /* Must be called before calling any other functions */ int (*check_availability)(void); } grpc_wakeup_fd_vtable; @@ -87,10 +87,10 @@ struct grpc_wakeup_fd { #define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd) -void grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info); -void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info); -void grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info); -void grpc_wakeup_fd_destroy(grpc_wakeup_fd *fd_info); +void grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info); +void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info); +void grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info); +void grpc_wakeup_fd_destroy(grpc_wakeup_fd* fd_info); /* Defined in some specialized implementation's .c file, or by * wakeup_fd_nospecial.c if no such implementation exists. */ diff --git a/src/core/iomgr/workqueue.h b/src/core/iomgr/workqueue.h new file mode 100644 index 0000000000..714536233c --- /dev/null +++ b/src/core/iomgr/workqueue.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_H +#define GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_H + +#include "src/core/iomgr/iomgr.h" +#include "src/core/iomgr/pollset.h" +#include "src/core/iomgr/closure.h" +#include "src/core/iomgr/exec_ctx.h" + +#ifdef GPR_POSIX_SOCKET +#include "src/core/iomgr/workqueue_posix.h" +#endif + +#ifdef GPR_WIN32 +#include "src/core/iomgr/workqueue_windows.h" +#endif + +/** A workqueue represents a list of work to be executed asynchronously. */ +struct grpc_workqueue; +typedef struct grpc_workqueue grpc_workqueue; + +/** Create a work queue */ +grpc_workqueue *grpc_workqueue_create(grpc_exec_ctx *exec_ctx); + +void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue); + +#define GRPC_WORKQUEUE_REFCOUNT_DEBUG +#ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG +#define GRPC_WORKQUEUE_REF(p, r) \ + grpc_workqueue_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_WORKQUEUE_UNREF(cl, p, r) \ + grpc_workqueue_unref((cl), (p), __FILE__, __LINE__, (r)) +void grpc_workqueue_ref(grpc_workqueue *workqueue, const char *file, int line, + const char *reason); +void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue, + const char *file, int line, const char *reason); +#else +#define GRPC_WORKQUEUE_REF(p, r) grpc_workqueue_ref((p)) +#define GRPC_WORKQUEUE_UNREF(cl, p, r) grpc_workqueue_unref((cl), (p)) +void grpc_workqueue_ref(grpc_workqueue *workqueue); +void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue); +#endif + +/** Bind this workqueue to a pollset */ +void grpc_workqueue_add_to_pollset(grpc_exec_ctx *exec_ctx, + grpc_workqueue *workqueue, + grpc_pollset *pollset); + +/** Add a work item to a workqueue */ +void grpc_workqueue_push(grpc_workqueue *workqueue, grpc_closure *closure, + int success); + +#endif diff --git a/src/core/iomgr/workqueue_posix.c b/src/core/iomgr/workqueue_posix.c new file mode 100644 index 0000000000..0a0f3c364e --- /dev/null +++ b/src/core/iomgr/workqueue_posix.c @@ -0,0 +1,142 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/workqueue.h" + +#include <stdio.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/useful.h> + +#include "src/core/iomgr/fd_posix.h" + +static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, int success); + +grpc_workqueue *grpc_workqueue_create(grpc_exec_ctx *exec_ctx) { + char name[32]; + grpc_workqueue *workqueue = gpr_malloc(sizeof(grpc_workqueue)); + gpr_ref_init(&workqueue->refs, 1); + gpr_mu_init(&workqueue->mu); + workqueue->closure_list.head = workqueue->closure_list.tail = NULL; + grpc_wakeup_fd_init(&workqueue->wakeup_fd); + sprintf(name, "workqueue:%p", (void *)workqueue); + workqueue->wakeup_read_fd = + grpc_fd_create(GRPC_WAKEUP_FD_GET_READ_FD(&workqueue->wakeup_fd), name); + grpc_closure_init(&workqueue->read_closure, on_readable, workqueue); + grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd, + &workqueue->read_closure); + return workqueue; +} + +static void workqueue_destroy(grpc_exec_ctx *exec_ctx, + grpc_workqueue *workqueue) { + GPR_ASSERT(grpc_closure_list_empty(workqueue->closure_list)); + grpc_fd_shutdown(exec_ctx, workqueue->wakeup_read_fd); +} + +#ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG +void grpc_workqueue_ref(grpc_workqueue *workqueue, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "WORKQUEUE:%p ref %d -> %d %s", + workqueue, (int)workqueue->refs.count, (int)workqueue->refs.count + 1, + reason); +#else +void grpc_workqueue_ref(grpc_workqueue *workqueue) { +#endif + gpr_ref(&workqueue->refs); +} + +#ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG +void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue, + const char *file, int line, const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "WORKQUEUE:%p unref %d -> %d %s", + workqueue, (int)workqueue->refs.count, (int)workqueue->refs.count - 1, + reason); +#else +void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) { +#endif + if (gpr_unref(&workqueue->refs)) { + workqueue_destroy(exec_ctx, workqueue); + } +} + +void grpc_workqueue_add_to_pollset(grpc_exec_ctx *exec_ctx, + grpc_workqueue *workqueue, + grpc_pollset *pollset) { + grpc_pollset_add_fd(exec_ctx, pollset, workqueue->wakeup_read_fd); +} + +void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) { + gpr_mu_lock(&workqueue->mu); + grpc_closure_list_move(&exec_ctx->closure_list, &workqueue->closure_list); + gpr_mu_unlock(&workqueue->mu); +} + +static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, int success) { + grpc_workqueue *workqueue = arg; + + if (!success) { + gpr_mu_destroy(&workqueue->mu); + /* HACK: let wakeup_fd code know that we stole the fd */ + workqueue->wakeup_fd.read_fd = 0; + grpc_wakeup_fd_destroy(&workqueue->wakeup_fd); + grpc_fd_orphan(exec_ctx, workqueue->wakeup_read_fd, NULL, "destroy"); + gpr_free(workqueue); + } else { + gpr_mu_lock(&workqueue->mu); + grpc_closure_list_move(&workqueue->closure_list, &exec_ctx->closure_list); + grpc_wakeup_fd_consume_wakeup(&workqueue->wakeup_fd); + gpr_mu_unlock(&workqueue->mu); + grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd, + &workqueue->read_closure); + } +} + +void grpc_workqueue_push(grpc_workqueue *workqueue, grpc_closure *closure, + int success) { + closure->success = success; + closure->next = NULL; + gpr_mu_lock(&workqueue->mu); + if (grpc_closure_list_empty(workqueue->closure_list)) { + grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd); + } + grpc_closure_list_add(&workqueue->closure_list, closure, success); + gpr_mu_unlock(&workqueue->mu); +} + +#endif /* GPR_POSIX_SOCKET */ diff --git a/src/core/iomgr/workqueue_posix.h b/src/core/iomgr/workqueue_posix.h new file mode 100644 index 0000000000..589034fe1b --- /dev/null +++ b/src/core/iomgr/workqueue_posix.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_POSIX_H + +struct grpc_fd; + +struct grpc_workqueue { + gpr_refcount refs; + + gpr_mu mu; + grpc_closure_list closure_list; + + grpc_wakeup_fd wakeup_fd; + struct grpc_fd *wakeup_read_fd; + + grpc_closure read_closure; +}; + +#endif /* GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_POSIX_H */ diff --git a/src/node/cli/service_packager/index.js b/src/core/iomgr/workqueue_windows.c index 811e08b89a..f9ca57557b 100644 --- a/src/node/cli/service_packager/index.js +++ b/src/core/iomgr/workqueue_windows.c @@ -31,6 +31,10 @@ * */ -var grpc = require('grpc'); -exports.client = grpc.load(__dirname + '/service.json', 'json'); -exports.auth = require('google-auth-library'); +#include <grpc/support/port_platform.h> + +#ifdef GPR_WIN32 + +#include "src/core/iomgr/workqueue.h" + +#endif /* GPR_WIN32 */ diff --git a/src/core/iomgr/workqueue_windows.h b/src/core/iomgr/workqueue_windows.h new file mode 100644 index 0000000000..941f195f51 --- /dev/null +++ b/src/core/iomgr/workqueue_windows.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_WINDOWS_H +#define GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_WINDOWS_H + +#endif /* GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_WINDOWS_H */ diff --git a/src/core/json/json.h b/src/core/json/json.h index 573584bf6f..c4df2998c3 100644 --- a/src/core/json/json.h +++ b/src/core/json/json.h @@ -42,14 +42,14 @@ * are not owned by it. */ typedef struct grpc_json { - struct grpc_json* next; - struct grpc_json* prev; - struct grpc_json* child; - struct grpc_json* parent; + struct grpc_json *next; + struct grpc_json *prev; + struct grpc_json *child; + struct grpc_json *parent; grpc_json_type type; - const char* key; - const char* value; + const char *key; + const char *value; } grpc_json; /* The next two functions are going to parse the input string, and @@ -65,8 +65,8 @@ typedef struct grpc_json { * * Delete the allocated tree afterward using grpc_json_destroy(). */ -grpc_json* grpc_json_parse_string_with_len(char* input, size_t size); -grpc_json* grpc_json_parse_string(char* input); +grpc_json *grpc_json_parse_string_with_len(char *input, size_t size); +grpc_json *grpc_json_parse_string(char *input); /* This function will create a new string using gpr_realloc, and will * deserialize the grpc_json tree into it. It'll be zero-terminated, @@ -76,13 +76,13 @@ grpc_json* grpc_json_parse_string(char* input); * If indent is 0, then newlines will be suppressed as well, and the * output will be condensed at its maximum. */ -char* grpc_json_dump_to_string(grpc_json* json, int indent); +char *grpc_json_dump_to_string(grpc_json *json, int indent); /* Use these to create or delete a grpc_json object. * Deletion is recursive. We will not attempt to free any of the strings * in any of the objects of that tree. */ -grpc_json* grpc_json_create(grpc_json_type type); -void grpc_json_destroy(grpc_json* json); +grpc_json *grpc_json_create(grpc_json_type type); +void grpc_json_destroy(grpc_json *json); #endif /* GRPC_INTERNAL_CORE_JSON_JSON_H */ diff --git a/src/core/json/json_reader.c b/src/core/json/json_reader.c index c22d4edd47..8abad01252 100644 --- a/src/core/json/json_reader.c +++ b/src/core/json/json_reader.c @@ -37,61 +37,61 @@ #include "src/core/json/json_reader.h" -static void json_reader_string_clear(grpc_json_reader* reader) { +static void json_reader_string_clear(grpc_json_reader *reader) { reader->vtable->string_clear(reader->userdata); } -static void json_reader_string_add_char(grpc_json_reader* reader, +static void json_reader_string_add_char(grpc_json_reader *reader, gpr_uint32 c) { reader->vtable->string_add_char(reader->userdata, c); } -static void json_reader_string_add_utf32(grpc_json_reader* reader, +static void json_reader_string_add_utf32(grpc_json_reader *reader, gpr_uint32 utf32) { reader->vtable->string_add_utf32(reader->userdata, utf32); } -static gpr_uint32 grpc_json_reader_read_char(grpc_json_reader* reader) { +static gpr_uint32 grpc_json_reader_read_char(grpc_json_reader *reader) { return reader->vtable->read_char(reader->userdata); } -static void json_reader_container_begins(grpc_json_reader* reader, +static void json_reader_container_begins(grpc_json_reader *reader, grpc_json_type type) { reader->vtable->container_begins(reader->userdata, type); } static grpc_json_type grpc_json_reader_container_ends( - grpc_json_reader* reader) { + grpc_json_reader *reader) { return reader->vtable->container_ends(reader->userdata); } -static void json_reader_set_key(grpc_json_reader* reader) { +static void json_reader_set_key(grpc_json_reader *reader) { reader->vtable->set_key(reader->userdata); } -static void json_reader_set_string(grpc_json_reader* reader) { +static void json_reader_set_string(grpc_json_reader *reader) { reader->vtable->set_string(reader->userdata); } -static int json_reader_set_number(grpc_json_reader* reader) { +static int json_reader_set_number(grpc_json_reader *reader) { return reader->vtable->set_number(reader->userdata); } -static void json_reader_set_true(grpc_json_reader* reader) { +static void json_reader_set_true(grpc_json_reader *reader) { reader->vtable->set_true(reader->userdata); } -static void json_reader_set_false(grpc_json_reader* reader) { +static void json_reader_set_false(grpc_json_reader *reader) { reader->vtable->set_false(reader->userdata); } -static void json_reader_set_null(grpc_json_reader* reader) { +static void json_reader_set_null(grpc_json_reader *reader) { reader->vtable->set_null(reader->userdata); } /* Call this function to initialize the reader structure. */ -void grpc_json_reader_init(grpc_json_reader* reader, - grpc_json_reader_vtable* vtable, void* userdata) { +void grpc_json_reader_init(grpc_json_reader *reader, + grpc_json_reader_vtable *vtable, void *userdata) { memset(reader, 0, sizeof(*reader)); reader->vtable = vtable; reader->userdata = userdata; @@ -99,13 +99,13 @@ void grpc_json_reader_init(grpc_json_reader* reader, reader->state = GRPC_JSON_STATE_VALUE_BEGIN; } -int grpc_json_reader_is_complete(grpc_json_reader* reader) { +int grpc_json_reader_is_complete(grpc_json_reader *reader) { return ((reader->depth == 0) && ((reader->state == GRPC_JSON_STATE_END) || (reader->state == GRPC_JSON_STATE_VALUE_END))); } -grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader) { +grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { gpr_uint32 c, success; /* This state-machine is a strict implementation of ECMA-404 */ diff --git a/src/core/json/json_reader.h b/src/core/json/json_reader.h index 4d5487f790..417db110cf 100644 --- a/src/core/json/json_reader.h +++ b/src/core/json/json_reader.h @@ -82,27 +82,27 @@ struct grpc_json_reader; typedef struct grpc_json_reader_vtable { /* Clears your internal string scratchpad. */ - void (*string_clear)(void* userdata); + void (*string_clear)(void *userdata); /* Adds a char to the string scratchpad. */ - void (*string_add_char)(void* userdata, gpr_uint32 c); + void (*string_add_char)(void *userdata, gpr_uint32 c); /* Adds a utf32 char to the string scratchpad. */ - void (*string_add_utf32)(void* userdata, gpr_uint32 c); + void (*string_add_utf32)(void *userdata, gpr_uint32 c); /* Reads a character from your input. May be utf-8, 16 or 32. */ - gpr_uint32 (*read_char)(void* userdata); + gpr_uint32 (*read_char)(void *userdata); /* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */ - void (*container_begins)(void* userdata, grpc_json_type type); + void (*container_begins)(void *userdata, grpc_json_type type); /* Ends the current container. Must return the type of its parent. */ - grpc_json_type (*container_ends)(void* userdata); + grpc_json_type (*container_ends)(void *userdata); /* Your internal string scratchpad is an object's key. */ - void (*set_key)(void* userdata); + void (*set_key)(void *userdata); /* Your internal string scratchpad is a string value. */ - void (*set_string)(void* userdata); + void (*set_string)(void *userdata); /* Your internal string scratchpad is a numerical value. Return 1 if valid. */ - int (*set_number)(void* userdata); + int (*set_number)(void *userdata); /* Sets the values true, false or null. */ - void (*set_true)(void* userdata); - void (*set_false)(void* userdata); - void (*set_null)(void* userdata); + void (*set_true)(void *userdata); + void (*set_false)(void *userdata); + void (*set_null)(void *userdata); } grpc_json_reader_vtable; typedef struct grpc_json_reader { @@ -110,8 +110,8 @@ typedef struct grpc_json_reader { * The definition is public so you can put it on your stack. */ - void* userdata; - grpc_json_reader_vtable* vtable; + void *userdata; + grpc_json_reader_vtable *vtable; int depth; int in_object; int in_array; @@ -144,17 +144,17 @@ typedef enum { * . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid * internal state. */ -grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader); +grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader); /* Call this function to initialize the reader structure. */ -void grpc_json_reader_init(grpc_json_reader* reader, - grpc_json_reader_vtable* vtable, void* userdata); +void grpc_json_reader_init(grpc_json_reader *reader, + grpc_json_reader_vtable *vtable, void *userdata); /* You may call this from the read_char callback if you don't know where is the * end of your input stream, and you'd like the json reader to hint you that it * has completed reading its input, so you can return an EOF to it. Note that * there might still be trailing whitespaces after that point. */ -int grpc_json_reader_is_complete(grpc_json_reader* reader); +int grpc_json_reader_is_complete(grpc_json_reader *reader); #endif /* GRPC_INTERNAL_CORE_JSON_JSON_READER_H */ diff --git a/src/core/json/json_string.c b/src/core/json/json_string.c index e6622ec461..0461c2703f 100644 --- a/src/core/json/json_string.c +++ b/src/core/json/json_string.c @@ -53,13 +53,13 @@ * input size, and never expands it. */ typedef struct { - grpc_json* top; - grpc_json* current_container; - grpc_json* current_value; - gpr_uint8* input; - gpr_uint8* key; - gpr_uint8* string; - gpr_uint8* string_ptr; + grpc_json *top; + grpc_json *current_container; + grpc_json *current_value; + gpr_uint8 *input; + gpr_uint8 *key; + gpr_uint8 *string; + gpr_uint8 *string_ptr; size_t remaining_input; } json_reader_userdata; @@ -67,7 +67,7 @@ typedef struct { * The point is that we allocate that string in chunks of 256 bytes. */ typedef struct { - char* output; + char *output; size_t free_space; size_t string_len; size_t allocated; @@ -77,8 +77,8 @@ typedef struct { * and will enlarge it if necessary. We're only allocating chunks of 256 * bytes at a time (or multiples thereof). */ -static void json_writer_output_check(void* userdata, size_t needed) { - json_writer_userdata* state = userdata; +static void json_writer_output_check(void *userdata, size_t needed) { + json_writer_userdata *state = userdata; if (state->free_space >= needed) return; needed -= state->free_space; /* Round up by 256 bytes. */ @@ -89,23 +89,23 @@ static void json_writer_output_check(void* userdata, size_t needed) { } /* These are needed by the writer's implementation. */ -static void json_writer_output_char(void* userdata, char c) { - json_writer_userdata* state = userdata; +static void json_writer_output_char(void *userdata, char c) { + json_writer_userdata *state = userdata; json_writer_output_check(userdata, 1); state->output[state->string_len++] = c; state->free_space--; } -static void json_writer_output_string_with_len(void* userdata, const char* str, +static void json_writer_output_string_with_len(void *userdata, const char *str, size_t len) { - json_writer_userdata* state = userdata; + json_writer_userdata *state = userdata; json_writer_output_check(userdata, len); memcpy(state->output + state->string_len, str, len); state->string_len += len; state->free_space -= len; } -static void json_writer_output_string(void* userdata, const char* str) { +static void json_writer_output_string(void *userdata, const char *str) { size_t len = strlen(str); json_writer_output_string_with_len(userdata, str, len); } @@ -113,8 +113,8 @@ static void json_writer_output_string(void* userdata, const char* str) { /* The reader asks us to clear our scratchpad. In our case, we'll simply mark * the end of the current string, and advance our output pointer. */ -static void json_reader_string_clear(void* userdata) { - json_reader_userdata* state = userdata; +static void json_reader_string_clear(void *userdata) { + json_reader_userdata *state = userdata; if (state->string) { GPR_ASSERT(state->string_ptr < state->input); *state->string_ptr++ = 0; @@ -122,8 +122,8 @@ static void json_reader_string_clear(void* userdata) { state->string = state->string_ptr; } -static void json_reader_string_add_char(void* userdata, gpr_uint32 c) { - json_reader_userdata* state = userdata; +static void json_reader_string_add_char(void *userdata, gpr_uint32 c) { + json_reader_userdata *state = userdata; GPR_ASSERT(state->string_ptr < state->input); GPR_ASSERT(c <= 0xff); *state->string_ptr++ = (gpr_uint8)c; @@ -132,7 +132,7 @@ static void json_reader_string_add_char(void* userdata, gpr_uint32 c) { /* We are converting a UTF-32 character into UTF-8 here, * as described by RFC3629. */ -static void json_reader_string_add_utf32(void* userdata, gpr_uint32 c) { +static void json_reader_string_add_utf32(void *userdata, gpr_uint32 c) { if (c <= 0x7f) { json_reader_string_add_char(userdata, c); } else if (c <= 0x7ff) { @@ -162,9 +162,9 @@ static void json_reader_string_add_utf32(void* userdata, gpr_uint32 c) { /* We consider that the input may be a zero-terminated string. So we * can end up hitting eof before the end of the alleged string length. */ -static gpr_uint32 json_reader_read_char(void* userdata) { +static gpr_uint32 json_reader_read_char(void *userdata) { gpr_uint32 r; - json_reader_userdata* state = userdata; + json_reader_userdata *state = userdata; if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF; @@ -182,9 +182,9 @@ static gpr_uint32 json_reader_read_char(void* userdata) { /* Helper function to create a new grpc_json object and link it into * our tree-in-progress inside our opaque structure. */ -static grpc_json* json_create_and_link(void* userdata, grpc_json_type type) { - json_reader_userdata* state = userdata; - grpc_json* json = grpc_json_create(type); +static grpc_json *json_create_and_link(void *userdata, grpc_json_type type) { + json_reader_userdata *state = userdata; + grpc_json *json = grpc_json_create(type); json->parent = state->current_container; json->prev = state->current_value; @@ -198,7 +198,7 @@ static grpc_json* json_create_and_link(void* userdata, grpc_json_type type) { json->parent->child = json; } if (json->parent->type == GRPC_JSON_OBJECT) { - json->key = (char*)state->key; + json->key = (char *)state->key; } } if (!state->top) { @@ -208,9 +208,9 @@ static grpc_json* json_create_and_link(void* userdata, grpc_json_type type) { return json; } -static void json_reader_container_begins(void* userdata, grpc_json_type type) { - json_reader_userdata* state = userdata; - grpc_json* container; +static void json_reader_container_begins(void *userdata, grpc_json_type type) { + json_reader_userdata *state = userdata; + grpc_json *container; GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT); @@ -228,9 +228,9 @@ static void json_reader_container_begins(void* userdata, grpc_json_type type) { * Also note that if we're at the top of the tree, and the last container * ends, we have to return GRPC_JSON_TOP_LEVEL. */ -static grpc_json_type json_reader_container_ends(void* userdata) { +static grpc_json_type json_reader_container_ends(void *userdata) { grpc_json_type container_type = GRPC_JSON_TOP_LEVEL; - json_reader_userdata* state = userdata; + json_reader_userdata *state = userdata; GPR_ASSERT(state->current_container); @@ -250,36 +250,36 @@ static grpc_json_type json_reader_container_ends(void* userdata) { * Note that in the set_number case, we're not going to try interpreting it. * We'll keep it as a string, and leave it to the caller to evaluate it. */ -static void json_reader_set_key(void* userdata) { - json_reader_userdata* state = userdata; +static void json_reader_set_key(void *userdata) { + json_reader_userdata *state = userdata; state->key = state->string; } -static void json_reader_set_string(void* userdata) { - json_reader_userdata* state = userdata; - grpc_json* json = json_create_and_link(userdata, GRPC_JSON_STRING); - json->value = (char*)state->string; +static void json_reader_set_string(void *userdata) { + json_reader_userdata *state = userdata; + grpc_json *json = json_create_and_link(userdata, GRPC_JSON_STRING); + json->value = (char *)state->string; } -static int json_reader_set_number(void* userdata) { - json_reader_userdata* state = userdata; - grpc_json* json = json_create_and_link(userdata, GRPC_JSON_NUMBER); - json->value = (char*)state->string; +static int json_reader_set_number(void *userdata) { + json_reader_userdata *state = userdata; + grpc_json *json = json_create_and_link(userdata, GRPC_JSON_NUMBER); + json->value = (char *)state->string; return 1; } /* The object types true, false and null are self-sufficient, and don't need * any more information beside their type. */ -static void json_reader_set_true(void* userdata) { +static void json_reader_set_true(void *userdata) { json_create_and_link(userdata, GRPC_JSON_TRUE); } -static void json_reader_set_false(void* userdata) { +static void json_reader_set_false(void *userdata) { json_create_and_link(userdata, GRPC_JSON_FALSE); } -static void json_reader_set_null(void* userdata) { +static void json_reader_set_null(void *userdata) { json_create_and_link(userdata, GRPC_JSON_NULL); } @@ -292,17 +292,17 @@ static grpc_json_reader_vtable reader_vtable = { json_reader_set_false, json_reader_set_null}; /* And finally, let's define our public API. */ -grpc_json* grpc_json_parse_string_with_len(char* input, size_t size) { +grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { grpc_json_reader reader; json_reader_userdata state; - grpc_json* json = NULL; + grpc_json *json = NULL; grpc_json_reader_status status; if (!input) return NULL; state.top = state.current_container = state.current_value = NULL; state.string = state.key = NULL; - state.string_ptr = state.input = (gpr_uint8*)input; + state.string_ptr = state.input = (gpr_uint8 *)input; state.remaining_input = size; grpc_json_reader_init(&reader, &reader_vtable, &state); @@ -319,11 +319,11 @@ grpc_json* grpc_json_parse_string_with_len(char* input, size_t size) { #define UNBOUND_JSON_STRING_LENGTH 0x7fffffff -grpc_json* grpc_json_parse_string(char* input) { +grpc_json *grpc_json_parse_string(char *input) { return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH); } -static void json_dump_recursive(grpc_json_writer* writer, grpc_json* json, +static void json_dump_recursive(grpc_json_writer *writer, grpc_json *json, int in_object) { while (json) { if (in_object) grpc_json_writer_object_key(writer, json->key); @@ -363,7 +363,7 @@ static grpc_json_writer_vtable writer_vtable = { json_writer_output_char, json_writer_output_string, json_writer_output_string_with_len}; -char* grpc_json_dump_to_string(grpc_json* json, int indent) { +char *grpc_json_dump_to_string(grpc_json *json, int indent) { grpc_json_writer writer; json_writer_userdata state; diff --git a/src/core/json/json_writer.c b/src/core/json/json_writer.c index ca9c835825..e0d02f411e 100644 --- a/src/core/json/json_writer.c +++ b/src/core/json/json_writer.c @@ -37,22 +37,22 @@ #include "src/core/json/json_writer.h" -static void json_writer_output_char(grpc_json_writer* writer, char c) { +static void json_writer_output_char(grpc_json_writer *writer, char c) { writer->vtable->output_char(writer->userdata, c); } -static void json_writer_output_string(grpc_json_writer* writer, - const char* str) { +static void json_writer_output_string(grpc_json_writer *writer, + const char *str) { writer->vtable->output_string(writer->userdata, str); } -static void json_writer_output_string_with_len(grpc_json_writer* writer, - const char* str, size_t len) { +static void json_writer_output_string_with_len(grpc_json_writer *writer, + const char *str, size_t len) { writer->vtable->output_string_with_len(writer->userdata, str, len); } -void grpc_json_writer_init(grpc_json_writer* writer, int indent, - grpc_json_writer_vtable* vtable, void* userdata) { +void grpc_json_writer_init(grpc_json_writer *writer, int indent, + grpc_json_writer_vtable *vtable, void *userdata) { memset(writer, 0, sizeof(*writer)); writer->container_empty = 1; writer->indent = indent; @@ -60,7 +60,7 @@ void grpc_json_writer_init(grpc_json_writer* writer, int indent, writer->userdata = userdata; } -static void json_writer_output_indent(grpc_json_writer* writer) { +static void json_writer_output_indent(grpc_json_writer *writer) { static const char spacesstr[] = " " " " @@ -88,7 +88,7 @@ static void json_writer_output_indent(grpc_json_writer* writer) { writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); } -static void json_writer_value_end(grpc_json_writer* writer) { +static void json_writer_value_end(grpc_json_writer *writer) { if (writer->container_empty) { writer->container_empty = 0; if ((writer->indent == 0) || (writer->depth == 0)) return; @@ -100,7 +100,7 @@ static void json_writer_value_end(grpc_json_writer* writer) { } } -static void json_writer_escape_utf16(grpc_json_writer* writer, +static void json_writer_escape_utf16(grpc_json_writer *writer, gpr_uint16 utf16) { static const char hex[] = "0123456789abcdef"; @@ -111,8 +111,8 @@ static void json_writer_escape_utf16(grpc_json_writer* writer, json_writer_output_char(writer, hex[(utf16)&0x0f]); } -static void json_writer_escape_string(grpc_json_writer* writer, - const char* string) { +static void json_writer_escape_string(grpc_json_writer *writer, + const char *string) { json_writer_output_char(writer, '"'); for (;;) { @@ -207,7 +207,7 @@ static void json_writer_escape_string(grpc_json_writer* writer, json_writer_output_char(writer, '"'); } -void grpc_json_writer_container_begins(grpc_json_writer* writer, +void grpc_json_writer_container_begins(grpc_json_writer *writer, grpc_json_type type) { if (!writer->got_key) json_writer_value_end(writer); json_writer_output_indent(writer); @@ -217,7 +217,7 @@ void grpc_json_writer_container_begins(grpc_json_writer* writer, writer->depth++; } -void grpc_json_writer_container_ends(grpc_json_writer* writer, +void grpc_json_writer_container_ends(grpc_json_writer *writer, grpc_json_type type) { if (writer->indent && !writer->container_empty) json_writer_output_char(writer, '\n'); @@ -228,7 +228,7 @@ void grpc_json_writer_container_ends(grpc_json_writer* writer, writer->got_key = 0; } -void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) { +void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string) { json_writer_value_end(writer); json_writer_output_indent(writer); json_writer_escape_string(writer, string); @@ -236,23 +236,23 @@ void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) { writer->got_key = 1; } -void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string) { +void grpc_json_writer_value_raw(grpc_json_writer *writer, const char *string) { if (!writer->got_key) json_writer_value_end(writer); json_writer_output_indent(writer); json_writer_output_string(writer, string); writer->got_key = 0; } -void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, - const char* string, size_t len) { +void grpc_json_writer_value_raw_with_len(grpc_json_writer *writer, + const char *string, size_t len) { if (!writer->got_key) json_writer_value_end(writer); json_writer_output_indent(writer); json_writer_output_string_with_len(writer, string, len); writer->got_key = 0; } -void grpc_json_writer_value_string(grpc_json_writer* writer, - const char* string) { +void grpc_json_writer_value_string(grpc_json_writer *writer, + const char *string) { if (!writer->got_key) json_writer_value_end(writer); json_writer_output_indent(writer); json_writer_escape_string(writer, string); diff --git a/src/core/json/json_writer.h b/src/core/json/json_writer.h index a299dfabf8..9ef04aab01 100644 --- a/src/core/json/json_writer.h +++ b/src/core/json/json_writer.h @@ -52,17 +52,17 @@ typedef struct grpc_json_writer_vtable { /* Adds a character to the output stream. */ - void (*output_char)(void* userdata, char); + void (*output_char)(void *userdata, char); /* Adds a zero-terminated string to the output stream. */ - void (*output_string)(void* userdata, const char* str); + void (*output_string)(void *userdata, const char *str); /* Adds a fixed-length string to the output stream. */ - void (*output_string_with_len)(void* userdata, const char* str, size_t len); + void (*output_string_with_len)(void *userdata, const char *str, size_t len); } grpc_json_writer_vtable; typedef struct grpc_json_writer { - void* userdata; - grpc_json_writer_vtable* vtable; + void *userdata; + grpc_json_writer_vtable *vtable; int indent; int depth; int container_empty; @@ -74,24 +74,24 @@ typedef struct grpc_json_writer { * use indent=0, then the output will not have any newlines either, thus * emitting a condensed json output. */ -void grpc_json_writer_init(grpc_json_writer* writer, int indent, - grpc_json_writer_vtable* vtable, void* userdata); +void grpc_json_writer_init(grpc_json_writer *writer, int indent, + grpc_json_writer_vtable *vtable, void *userdata); /* Signals the beginning of a container. */ -void grpc_json_writer_container_begins(grpc_json_writer* writer, +void grpc_json_writer_container_begins(grpc_json_writer *writer, grpc_json_type type); /* Signals the end of a container. */ -void grpc_json_writer_container_ends(grpc_json_writer* writer, +void grpc_json_writer_container_ends(grpc_json_writer *writer, grpc_json_type type); /* Writes down an object key for the next value. */ -void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string); +void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string); /* Sets a raw value. Useful for numbers. */ -void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string); +void grpc_json_writer_value_raw(grpc_json_writer *writer, const char *string); /* Sets a raw value with its length. Useful for values like true or false. */ -void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, - const char* string, size_t len); +void grpc_json_writer_value_raw_with_len(grpc_json_writer *writer, + const char *string, size_t len); /* Sets a string value. It'll be escaped, and utf-8 validated. */ -void grpc_json_writer_value_string(grpc_json_writer* writer, - const char* string); +void grpc_json_writer_value_string(grpc_json_writer *writer, + const char *string); #endif /* GRPC_INTERNAL_CORE_JSON_JSON_WRITER_H */ diff --git a/src/core/profiling/basic_timers.c b/src/core/profiling/basic_timers.c index 4b6a0d2f56..b49cdd07b3 100644 --- a/src/core/profiling/basic_timers.c +++ b/src/core/profiling/basic_timers.c @@ -44,97 +44,91 @@ #include <grpc/support/thd.h> #include <stdio.h> -typedef enum { - BEGIN = '{', - END = '}', - MARK = '.', - IMPORTANT = '!' -} marker_type; - -typedef struct grpc_timer_entry { +typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type; + +typedef struct gpr_timer_entry { gpr_timespec tm; - int tag; - const char* tagstr; - marker_type type; - void* id; - const char* file; + const char *tagstr; + const char *file; int line; -} grpc_timer_entry; + char type; + gpr_uint8 important; +} gpr_timer_entry; + +#define MAX_COUNT (1024 * 1024 / sizeof(gpr_timer_entry)) + +static __thread gpr_timer_entry g_log[MAX_COUNT]; +static __thread int g_count; +static gpr_once g_once_init = GPR_ONCE_INIT; +static FILE *output_file; -#define MAX_COUNT (1024 * 1024 / sizeof(grpc_timer_entry)) +static void close_output() { fclose(output_file); } -static __thread grpc_timer_entry log[MAX_COUNT]; -static __thread int count; +static void init_output() { + output_file = fopen("latency_trace.txt", "w"); + GPR_ASSERT(output_file); + atexit(close_output); +} static void log_report() { int i; - for (i = 0; i < count; i++) { - grpc_timer_entry* entry = &(log[i]); - printf("GRPC_LAT_PROF %ld.%09d %p %c %d(%s) %p %s %d\n", - entry->tm.tv_sec, entry->tm.tv_nsec, - (void*)(gpr_intptr)gpr_thd_currentid(), entry->type, entry->tag, - entry->tagstr, entry->id, entry->file, entry->line); + gpr_once_init(&g_once_init, init_output); + for (i = 0; i < g_count; i++) { + gpr_timer_entry *entry = &(g_log[i]); + fprintf(output_file, + "{\"t\": %ld.%09d, \"thd\": \"%p\", \"type\": \"%c\", \"tag\": " + "\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n", + entry->tm.tv_sec, entry->tm.tv_nsec, + (void *)(gpr_intptr)gpr_thd_currentid(), entry->type, entry->tagstr, + entry->file, entry->line, entry->important); } /* Now clear out the log */ - count = 0; + g_count = 0; } -static void grpc_timers_log_add(int tag, const char* tagstr, marker_type type, - void* id, const char* file, int line) { - grpc_timer_entry* entry; +static void gpr_timers_log_add(const char *tagstr, marker_type type, + int important, const char *file, int line) { + gpr_timer_entry *entry; /* TODO (vpai) : Improve concurrency */ - if (count == MAX_COUNT) { + if (g_count == MAX_COUNT) { log_report(); } - entry = &log[count++]; + entry = &g_log[g_count++]; entry->tm = gpr_now(GPR_CLOCK_PRECISE); - entry->tag = tag; entry->tagstr = tagstr; entry->type = type; - entry->id = id; entry->file = file; entry->line = line; + entry->important = important != 0; } /* Latency profiler API implementation. */ -void grpc_timer_add_mark(int tag, const char* tagstr, void* id, - const char* file, int line) { - if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { - grpc_timers_log_add(tag, tagstr, MARK, id, file, line); - } +void gpr_timer_add_mark(const char *tagstr, int important, const char *file, + int line) { + gpr_timers_log_add(tagstr, MARK, important, file, line); } -void grpc_timer_add_important_mark(int tag, const char* tagstr, void* id, - const char* file, int line) { - if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { - grpc_timers_log_add(tag, tagstr, IMPORTANT, id, file, line); - } +void gpr_timer_begin(const char *tagstr, int important, const char *file, + int line) { + gpr_timers_log_add(tagstr, BEGIN, important, file, line); } -void grpc_timer_begin(int tag, const char* tagstr, void* id, const char* file, - int line) { - if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { - grpc_timers_log_add(tag, tagstr, BEGIN, id, file, line); - } -} - -void grpc_timer_end(int tag, const char* tagstr, void* id, const char* file, - int line) { - if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { - grpc_timers_log_add(tag, tagstr, END, id, file, line); - } +void gpr_timer_end(const char *tagstr, int important, const char *file, + int line) { + gpr_timers_log_add(tagstr, END, important, file, line); } /* Basic profiler specific API functions. */ -void grpc_timers_global_init(void) {} +void gpr_timers_global_init(void) {} -void grpc_timers_global_destroy(void) {} +void gpr_timers_global_destroy(void) {} #else /* !GRPC_BASIC_PROFILER */ -void grpc_timers_global_init(void) {} -void grpc_timers_global_destroy(void) {} +void gpr_timers_global_init(void) {} + +void gpr_timers_global_destroy(void) {} #endif /* GRPC_BASIC_PROFILER */ diff --git a/src/core/profiling/stap_timers.c b/src/core/profiling/stap_timers.c index 99975163f9..efcd1af4a1 100644 --- a/src/core/profiling/stap_timers.c +++ b/src/core/profiling/stap_timers.c @@ -42,23 +42,23 @@ #include "src/core/profiling/stap_probes.h" /* Latency profiler API implementation. */ -void grpc_timer_add_mark(int tag, const char* tagstr, void* id, - const char* file, int line) { +void gpr_timer_add_mark(int tag, const char *tagstr, void *id, const char *file, + int line) { _STAP_ADD_MARK(tag); } -void grpc_timer_add_important_mark(int tag, const char* tagstr, void* id, - const char* file, int line) { +void gpr_timer_add_important_mark(int tag, const char *tagstr, void *id, + const char *file, int line) { _STAP_ADD_IMPORTANT_MARK(tag); } -void grpc_timer_begin(int tag, const char* tagstr, void* id, const char* file, - int line) { +void gpr_timer_begin(int tag, const char *tagstr, void *id, const char *file, + int line) { _STAP_TIMING_NS_BEGIN(tag); } -void grpc_timer_end(int tag, const char* tagstr, void* id, const char* file, - int line) { +void gpr_timer_end(int tag, const char *tagstr, void *id, const char *file, + int line) { _STAP_TIMING_NS_END(tag); } diff --git a/src/core/profiling/timers.h b/src/core/profiling/timers.h index 92dbab9042..0d112e7248 100644 --- a/src/core/profiling/timers.h +++ b/src/core/profiling/timers.h @@ -38,65 +38,28 @@ extern "C" { #endif -void grpc_timers_global_init(void); -void grpc_timers_global_destroy(void); - -void grpc_timer_add_mark(int tag, const char *tagstr, void *id, - const char *file, int line); -void grpc_timer_add_important_mark(int tag, const char *tagstr, void *id, - const char *file, int line); -void grpc_timer_begin(int tag, const char *tagstr, void *id, const char *file, - int line); -void grpc_timer_end(int tag, const char *tagstr, void *id, const char *file, - int line); - -enum grpc_profiling_tags { - /* Any GRPC_PTAG_* >= than the threshold won't generate any profiling mark. */ - GRPC_PTAG_IGNORE_THRESHOLD = 1000000, - - /* Re. Protos. */ - GRPC_PTAG_PROTO_SERIALIZE = 100 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_PROTO_DESERIALIZE = 101 + GRPC_PTAG_IGNORE_THRESHOLD, - - /* Re. sockets. */ - GRPC_PTAG_HANDLE_READ = 200 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_SENDMSG = 201 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_RECVMSG = 202 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_POLL_FINISHED = 203 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_TCP_CB_WRITE = 204 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_TCP_WRITE = 205 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_CALL_ON_DONE_RECV = 206 + GRPC_PTAG_IGNORE_THRESHOLD, - - /* C++ */ - GRPC_PTAG_CPP_CALL_CREATED = 300 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_CPP_PERFORM_OPS = 301 + GRPC_PTAG_IGNORE_THRESHOLD, - - /* Transports */ - GRPC_PTAG_HTTP2_UNLOCK = 401 + GRPC_PTAG_IGNORE_THRESHOLD, - GRPC_PTAG_HTTP2_UNLOCK_CLEANUP = 402 + GRPC_PTAG_IGNORE_THRESHOLD, - - /* > 1024 Unassigned reserved. For any miscellaneous use. - * Use addition to generate tags from this base or take advantage of the 10 - * zero'd bits for OR-ing. */ - GRPC_PTAG_OTHER_BASE = 1024 -}; +void gpr_timers_global_init(void); +void gpr_timers_global_destroy(void); + +void gpr_timer_add_mark(const char *tagstr, int important, const char *file, + int line); +void gpr_timer_begin(const char *tagstr, int important, const char *file, + int line); +void gpr_timer_end(const char *tagstr, int important, const char *file, + int line); #if !(defined(GRPC_STAP_PROFILER) + defined(GRPC_BASIC_PROFILER)) /* No profiling. No-op all the things. */ -#define GRPC_TIMER_MARK(tag, id) \ - do { \ - } while (0) - -#define GRPC_TIMER_IMPORTANT_MARK(tag, id) \ - do { \ +#define GPR_TIMER_MARK(tag, important) \ + do { \ } while (0) -#define GRPC_TIMER_BEGIN(tag, id) \ - do { \ +#define GPR_TIMER_BEGIN(tag, important) \ + do { \ } while (0) -#define GRPC_TIMER_END(tag, id) \ - do { \ +#define GPR_TIMER_END(tag, important) \ + do { \ } while (0) #else /* at least one profiler requested... */ @@ -106,28 +69,14 @@ enum grpc_profiling_tags { #endif /* Generic profiling interface. */ -#define GRPC_TIMER_MARK(tag, id) \ - if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ - grpc_timer_add_mark(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, \ - __LINE__); \ - } - -#define GRPC_TIMER_IMPORTANT_MARK(tag, id) \ - if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ - grpc_timer_add_important_mark(tag, #tag, ((void *)(gpr_intptr)(id)), \ - __FILE__, __LINE__); \ - } +#define GPR_TIMER_MARK(tag, important) \ + gpr_timer_add_mark(tag, important, __FILE__, __LINE__); -#define GRPC_TIMER_BEGIN(tag, id) \ - if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ - grpc_timer_begin(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, \ - __LINE__); \ - } +#define GPR_TIMER_BEGIN(tag, important) \ + gpr_timer_begin(tag, important, __FILE__, __LINE__); -#define GRPC_TIMER_END(tag, id) \ - if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ - grpc_timer_end(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, __LINE__); \ - } +#define GPR_TIMER_END(tag, important) \ + gpr_timer_end(tag, important, __FILE__, __LINE__); #ifdef GRPC_STAP_PROFILER /* Empty placeholder for now. */ @@ -141,6 +90,28 @@ enum grpc_profiling_tags { #ifdef __cplusplus } + +#if (defined(GRPC_STAP_PROFILER) + defined(GRPC_BASIC_PROFILER)) +namespace grpc { +class ProfileScope { + public: + ProfileScope(const char *desc, bool important) : desc_(desc) { + GPR_TIMER_BEGIN(desc_, important ? 1 : 0); + } + ~ProfileScope() { GPR_TIMER_END(desc_, 0); } + + private: + const char *const desc_; +}; +} + +#define GPR_TIMER_SCOPE(tag, important) \ + ::grpc::ProfileScope _profile_scope_##__LINE__((tag), (important)) +#else +#define GPR_TIMER_SCOPE(tag, important) \ + do { \ + } while (false) +#endif #endif #endif /* GRPC_CORE_PROFILING_TIMERS_H */ diff --git a/src/core/security/base64.c b/src/core/security/base64.c index 8dfaef846f..5226d2c578 100644 --- a/src/core/security/base64.c +++ b/src/core/security/base64.c @@ -125,13 +125,14 @@ gpr_slice grpc_base64_decode(const char *b64, int url_safe) { static void decode_one_char(const unsigned char *codes, unsigned char *result, size_t *result_offset) { - gpr_uint32 packed = (codes[0] << 2) | (codes[1] >> 4); + gpr_uint32 packed = ((gpr_uint32)codes[0] << 2) | ((gpr_uint32)codes[1] >> 4); result[(*result_offset)++] = (unsigned char)packed; } static void decode_two_chars(const unsigned char *codes, unsigned char *result, size_t *result_offset) { - gpr_uint32 packed = (codes[0] << 10) | (codes[1] << 4) | (codes[2] >> 2); + gpr_uint32 packed = ((gpr_uint32)codes[0] << 10) | + ((gpr_uint32)codes[1] << 4) | ((gpr_uint32)codes[2] >> 2); result[(*result_offset)++] = (unsigned char)(packed >> 8); result[(*result_offset)++] = (unsigned char)(packed); } @@ -171,8 +172,9 @@ static int decode_group(const unsigned char *codes, size_t num_codes, decode_two_chars(codes, result, result_offset); } else { /* No padding. */ - gpr_uint32 packed = - (codes[0] << 18) | (codes[1] << 12) | (codes[2] << 6) | codes[3]; + gpr_uint32 packed = ((gpr_uint32)codes[0] << 18) | + ((gpr_uint32)codes[1] << 12) | + ((gpr_uint32)codes[2] << 6) | codes[3]; result[(*result_offset)++] = (unsigned char)(packed >> 16); result[(*result_offset)++] = (unsigned char)(packed >> 8); result[(*result_offset)++] = (unsigned char)(packed); diff --git a/src/core/security/client_auth_filter.c b/src/core/security/client_auth_filter.c index f3ecfd0e60..635982b252 100644 --- a/src/core/security/client_auth_filter.c +++ b/src/core/security/client_auth_filter.c @@ -63,6 +63,7 @@ typedef struct { int sent_initial_metadata; gpr_uint8 security_context_set; grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; + char *service_url; } call_data; /* We can have a per-channel credentials. */ @@ -75,15 +76,22 @@ typedef struct { grpc_mdstr *status_key; } channel_data; -static void bubble_up_error(grpc_call_element *elem, grpc_status_code status, - const char *error_msg) { +static void reset_service_url(call_data *calld) { + if (calld->service_url != NULL) { + gpr_free(calld->service_url); + calld->service_url = NULL; + } +} + +static void bubble_up_error(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_status_code status, const char *error_msg) { call_data *calld = elem->call_data; gpr_log(GPR_ERROR, "Client side authentication failure: %s", error_msg); grpc_transport_stream_op_add_cancellation(&calld->op, status); - grpc_call_next_op(elem, &calld->op); + grpc_call_next_op(exec_ctx, elem, &calld->op); } -static void on_credentials_metadata(void *user_data, +static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data, grpc_credentials_md *md_elems, size_t num_md, grpc_credentials_status status) { @@ -93,8 +101,9 @@ static void on_credentials_metadata(void *user_data, grpc_transport_stream_op *op = &calld->op; grpc_metadata_batch *mdb; size_t i; + reset_service_url(calld); if (status != GRPC_CREDENTIALS_OK) { - bubble_up_error(elem, GRPC_STATUS_UNAUTHENTICATED, + bubble_up_error(exec_ctx, elem, GRPC_STATUS_UNAUTHENTICATED, "Credentials failed to get metadata."); return; } @@ -108,11 +117,10 @@ static void on_credentials_metadata(void *user_data, grpc_mdelem_from_slices(chand->md_ctx, gpr_slice_ref(md_elems[i].key), gpr_slice_ref(md_elems[i].value))); } - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); } -static char *build_service_url(const char *url_scheme, call_data *calld) { - char *service_url; +void build_service_url(const char *url_scheme, call_data *calld) { char *service = gpr_strdup(grpc_mdstr_as_c_string(calld->method)); char *last_slash = strrchr(service, '/'); if (last_slash == NULL) { @@ -125,19 +133,19 @@ static char *build_service_url(const char *url_scheme, call_data *calld) { *last_slash = '\0'; } if (url_scheme == NULL) url_scheme = ""; - gpr_asprintf(&service_url, "%s://%s%s", url_scheme, + reset_service_url(calld); + gpr_asprintf(&calld->service_url, "%s://%s%s", url_scheme, grpc_mdstr_as_c_string(calld->host), service); gpr_free(service); - return service_url; } -static void send_security_metadata(grpc_call_element *elem, +static void send_security_metadata(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; grpc_client_security_context *ctx = (grpc_client_security_context *)op->context[GRPC_CONTEXT_SECURITY].value; - char *service_url = NULL; grpc_credentials *channel_creds = chand->security_connector->request_metadata_creds; int channel_creds_has_md = @@ -148,7 +156,7 @@ static void send_security_metadata(grpc_call_element *elem, if (!channel_creds_has_md && !call_creds_has_md) { /* Skip sending metadata altogether. */ - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); return; } @@ -156,7 +164,7 @@ static void send_security_metadata(grpc_call_element *elem, calld->creds = grpc_composite_credentials_create(channel_creds, ctx->creds, NULL); if (calld->creds == NULL) { - bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT, + bubble_up_error(exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, "Incompatible credentials set on channel and call."); return; } @@ -165,26 +173,26 @@ static void send_security_metadata(grpc_call_element *elem, grpc_credentials_ref(call_creds_has_md ? ctx->creds : channel_creds); } - service_url = - build_service_url(chand->security_connector->base.url_scheme, calld); + build_service_url(chand->security_connector->base.url_scheme, calld); calld->op = *op; /* Copy op (originates from the caller's stack). */ GPR_ASSERT(calld->pollset); - grpc_credentials_get_request_metadata( - calld->creds, calld->pollset, service_url, on_credentials_metadata, elem); - gpr_free(service_url); + grpc_credentials_get_request_metadata(exec_ctx, calld->creds, calld->pollset, + calld->service_url, + on_credentials_metadata, elem); } -static void on_host_checked(void *user_data, grpc_security_status status) { +static void on_host_checked(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_security_status status) { grpc_call_element *elem = (grpc_call_element *)user_data; call_data *calld = elem->call_data; if (status == GRPC_SECURITY_OK) { - send_security_metadata(elem, &calld->op); + send_security_metadata(exec_ctx, elem, &calld->op); } else { char *error_msg; gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.", grpc_mdstr_as_c_string(calld->host)); - bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT, error_msg); + bubble_up_error(exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, error_msg); gpr_free(error_msg); } } @@ -194,7 +202,8 @@ static void on_host_checked(void *user_data, grpc_security_status status) { - a network event (or similar) from below, to receive something op contains type and call direction information, in addition to the data that is being sent or received. */ -static void auth_start_transport_op(grpc_call_element *elem, +static void auth_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; @@ -203,7 +212,8 @@ static void auth_start_transport_op(grpc_call_element *elem, size_t i; grpc_client_security_context *sec_ctx = NULL; - if (calld->security_context_set == 0) { + if (calld->security_context_set == 0 && + op->cancel_with_status == GRPC_STATUS_OK) { calld->security_context_set = 1; GPR_ASSERT(op->context); if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) { @@ -218,11 +228,11 @@ static void auth_start_transport_op(grpc_call_element *elem, chand->security_connector->base.auth_context, "client_auth_filter"); } - if (op->bind_pollset) { + if (op->bind_pollset != NULL) { calld->pollset = op->bind_pollset; } - if (op->send_ops && !calld->sent_initial_metadata) { + if (op->send_ops != NULL && !calld->sent_initial_metadata) { size_t nops = op->send_ops->nops; grpc_stream_op *ops = op->send_ops->ops; for (i = 0; i < nops; i++) { @@ -247,45 +257,42 @@ static void auth_start_transport_op(grpc_call_element *elem, const char *call_host = grpc_mdstr_as_c_string(calld->host); calld->op = *op; /* Copy op (originates from the caller's stack). */ status = grpc_channel_security_connector_check_call_host( - chand->security_connector, call_host, on_host_checked, elem); + exec_ctx, chand->security_connector, call_host, on_host_checked, + elem); if (status != GRPC_SECURITY_OK) { if (status == GRPC_SECURITY_ERROR) { char *error_msg; gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.", call_host); - bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT, error_msg); + bubble_up_error(exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, + error_msg); gpr_free(error_msg); } return; /* early exit */ } } - send_security_metadata(elem, op); + send_security_metadata(exec_ctx, elem, op); return; /* early exit */ } } - /* pass control up or down the stack */ - grpc_call_next_op(elem, op); + /* pass control down the stack */ + grpc_call_next_op(exec_ctx, elem, op); } /* Constructor for call_data */ -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; - calld->creds = NULL; - calld->host = NULL; - calld->method = NULL; - calld->pollset = NULL; - calld->sent_initial_metadata = 0; - calld->security_context_set = 0; - + memset(calld, 0, sizeof(*calld)); GPR_ASSERT(!initial_op || !initial_op->send_ops); } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) { +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { call_data *calld = elem->call_data; grpc_credentials_unref(calld->creds); if (calld->host != NULL) { @@ -294,10 +301,12 @@ static void destroy_call_elem(grpc_call_element *elem) { if (calld->method != NULL) { GRPC_MDSTR_UNREF(calld->method); } + reset_service_url(calld); } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, int is_first, int is_last) { @@ -317,16 +326,15 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, (grpc_channel_security_connector *)GRPC_SECURITY_CONNECTOR_REF( sc, "client_auth_filter"); chand->md_ctx = metadata_context; - chand->authority_string = - grpc_mdstr_from_string(chand->md_ctx, ":authority", 0); - chand->path_string = grpc_mdstr_from_string(chand->md_ctx, ":path", 0); - chand->error_msg_key = - grpc_mdstr_from_string(chand->md_ctx, "grpc-message", 0); - chand->status_key = grpc_mdstr_from_string(chand->md_ctx, "grpc-status", 0); + chand->authority_string = grpc_mdstr_from_string(chand->md_ctx, ":authority"); + chand->path_string = grpc_mdstr_from_string(chand->md_ctx, ":path"); + chand->error_msg_key = grpc_mdstr_from_string(chand->md_ctx, "grpc-message"); + chand->status_key = grpc_mdstr_from_string(chand->md_ctx, "grpc-status"); } /* Destructor for channel data */ -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { /* grab pointers to our data from the channel element */ channel_data *chand = elem->channel_data; grpc_channel_security_connector *ctx = chand->security_connector; @@ -347,8 +355,7 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_client_auth_filter = { - auth_start_transport_op, grpc_channel_next_op, - sizeof(call_data), init_call_elem, - destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, - grpc_call_next_get_peer, "client-auth"}; + auth_start_transport_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer, + "client-auth"}; diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index a764413300..5e155d83b9 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -41,12 +41,14 @@ #include "src/core/json/json.h" #include "src/core/httpcli/httpcli.h" #include "src/core/iomgr/iomgr.h" +#include "src/core/surface/api_trace.h" #include "src/core/support/string.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/string_util.h> #include <grpc/support/sync.h> +#include <grpc/support/thd.h> #include <grpc/support/time.h> /* -- Common. -- */ @@ -54,7 +56,6 @@ struct grpc_credentials_metadata_request { grpc_credentials *creds; grpc_credentials_metadata_cb cb; - grpc_iomgr_closure *on_simulated_token_fetch_done_closure; void *user_data; }; @@ -66,8 +67,6 @@ grpc_credentials_metadata_request_create(grpc_credentials *creds, gpr_malloc(sizeof(grpc_credentials_metadata_request)); r->creds = grpc_credentials_ref(creds); r->cb = cb; - r->on_simulated_token_fetch_done_closure = - gpr_malloc(sizeof(grpc_iomgr_closure)); r->user_data = user_data; return r; } @@ -75,7 +74,6 @@ grpc_credentials_metadata_request_create(grpc_credentials *creds, static void grpc_credentials_metadata_request_destroy( grpc_credentials_metadata_request *r) { grpc_credentials_unref(r->creds); - gpr_free(r->on_simulated_token_fetch_done_closure); gpr_free(r); } @@ -94,6 +92,7 @@ void grpc_credentials_unref(grpc_credentials *creds) { } void grpc_credentials_release(grpc_credentials *creds) { + GRPC_API_TRACE("grpc_credentials_release(creds=%p)", 1, (creds)); grpc_credentials_unref(creds); } @@ -107,19 +106,17 @@ int grpc_credentials_has_request_metadata_only(grpc_credentials *creds) { return creds->vtable->has_request_metadata_only(creds); } -void grpc_credentials_get_request_metadata(grpc_credentials *creds, - grpc_pollset *pollset, - const char *service_url, - grpc_credentials_metadata_cb cb, - void *user_data) { +void grpc_credentials_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { if (creds == NULL || !grpc_credentials_has_request_metadata(creds) || creds->vtable->get_request_metadata == NULL) { if (cb != NULL) { - cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK); + cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK); } return; } - creds->vtable->get_request_metadata(creds, pollset, service_url, cb, + creds->vtable->get_request_metadata(exec_ctx, creds, pollset, service_url, cb, user_data); } @@ -157,6 +154,7 @@ void grpc_server_credentials_unref(grpc_server_credentials *creds) { } void grpc_server_credentials_release(grpc_server_credentials *creds) { + GRPC_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (creds)); grpc_server_credentials_unref(creds); } @@ -171,6 +169,11 @@ grpc_security_status grpc_server_credentials_create_security_connector( void grpc_server_credentials_set_auth_metadata_processor( grpc_server_credentials *creds, grpc_auth_metadata_processor processor) { + GRPC_API_TRACE( + "grpc_server_credentials_set_auth_metadata_processor(" + "creds=%p, " + "processor=grpc_auth_metadata_processor { process: %lx, state: %p })", + 3, (creds, (unsigned long)processor.process, processor.state)); if (creds == NULL) return; if (creds->processor.destroy != NULL && creds->processor.state != NULL) { creds->processor.destroy(creds->processor.state); @@ -178,6 +181,48 @@ void grpc_server_credentials_set_auth_metadata_processor( creds->processor = processor; } +static void server_credentials_pointer_arg_destroy(void *p) { + grpc_server_credentials_unref(p); +} + +static void *server_credentials_pointer_arg_copy(void *p) { + return grpc_server_credentials_ref(p); +} + +grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *p) { + grpc_arg arg; + memset(&arg, 0, sizeof(grpc_arg)); + arg.type = GRPC_ARG_POINTER; + arg.key = GRPC_SERVER_CREDENTIALS_ARG; + arg.value.pointer.p = p; + arg.value.pointer.copy = server_credentials_pointer_arg_copy; + arg.value.pointer.destroy = server_credentials_pointer_arg_destroy; + return arg; +} + +grpc_server_credentials *grpc_server_credentials_from_arg( + const grpc_arg *arg) { + if (strcmp(arg->key, GRPC_SERVER_CREDENTIALS_ARG) != 0) return NULL; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_SERVER_CREDENTIALS_ARG); + return NULL; + } + return arg->value.pointer.p; +} + +grpc_server_credentials *grpc_find_server_credentials_in_args( + const grpc_channel_args *args) { + size_t i; + if (args == NULL) return NULL; + for (i = 0; i < args->num_args; i++) { + grpc_server_credentials *p = + grpc_server_credentials_from_arg(&args->args[i]); + if (p != NULL) return p; + } + return NULL; +} + /* -- Ssl credentials. -- */ static void ssl_destruct(grpc_credentials *creds) { @@ -223,7 +268,7 @@ static grpc_security_status ssl_create_security_connector( grpc_security_status status = GRPC_SECURITY_OK; size_t i = 0; const char *overridden_target_name = NULL; - grpc_arg arg; + grpc_arg new_arg; for (i = 0; args && i < args->num_args; i++) { grpc_arg *arg = &args->args[i]; @@ -238,10 +283,10 @@ static grpc_security_status ssl_create_security_connector( if (status != GRPC_SECURITY_OK) { return status; } - arg.type = GRPC_ARG_STRING; - arg.key = GRPC_ARG_HTTP2_SCHEME; - arg.value.string = "https"; - *new_args = grpc_channel_args_copy_and_add(args, &arg, 1); + new_arg.type = GRPC_ARG_STRING; + new_arg.key = GRPC_ARG_HTTP2_SCHEME; + new_arg.value.string = "https"; + *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1); return status; } @@ -322,6 +367,11 @@ grpc_credentials *grpc_ssl_credentials_create( const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, void *reserved) { grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials)); + GRPC_API_TRACE( + "grpc_ssl_credentials_create(pem_root_certs=%s, " + "pem_key_cert_pair=%p, " + "reserved=%p)", + 3, (pem_root_certs, pem_key_cert_pair, reserved)); GPR_ASSERT(reserved == NULL); memset(c, 0, sizeof(grpc_ssl_credentials)); c->base.type = GRPC_CREDENTIALS_TYPE_SSL; @@ -336,6 +386,12 @@ grpc_server_credentials *grpc_ssl_server_credentials_create( size_t num_key_cert_pairs, int force_client_auth, void *reserved) { grpc_ssl_server_credentials *c = gpr_malloc(sizeof(grpc_ssl_server_credentials)); + GRPC_API_TRACE( + "grpc_ssl_server_credentials_create(" + "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, " + "force_client_auth=%d, reserved=%p)", + 5, (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs, + force_client_auth, reserved)); GPR_ASSERT(reserved == NULL); memset(c, 0, sizeof(grpc_ssl_server_credentials)); c->base.type = GRPC_CREDENTIALS_TYPE_SSL; @@ -374,11 +430,9 @@ static int jwt_has_request_metadata_only(const grpc_credentials *creds) { return 1; } -static void jwt_get_request_metadata(grpc_credentials *creds, - grpc_pollset *pollset, - const char *service_url, - grpc_credentials_metadata_cb cb, - void *user_data) { +static void jwt_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_service_account_jwt_access_credentials *c = (grpc_service_account_jwt_access_credentials *)creds; gpr_timespec refresh_threshold = gpr_time_from_seconds( @@ -422,10 +476,11 @@ static void jwt_get_request_metadata(grpc_credentials *creds, } if (jwt_md != NULL) { - cb(user_data, jwt_md->entries, jwt_md->num_entries, GRPC_CREDENTIALS_OK); + cb(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries, + GRPC_CREDENTIALS_OK); grpc_credentials_md_store_unref(jwt_md); } else { - cb(user_data, NULL, 0, GRPC_CREDENTIALS_ERROR); + cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR); } } @@ -455,6 +510,14 @@ grpc_service_account_jwt_access_credentials_create_from_auth_json_key( grpc_credentials *grpc_service_account_jwt_access_credentials_create( const char *json_key, gpr_timespec token_lifetime, void *reserved) { + GRPC_API_TRACE( + "grpc_service_account_jwt_access_credentials_create(" + "json_key=%s, " + "token_lifetime=" + "gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 5, (json_key, (long)token_lifetime.tv_sec, token_lifetime.tv_nsec, + (int)token_lifetime.clock_type, reserved)); GPR_ASSERT(reserved == NULL); return grpc_service_account_jwt_access_credentials_create_from_auth_json_key( grpc_auth_json_key_create_from_string(json_key), token_lifetime); @@ -571,7 +634,8 @@ end: } static void on_oauth2_token_fetcher_http_response( - void *user_data, const grpc_httpcli_response *response) { + grpc_exec_ctx *exec_ctx, void *user_data, + const grpc_httpcli_response *response) { grpc_credentials_metadata_request *r = (grpc_credentials_metadata_request *)user_data; grpc_oauth2_token_fetcher_credentials *c = @@ -585,19 +649,19 @@ static void on_oauth2_token_fetcher_http_response( if (status == GRPC_CREDENTIALS_OK) { c->token_expiration = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime); - r->cb(r->user_data, c->access_token_md->entries, + r->cb(exec_ctx, r->user_data, c->access_token_md->entries, c->access_token_md->num_entries, status); } else { c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); - r->cb(r->user_data, NULL, 0, status); + r->cb(exec_ctx, r->user_data, NULL, 0, status); } gpr_mu_unlock(&c->mu); grpc_credentials_metadata_request_destroy(r); } static void oauth2_token_fetcher_get_request_metadata( - grpc_credentials *creds, grpc_pollset *pollset, const char *service_url, - grpc_credentials_metadata_cb cb, void *user_data) { + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_oauth2_token_fetcher_credentials *c = (grpc_oauth2_token_fetcher_credentials *)creds; gpr_timespec refresh_threshold = gpr_time_from_seconds( @@ -615,11 +679,12 @@ static void oauth2_token_fetcher_get_request_metadata( gpr_mu_unlock(&c->mu); } if (cached_access_token_md != NULL) { - cb(user_data, cached_access_token_md->entries, + cb(exec_ctx, user_data, cached_access_token_md->entries, cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK); grpc_credentials_md_store_unref(cached_access_token_md); } else { c->fetch_func( + exec_ctx, grpc_credentials_metadata_request_create(creds, cb, user_data), &c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold)); @@ -645,7 +710,7 @@ static grpc_credentials_vtable compute_engine_vtable = { oauth2_token_fetcher_get_request_metadata, NULL}; static void compute_engine_fetch_oauth2( - grpc_credentials_metadata_request *metadata_req, + grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, grpc_httpcli_context *httpcli_context, grpc_pollset *pollset, grpc_httpcli_response_cb response_cb, gpr_timespec deadline) { grpc_httpcli_header header = {"Metadata-Flavor", "Google"}; @@ -655,14 +720,16 @@ static void compute_engine_fetch_oauth2( request.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH; request.hdr_count = 1; request.hdrs = &header; - grpc_httpcli_get(httpcli_context, pollset, &request, deadline, response_cb, - metadata_req); + grpc_httpcli_get(exec_ctx, httpcli_context, pollset, &request, deadline, + response_cb, metadata_req); } grpc_credentials *grpc_google_compute_engine_credentials_create( void *reserved) { grpc_oauth2_token_fetcher_credentials *c = gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials)); + GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1, + (reserved)); GPR_ASSERT(reserved == NULL); init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2); c->base.vtable = &compute_engine_vtable; @@ -684,7 +751,7 @@ static grpc_credentials_vtable refresh_token_vtable = { oauth2_token_fetcher_get_request_metadata, NULL}; static void refresh_token_fetch_oauth2( - grpc_credentials_metadata_request *metadata_req, + grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, grpc_httpcli_context *httpcli_context, grpc_pollset *pollset, grpc_httpcli_response_cb response_cb, gpr_timespec deadline) { grpc_google_refresh_token_credentials *c = @@ -702,13 +769,12 @@ static void refresh_token_fetch_oauth2( request.hdr_count = 1; request.hdrs = &header; request.handshaker = &grpc_httpcli_ssl; - grpc_httpcli_post(httpcli_context, pollset, &request, body, strlen(body), - deadline, response_cb, metadata_req); + grpc_httpcli_post(exec_ctx, httpcli_context, pollset, &request, body, + strlen(body), deadline, response_cb, metadata_req); gpr_free(body); } -grpc_credentials * -grpc_refresh_token_credentials_create_from_auth_refresh_token( +grpc_credentials *grpc_refresh_token_credentials_create_from_auth_refresh_token( grpc_auth_refresh_token refresh_token) { grpc_google_refresh_token_credentials *c; if (!grpc_auth_refresh_token_is_valid(&refresh_token)) { @@ -725,6 +791,10 @@ grpc_refresh_token_credentials_create_from_auth_refresh_token( grpc_credentials *grpc_google_refresh_token_credentials_create( const char *json_refresh_token, void *reserved) { + GRPC_API_TRACE( + "grpc_refresh_token_credentials_create(json_refresh_token=%s, " + "reserved=%p)", + 2, (json_refresh_token, reserved)); GPR_ASSERT(reserved == NULL); return grpc_refresh_token_credentials_create_from_auth_refresh_token( grpc_auth_refresh_token_create_from_string(json_refresh_token)); @@ -746,31 +816,29 @@ static int md_only_test_has_request_metadata_only( return 1; } -void on_simulated_token_fetch_done(void *user_data, int success) { +static void on_simulated_token_fetch_done(void *user_data) { grpc_credentials_metadata_request *r = (grpc_credentials_metadata_request *)user_data; grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds; - GPR_ASSERT(success); - r->cb(r->user_data, c->md_store->entries, c->md_store->num_entries, + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + r->cb(&exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries, GRPC_CREDENTIALS_OK); grpc_credentials_metadata_request_destroy(r); + grpc_exec_ctx_finish(&exec_ctx); } -static void md_only_test_get_request_metadata(grpc_credentials *creds, - grpc_pollset *pollset, - const char *service_url, - grpc_credentials_metadata_cb cb, - void *user_data) { +static void md_only_test_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; if (c->is_async) { + gpr_thd_id thd_id; grpc_credentials_metadata_request *cb_arg = grpc_credentials_metadata_request_create(creds, cb, user_data); - grpc_iomgr_closure_init(cb_arg->on_simulated_token_fetch_done_closure, - on_simulated_token_fetch_done, cb_arg); - grpc_iomgr_add_callback(cb_arg->on_simulated_token_fetch_done_closure); + gpr_thd_new(&thd_id, on_simulated_token_fetch_done, cb_arg, NULL); } else { - cb(user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK); + cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK); } } @@ -810,13 +878,11 @@ static int access_token_has_request_metadata_only( return 1; } -static void access_token_get_request_metadata(grpc_credentials *creds, - grpc_pollset *pollset, - const char *service_url, - grpc_credentials_metadata_cb cb, - void *user_data) { +static void access_token_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; - cb(user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK); + cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK); } static grpc_credentials_vtable access_token_vtable = { @@ -829,6 +895,10 @@ grpc_credentials *grpc_access_token_credentials_create(const char *access_token, grpc_access_token_credentials *c = gpr_malloc(sizeof(grpc_access_token_credentials)); char *token_md_value; + GRPC_API_TRACE( + "grpc_access_token_credentials_create(access_token=%s, " + "reserved=%p)", + 2, (access_token, reserved)); GPR_ASSERT(reserved == NULL); memset(c, 0, sizeof(grpc_access_token_credentials)); c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2; @@ -961,13 +1031,13 @@ static void composite_md_context_destroy( gpr_free(ctx); } -static void composite_metadata_cb(void *user_data, +static void composite_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data, grpc_credentials_md *md_elems, size_t num_md, grpc_credentials_status status) { grpc_composite_credentials_metadata_context *ctx = (grpc_composite_credentials_metadata_context *)user_data; if (status != GRPC_CREDENTIALS_OK) { - ctx->cb(ctx->user_data, NULL, 0, status); + ctx->cb(exec_ctx, ctx->user_data, NULL, 0, status); return; } @@ -985,7 +1055,7 @@ static void composite_metadata_cb(void *user_data, grpc_credentials *inner_creds = ctx->composite_creds->inner.creds_array[ctx->creds_index++]; if (grpc_credentials_has_request_metadata(inner_creds)) { - grpc_credentials_get_request_metadata(inner_creds, ctx->pollset, + grpc_credentials_get_request_metadata(exec_ctx, inner_creds, ctx->pollset, ctx->service_url, composite_metadata_cb, ctx); return; @@ -993,20 +1063,18 @@ static void composite_metadata_cb(void *user_data, } /* We're done!. */ - ctx->cb(ctx->user_data, ctx->md_elems->entries, ctx->md_elems->num_entries, - GRPC_CREDENTIALS_OK); + ctx->cb(exec_ctx, ctx->user_data, ctx->md_elems->entries, + ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK); composite_md_context_destroy(ctx); } -static void composite_get_request_metadata(grpc_credentials *creds, - grpc_pollset *pollset, - const char *service_url, - grpc_credentials_metadata_cb cb, - void *user_data) { +static void composite_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_composite_credentials *c = (grpc_composite_credentials *)creds; grpc_composite_credentials_metadata_context *ctx; if (!grpc_credentials_has_request_metadata(creds)) { - cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK); + cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK); return; } ctx = gpr_malloc(sizeof(grpc_composite_credentials_metadata_context)); @@ -1020,8 +1088,9 @@ static void composite_get_request_metadata(grpc_credentials *creds, while (ctx->creds_index < c->inner.num_creds) { grpc_credentials *inner_creds = c->inner.creds_array[ctx->creds_index++]; if (grpc_credentials_has_request_metadata(inner_creds)) { - grpc_credentials_get_request_metadata(inner_creds, pollset, service_url, - composite_metadata_cb, ctx); + grpc_credentials_get_request_metadata(exec_ctx, inner_creds, pollset, + service_url, composite_metadata_cb, + ctx); return; } } @@ -1066,6 +1135,10 @@ grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1, grpc_credentials_array creds1_array; grpc_credentials_array creds2_array; grpc_composite_credentials *c; + GRPC_API_TRACE( + "grpc_composite_credentials_create(creds1=%p, creds2=%p, " + "reserved=%p)", + 3, (creds1, creds2, reserved)); GPR_ASSERT(reserved == NULL); GPR_ASSERT(creds1 != NULL); GPR_ASSERT(creds2 != NULL); @@ -1153,13 +1226,11 @@ static int iam_has_request_metadata_only(const grpc_credentials *creds) { return 1; } -static void iam_get_request_metadata(grpc_credentials *creds, - grpc_pollset *pollset, - const char *service_url, - grpc_credentials_metadata_cb cb, - void *user_data) { +static void iam_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; - cb(user_data, c->iam_md->entries, c->iam_md->num_entries, + cb(exec_ctx, user_data, c->iam_md->entries, c->iam_md->num_entries, GRPC_CREDENTIALS_OK); } @@ -1170,6 +1241,10 @@ static grpc_credentials_vtable iam_vtable = { grpc_credentials *grpc_google_iam_credentials_create( const char *token, const char *authority_selector, void *reserved) { grpc_google_iam_credentials *c; + GRPC_API_TRACE( + "grpc_iam_credentials_create(token=%s, authority_selector=%s, " + "reserved=%p)", + 3, (token, authority_selector, reserved)); GPR_ASSERT(reserved == NULL); GPR_ASSERT(token != NULL); GPR_ASSERT(authority_selector != NULL); @@ -1185,3 +1260,95 @@ grpc_credentials *grpc_google_iam_credentials_create( c->iam_md, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector); return &c->base; } + +/* -- Plugin credentials. -- */ + +typedef struct { + void *user_data; + grpc_credentials_metadata_cb cb; +} grpc_metadata_plugin_request; + +static void plugin_destruct(grpc_credentials *creds) { + grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; + if (c->plugin.state != NULL && c->plugin.destroy != NULL) { + c->plugin.destroy(c->plugin.state); + } +} + +static int plugin_has_request_metadata(const grpc_credentials *creds) { + return 1; +} + +static int plugin_has_request_metadata_only(const grpc_credentials *creds) { + return 1; +} + +static void plugin_md_request_metadata_ready(void *request, + const grpc_metadata *md, + size_t num_md, + grpc_status_code status, + const char *error_details) { + /* called from application code */ + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_metadata_plugin_request *r = (grpc_metadata_plugin_request *)request; + if (status != GRPC_STATUS_OK) { + if (error_details != NULL) { + gpr_log(GPR_ERROR, "Getting metadata from plugin failed with error: %s", + error_details); + } + r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR); + } else { + size_t i; + grpc_credentials_md *md_array = NULL; + if (num_md > 0) { + md_array = gpr_malloc(num_md * sizeof(grpc_credentials_md)); + for (i = 0; i < num_md; i++) { + md_array[i].key = gpr_slice_from_copied_string(md[i].key); + md_array[i].value = + gpr_slice_from_copied_buffer(md[i].value, md[i].value_length); + } + } + r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK); + if (md_array != NULL) { + for (i = 0; i < num_md; i++) { + gpr_slice_unref(md_array[i].key); + gpr_slice_unref(md_array[i].value); + } + gpr_free(md_array); + } + } + gpr_free(r); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void plugin_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { + grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; + if (c->plugin.get_metadata != NULL) { + grpc_metadata_plugin_request *request = gpr_malloc(sizeof(*request)); + memset(request, 0, sizeof(*request)); + request->user_data = user_data; + request->cb = cb; + c->plugin.get_metadata(c->plugin.state, service_url, + plugin_md_request_metadata_ready, request); + } else { + cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK); + } +} + +static grpc_credentials_vtable plugin_vtable = { + plugin_destruct, plugin_has_request_metadata, + plugin_has_request_metadata_only, plugin_get_request_metadata, NULL}; + +grpc_credentials *grpc_metadata_credentials_create_from_plugin( + grpc_metadata_credentials_plugin plugin, void *reserved) { + grpc_plugin_credentials *c = gpr_malloc(sizeof(*c)); + GPR_ASSERT(reserved == NULL); + memset(c, 0, sizeof(*c)); + c->base.type = GRPC_CREDENTIALS_TYPE_METADATA_PLUGIN; + c->base.vtable = &plugin_vtable; + gpr_ref_init(&c->base.refcount, 1); + c->plugin = plugin; + return &c->base; +} diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h index 8e4fed7615..01203b08f1 100644 --- a/src/core/security/credentials.h +++ b/src/core/security/credentials.h @@ -56,6 +56,7 @@ typedef enum { #define GRPC_CREDENTIALS_TYPE_SSL "Ssl" #define GRPC_CREDENTIALS_TYPE_OAUTH2 "Oauth2" +#define GRPC_CREDENTIALS_TYPE_METADATA_PLUGIN "Plugin" #define GRPC_CREDENTIALS_TYPE_JWT "Jwt" #define GRPC_CREDENTIALS_TYPE_IAM "Iam" #define GRPC_CREDENTIALS_TYPE_COMPOSITE "Composite" @@ -123,7 +124,8 @@ grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( /* It is the caller's responsibility to gpr_free the result if not NULL. */ char *grpc_get_well_known_google_credentials_file_path(void); -typedef void (*grpc_credentials_metadata_cb)(void *user_data, +typedef void (*grpc_credentials_metadata_cb)(grpc_exec_ctx *exec_ctx, + void *user_data, grpc_credentials_md *md_elems, size_t num_md, grpc_credentials_status status); @@ -132,8 +134,8 @@ typedef struct { void (*destruct)(grpc_credentials *c); int (*has_request_metadata)(const grpc_credentials *c); int (*has_request_metadata_only)(const grpc_credentials *c); - void (*get_request_metadata)(grpc_credentials *c, grpc_pollset *pollset, - const char *service_url, + void (*get_request_metadata)(grpc_exec_ctx *exec_ctx, grpc_credentials *c, + grpc_pollset *pollset, const char *service_url, grpc_credentials_metadata_cb cb, void *user_data); grpc_security_status (*create_security_connector)( @@ -152,11 +154,9 @@ grpc_credentials *grpc_credentials_ref(grpc_credentials *creds); void grpc_credentials_unref(grpc_credentials *creds); int grpc_credentials_has_request_metadata(grpc_credentials *creds); int grpc_credentials_has_request_metadata_only(grpc_credentials *creds); -void grpc_credentials_get_request_metadata(grpc_credentials *creds, - grpc_pollset *pollset, - const char *service_url, - grpc_credentials_metadata_cb cb, - void *user_data); +void grpc_credentials_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_credentials *creds, grpc_pollset *pollset, + const char *service_url, grpc_credentials_metadata_cb cb, void *user_data); /* Creates a security connector for the channel. May also create new channel args for the channel to be used in place of the passed in const args if @@ -215,8 +215,6 @@ typedef struct { grpc_server_credentials *c, grpc_security_connector **sc); } grpc_server_credentials_vtable; - -/* TODO(jboeuf): Add a refcount. */ struct grpc_server_credentials { const grpc_server_credentials_vtable *vtable; const char *type; @@ -232,6 +230,13 @@ grpc_server_credentials *grpc_server_credentials_ref( void grpc_server_credentials_unref(grpc_server_credentials *creds); +#define GRPC_SERVER_CREDENTIALS_ARG "grpc.server_credentials" + +grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *c); +grpc_server_credentials *grpc_server_credentials_from_arg(const grpc_arg *arg); +grpc_server_credentials *grpc_find_server_credentials_in_args( + const grpc_channel_args *args); + /* -- Ssl credentials. -- */ typedef struct { @@ -270,7 +275,8 @@ typedef struct { typedef struct grpc_credentials_metadata_request grpc_credentials_metadata_request; -typedef void (*grpc_fetch_oauth2_func)(grpc_credentials_metadata_request *req, +typedef void (*grpc_fetch_oauth2_func)(grpc_exec_ctx *exec_ctx, + grpc_credentials_metadata_request *req, grpc_httpcli_context *http_context, grpc_pollset *pollset, grpc_httpcli_response_cb response_cb, @@ -322,4 +328,12 @@ typedef struct { grpc_credentials *connector_creds; } grpc_composite_credentials; +/* -- Plugin credentials. -- */ + +typedef struct { + grpc_credentials base; + grpc_metadata_credentials_plugin plugin; + grpc_credentials_md_store *plugin_md; +} grpc_plugin_credentials; + #endif /* GRPC_INTERNAL_CORE_SECURITY_CREDENTIALS_H */ diff --git a/src/core/security/google_default_credentials.c b/src/core/security/google_default_credentials.c index 874dd59e84..45135305b2 100644 --- a/src/core/security/google_default_credentials.c +++ b/src/core/security/google_default_credentials.c @@ -42,6 +42,7 @@ #include "src/core/httpcli/httpcli.h" #include "src/core/support/env.h" #include "src/core/support/file.h" +#include "src/core/surface/api_trace.h" /* -- Constants. -- */ @@ -63,7 +64,8 @@ typedef struct { } compute_engine_detector; static void on_compute_engine_detection_http_response( - void *user_data, const grpc_httpcli_response *response) { + grpc_exec_ctx *exec_ctx, void *user_data, + const grpc_httpcli_response *response) { compute_engine_detector *detector = (compute_engine_detector *)user_data; if (response != NULL && response->status == 200 && response->hdr_count > 0) { /* Internet providers can return a generic response to all requests, so @@ -84,12 +86,16 @@ static void on_compute_engine_detection_http_response( gpr_mu_unlock(GRPC_POLLSET_MU(&detector->pollset)); } -static void destroy_pollset(void *p) { grpc_pollset_destroy(p); } +static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, int s) { + grpc_pollset_destroy(p); +} static int is_stack_running_on_compute_engine(void) { compute_engine_detector detector; grpc_httpcli_request request; grpc_httpcli_context context; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_closure destroy_closure; /* The http call is local. If it takes more than one sec, it is for sure not on compute engine. */ @@ -106,22 +112,27 @@ static int is_stack_running_on_compute_engine(void) { grpc_httpcli_context_init(&context); grpc_httpcli_get( - &context, &detector.pollset, &request, + &exec_ctx, &context, &detector.pollset, &request, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay), on_compute_engine_detection_http_response, &detector); + grpc_exec_ctx_finish(&exec_ctx); + /* Block until we get the response. This is not ideal but this should only be called once for the lifetime of the process by the default credentials. */ gpr_mu_lock(GRPC_POLLSET_MU(&detector.pollset)); while (!detector.is_done) { grpc_pollset_worker worker; - grpc_pollset_work(&detector.pollset, &worker, gpr_now(GPR_CLOCK_MONOTONIC), + grpc_pollset_work(&exec_ctx, &detector.pollset, &worker, + gpr_now(GPR_CLOCK_MONOTONIC), gpr_inf_future(GPR_CLOCK_MONOTONIC)); } gpr_mu_unlock(GRPC_POLLSET_MU(&detector.pollset)); grpc_httpcli_context_destroy(&context); - grpc_pollset_shutdown(&detector.pollset, destroy_pollset, &detector.pollset); + grpc_closure_init(&destroy_closure, destroy_pollset, &detector.pollset); + grpc_pollset_shutdown(&exec_ctx, &detector.pollset, &destroy_closure); + grpc_exec_ctx_finish(&exec_ctx); return detector.success; } @@ -168,6 +179,9 @@ end: grpc_credentials *grpc_google_default_credentials_create(void) { grpc_credentials *result = NULL; int serving_cached_credentials = 0; + + GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ()); + gpr_once_init(&g_once, init_default_credentials); gpr_mu_lock(&g_mu); diff --git a/src/core/security/handshake.c b/src/core/security/handshake.c new file mode 100644 index 0000000000..adbdd0b40e --- /dev/null +++ b/src/core/security/handshake.c @@ -0,0 +1,288 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/security/handshake.h" + +#include <string.h> + +#include "src/core/security/secure_endpoint.h" +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/slice_buffer.h> + +#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 + +typedef struct { + grpc_security_connector *connector; + tsi_handshaker *handshaker; + unsigned char *handshake_buffer; + size_t handshake_buffer_size; + grpc_endpoint *wrapped_endpoint; + grpc_endpoint *secure_endpoint; + gpr_slice_buffer left_overs; + gpr_slice_buffer incoming; + gpr_slice_buffer outgoing; + grpc_security_handshake_done_cb cb; + void *user_data; + grpc_closure on_handshake_data_sent_to_peer; + grpc_closure on_handshake_data_received_from_peer; +} grpc_security_handshake; + +static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, + void *setup, int success); + +static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *setup, + int success); + +static void security_handshake_done(grpc_exec_ctx *exec_ctx, + grpc_security_handshake *h, + int is_success) { + if (is_success) { + h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->wrapped_endpoint, + h->secure_endpoint); + } else { + if (h->secure_endpoint != NULL) { + grpc_endpoint_shutdown(exec_ctx, h->secure_endpoint); + grpc_endpoint_destroy(exec_ctx, h->secure_endpoint); + } else { + grpc_endpoint_destroy(exec_ctx, h->wrapped_endpoint); + } + h->cb(exec_ctx, h->user_data, GRPC_SECURITY_ERROR, h->wrapped_endpoint, + NULL); + } + if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker); + if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer); + gpr_slice_buffer_destroy(&h->left_overs); + gpr_slice_buffer_destroy(&h->outgoing); + gpr_slice_buffer_destroy(&h->incoming); + GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake"); + gpr_free(h); +} + +static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_security_status status) { + grpc_security_handshake *h = user_data; + tsi_frame_protector *protector; + tsi_result result; + if (status != GRPC_SECURITY_OK) { + gpr_log(GPR_ERROR, "Error checking peer."); + security_handshake_done(exec_ctx, h, 0); + return; + } + result = + tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Frame protector creation failed with error %s.", + tsi_result_to_string(result)); + security_handshake_done(exec_ctx, h, 0); + return; + } + h->secure_endpoint = + grpc_secure_endpoint_create(protector, h->wrapped_endpoint, + h->left_overs.slices, h->left_overs.count); + h->left_overs.count = 0; + h->left_overs.length = 0; + security_handshake_done(exec_ctx, h, 1); + return; +} + +static void check_peer(grpc_exec_ctx *exec_ctx, grpc_security_handshake *h) { + grpc_security_status peer_status; + tsi_peer peer; + tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer); + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Peer extraction failed with error %s", + tsi_result_to_string(result)); + security_handshake_done(exec_ctx, h, 0); + return; + } + peer_status = grpc_security_connector_check_peer(h->connector, peer, + on_peer_checked, h); + if (peer_status == GRPC_SECURITY_ERROR) { + gpr_log(GPR_ERROR, "Peer check failed."); + security_handshake_done(exec_ctx, h, 0); + return; + } else if (peer_status == GRPC_SECURITY_OK) { + on_peer_checked(exec_ctx, h, peer_status); + } +} + +static void send_handshake_bytes_to_peer(grpc_exec_ctx *exec_ctx, + grpc_security_handshake *h) { + size_t offset = 0; + tsi_result result = TSI_OK; + gpr_slice to_send; + + do { + size_t to_send_size = h->handshake_buffer_size - offset; + result = tsi_handshaker_get_bytes_to_send_to_peer( + h->handshaker, h->handshake_buffer + offset, &to_send_size); + offset += to_send_size; + if (result == TSI_INCOMPLETE_DATA) { + h->handshake_buffer_size *= 2; + h->handshake_buffer = + gpr_realloc(h->handshake_buffer, h->handshake_buffer_size); + } + } while (result == TSI_INCOMPLETE_DATA); + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshake failed with error %s", + tsi_result_to_string(result)); + security_handshake_done(exec_ctx, h, 0); + return; + } + + to_send = + gpr_slice_from_copied_buffer((const char *)h->handshake_buffer, offset); + gpr_slice_buffer_reset_and_unref(&h->outgoing); + gpr_slice_buffer_add(&h->outgoing, to_send); + /* TODO(klempner,jboeuf): This should probably use the client setup + deadline */ + grpc_endpoint_write(exec_ctx, h->wrapped_endpoint, &h->outgoing, + &h->on_handshake_data_sent_to_peer); +} + +static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, + void *handshake, int success) { + grpc_security_handshake *h = handshake; + size_t consumed_slice_size = 0; + tsi_result result = TSI_OK; + size_t i; + size_t num_left_overs; + int has_left_overs_in_current_slice = 0; + + if (!success) { + gpr_log(GPR_ERROR, "Read failed."); + security_handshake_done(exec_ctx, h, 0); + return; + } + + for (i = 0; i < h->incoming.count; i++) { + consumed_slice_size = GPR_SLICE_LENGTH(h->incoming.slices[i]); + result = tsi_handshaker_process_bytes_from_peer( + h->handshaker, GPR_SLICE_START_PTR(h->incoming.slices[i]), + &consumed_slice_size); + if (!tsi_handshaker_is_in_progress(h->handshaker)) break; + } + + if (tsi_handshaker_is_in_progress(h->handshaker)) { + /* We may need more data. */ + if (result == TSI_INCOMPLETE_DATA) { + grpc_endpoint_read(exec_ctx, h->wrapped_endpoint, &h->incoming, + &h->on_handshake_data_received_from_peer); + return; + } else { + send_handshake_bytes_to_peer(exec_ctx, h); + return; + } + } + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshake failed with error %s", + tsi_result_to_string(result)); + security_handshake_done(exec_ctx, h, 0); + return; + } + + /* Handshake is done and successful this point. */ + has_left_overs_in_current_slice = + (consumed_slice_size < GPR_SLICE_LENGTH(h->incoming.slices[i])); + num_left_overs = + (has_left_overs_in_current_slice ? 1 : 0) + h->incoming.count - i - 1; + if (num_left_overs == 0) { + check_peer(exec_ctx, h); + return; + } + + /* Put the leftovers in our buffer (ownership transfered). */ + if (has_left_overs_in_current_slice) { + gpr_slice_buffer_add( + &h->left_overs, + gpr_slice_split_tail(&h->incoming.slices[i], consumed_slice_size)); + gpr_slice_unref( + h->incoming.slices[i]); /* split_tail above increments refcount. */ + } + gpr_slice_buffer_addn( + &h->left_overs, &h->incoming.slices[i + 1], + num_left_overs - (size_t)has_left_overs_in_current_slice); + check_peer(exec_ctx, h); +} + +/* If handshake is NULL, the handshake is done. */ +static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, + void *handshake, int success) { + grpc_security_handshake *h = handshake; + + /* Make sure that write is OK. */ + if (!success) { + gpr_log(GPR_ERROR, "Write failed."); + if (handshake != NULL) security_handshake_done(exec_ctx, h, 0); + return; + } + + /* We may be done. */ + if (tsi_handshaker_is_in_progress(h->handshaker)) { + /* TODO(klempner,jboeuf): This should probably use the client setup + deadline */ + grpc_endpoint_read(exec_ctx, h->wrapped_endpoint, &h->incoming, + &h->on_handshake_data_received_from_peer); + } else { + check_peer(exec_ctx, h); + } +} + +void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx, + tsi_handshaker *handshaker, + grpc_security_connector *connector, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { + grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake)); + memset(h, 0, sizeof(grpc_security_handshake)); + h->handshaker = handshaker; + h->connector = GRPC_SECURITY_CONNECTOR_REF(connector, "handshake"); + h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; + h->handshake_buffer = gpr_malloc(h->handshake_buffer_size); + h->wrapped_endpoint = nonsecure_endpoint; + h->user_data = user_data; + h->cb = cb; + grpc_closure_init(&h->on_handshake_data_sent_to_peer, + on_handshake_data_sent_to_peer, h); + grpc_closure_init(&h->on_handshake_data_received_from_peer, + on_handshake_data_received_from_peer, h); + gpr_slice_buffer_init(&h->left_overs); + gpr_slice_buffer_init(&h->outgoing); + gpr_slice_buffer_init(&h->incoming); + send_handshake_bytes_to_peer(exec_ctx, h); +} diff --git a/src/core/security/secure_transport_setup.h b/src/core/security/handshake.h index d9b802556d..28eaa79dc3 100644 --- a/src/core/security/secure_transport_setup.h +++ b/src/core/security/handshake.h @@ -31,23 +31,18 @@ * */ -#ifndef GRPC_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H -#define GRPC_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H +#ifndef GRPC_INTERNAL_CORE_SECURITY_HANDSHAKE_H +#define GRPC_INTERNAL_CORE_SECURITY_HANDSHAKE_H #include "src/core/iomgr/endpoint.h" #include "src/core/security/security_connector.h" -/* --- Secure transport setup --- */ +/* Calls the callback upon completion. Takes owership of handshaker. */ +void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx, + tsi_handshaker *handshaker, + grpc_security_connector *connector, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data); -/* Ownership of the secure_endpoint is transfered. */ -typedef void (*grpc_secure_transport_setup_done_cb)( - void *user_data, grpc_security_status status, - grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint); - -/* Calls the callback upon completion. */ -void grpc_setup_secure_transport(grpc_security_connector *connector, - grpc_endpoint *nonsecure_endpoint, - grpc_secure_transport_setup_done_cb cb, - void *user_data); - -#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H */ +#endif /* GRPC_INTERNAL_CORE_SECURITY_HANDSHAKE_H */ diff --git a/src/core/security/jwt_verifier.c b/src/core/security/jwt_verifier.c index 38ad134a6a..9de8482025 100644 --- a/src/core/security/jwt_verifier.c +++ b/src/core/security/jwt_verifier.c @@ -33,6 +33,7 @@ #include "src/core/security/jwt_verifier.h" +#include <limits.h> #include <string.h> #include "src/core/httpcli/httpcli.h" @@ -144,7 +145,7 @@ static jose_header *jose_header_from_json(grpc_json *json, gpr_slice buffer) { /* We only support RSA-1.5 signatures for now. Beware of this if we add HMAC support: https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/ - */ + */ if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) || evp_md_from_alg(cur->value) == NULL) { gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value); @@ -412,7 +413,9 @@ static EVP_PKEY *extract_pkey_from_x509(const char *x509_str) { X509 *x509 = NULL; EVP_PKEY *result = NULL; BIO *bio = BIO_new(BIO_s_mem()); - BIO_write(bio, x509_str, strlen(x509_str)); + size_t len = strlen(x509_str); + GPR_ASSERT(len < INT_MAX); + BIO_write(bio, x509_str, (int)len); x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); if (x509 == NULL) { gpr_log(GPR_ERROR, "Unable to parse x509 cert."); @@ -439,7 +442,8 @@ static BIGNUM *bignum_from_base64(const char *b64) { gpr_log(GPR_ERROR, "Invalid base64 for big num."); return NULL; } - result = BN_bin2bn(GPR_SLICE_START_PTR(bin), GPR_SLICE_LENGTH(bin), NULL); + result = + BN_bin2bn(GPR_SLICE_START_PTR(bin), (int)GPR_SLICE_LENGTH(bin), NULL); gpr_slice_unref(bin); return result; } @@ -490,7 +494,7 @@ static EVP_PKEY *find_verification_key(const grpc_json *json, jwk_keys = find_property_by_name(json, "keys"); if (jwk_keys == NULL) { /* Use the google proprietary format which is: - { <kid1>: <x5091>, <kid2>: <x5092>, ... } */ + { <kid1>: <x5091>, <kid2>: <x5092>, ... } */ const grpc_json *cur = find_property_by_name(json, header_kid); if (cur == NULL) return NULL; return extract_pkey_from_x509(cur->value); @@ -565,7 +569,7 @@ end: return result; } -static void on_keys_retrieved(void *user_data, +static void on_keys_retrieved(grpc_exec_ctx *exec_ctx, void *user_data, const grpc_httpcli_response *response) { grpc_json *json = json_from_http(response); verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data; @@ -606,7 +610,7 @@ end: verifier_cb_ctx_destroy(ctx); } -static void on_openid_config_retrieved(void *user_data, +static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data, const grpc_httpcli_response *response) { const grpc_json *cur; grpc_json *json = json_from_http(response); @@ -614,7 +618,7 @@ static void on_openid_config_retrieved(void *user_data, grpc_httpcli_request req; const char *jwks_uri; - /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time.*/ + /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */ if (json == NULL) goto error; cur = find_property_by_name(json, "jwks_uri"); if (cur == NULL) { @@ -637,7 +641,7 @@ static void on_openid_config_retrieved(void *user_data, *(req.host + (req.path - jwks_uri)) = '\0'; } grpc_httpcli_get( - &ctx->verifier->http_ctx, ctx->pollset, &req, + exec_ctx, &ctx->verifier->http_ctx, ctx->pollset, &req, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), on_keys_retrieved, ctx); grpc_json_destroy(json); @@ -678,7 +682,8 @@ static void verifier_put_mapping(grpc_jwt_verifier *v, const char *email_domain, } /* Takes ownership of ctx. */ -static void retrieve_key_and_verify(verifier_cb_ctx *ctx) { +static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx, + verifier_cb_ctx *ctx) { const char *at_sign; grpc_httpcli_response_cb http_cb; char *path_prefix = NULL; @@ -739,7 +744,7 @@ static void retrieve_key_and_verify(verifier_cb_ctx *ctx) { } grpc_httpcli_get( - &ctx->verifier->http_ctx, ctx->pollset, &req, + exec_ctx, &ctx->verifier->http_ctx, ctx->pollset, &req, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), http_cb, ctx); gpr_free(req.host); @@ -751,7 +756,8 @@ error: verifier_cb_ctx_destroy(ctx); } -void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, +void grpc_jwt_verifier_verify(grpc_exec_ctx *exec_ctx, + grpc_jwt_verifier *verifier, grpc_pollset *pollset, const char *jwt, const char *audience, grpc_jwt_verification_done_cb cb, @@ -769,7 +775,7 @@ void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, GPR_ASSERT(verifier != NULL && jwt != NULL && audience != NULL && cb != NULL); dot = strchr(cur, '.'); if (dot == NULL) goto error; - json = parse_json_part_from_jwt(cur, dot - cur, &header_buffer); + json = parse_json_part_from_jwt(cur, (size_t)(dot - cur), &header_buffer); if (json == NULL) goto error; header = jose_header_from_json(json, header_buffer); if (header == NULL) goto error; @@ -777,7 +783,7 @@ void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, cur = dot + 1; dot = strchr(cur, '.'); if (dot == NULL) goto error; - json = parse_json_part_from_jwt(cur, dot - cur, &claims_buffer); + json = parse_json_part_from_jwt(cur, (size_t)(dot - cur), &claims_buffer); if (json == NULL) goto error; claims = grpc_jwt_claims_from_json(json, claims_buffer); if (claims == NULL) goto error; @@ -787,6 +793,7 @@ void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, signature = grpc_base64_decode(cur, 1); if (GPR_SLICE_IS_EMPTY(signature)) goto error; retrieve_key_and_verify( + exec_ctx, verifier_cb_ctx_create(verifier, pollset, header, claims, audience, signature, jwt, signed_jwt_len, user_data, cb)); return; diff --git a/src/core/security/jwt_verifier.h b/src/core/security/jwt_verifier.h index 7a32debfcb..51ea036e4a 100644 --- a/src/core/security/jwt_verifier.h +++ b/src/core/security/jwt_verifier.h @@ -120,7 +120,8 @@ typedef void (*grpc_jwt_verification_done_cb)(void *user_data, grpc_jwt_claims *claims); /* Verifies for the JWT for the given expected audience. */ -void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, +void grpc_jwt_verifier_verify(grpc_exec_ctx *exec_ctx, + grpc_jwt_verifier *verifier, grpc_pollset *pollset, const char *jwt, const char *audience, grpc_jwt_verification_done_cb cb, diff --git a/src/core/security/secure_endpoint.c b/src/core/security/secure_endpoint.c index 81b3e33cb2..fd50abb773 100644 --- a/src/core/security/secure_endpoint.c +++ b/src/core/security/secure_endpoint.c @@ -49,15 +49,15 @@ typedef struct { struct tsi_frame_protector *protector; gpr_mu protector_mu; /* saved upper level callbacks and user_data. */ - grpc_endpoint_read_cb read_cb; - void *read_user_data; - grpc_endpoint_write_cb write_cb; - void *write_user_data; + grpc_closure *read_cb; + grpc_closure *write_cb; + grpc_closure on_read; + gpr_slice_buffer *read_buffer; + gpr_slice_buffer source_buffer; /* saved handshaker leftover data to unprotect. */ gpr_slice_buffer leftover_bytes; /* buffers for read and write */ gpr_slice read_staging_buffer; - gpr_slice_buffer input_buffer; gpr_slice write_staging_buffer; gpr_slice_buffer output_buffer; @@ -67,62 +67,97 @@ typedef struct { int grpc_trace_secure_endpoint = 0; -static void secure_endpoint_ref(secure_endpoint *ep) { gpr_ref(&ep->ref); } - -static void destroy(secure_endpoint *secure_ep) { +static void destroy(grpc_exec_ctx *exec_ctx, secure_endpoint *secure_ep) { secure_endpoint *ep = secure_ep; - grpc_endpoint_destroy(ep->wrapped_ep); + grpc_endpoint_destroy(exec_ctx, ep->wrapped_ep); tsi_frame_protector_destroy(ep->protector); gpr_slice_buffer_destroy(&ep->leftover_bytes); gpr_slice_unref(ep->read_staging_buffer); - gpr_slice_buffer_destroy(&ep->input_buffer); gpr_slice_unref(ep->write_staging_buffer); gpr_slice_buffer_destroy(&ep->output_buffer); + gpr_slice_buffer_destroy(&ep->source_buffer); gpr_mu_destroy(&ep->protector_mu); gpr_free(ep); } -static void secure_endpoint_unref(secure_endpoint *ep) { +/*#define GRPC_SECURE_ENDPOINT_REFCOUNT_DEBUG*/ +#ifdef GRPC_SECURE_ENDPOINT_REFCOUNT_DEBUG +#define SECURE_ENDPOINT_UNREF(exec_ctx, ep, reason) \ + secure_endpoint_unref((exec_ctx), (ep), (reason), __FILE__, __LINE__) +#define SECURE_ENDPOINT_REF(ep, reason) \ + secure_endpoint_ref((ep), (reason), __FILE__, __LINE__) +static void secure_endpoint_unref(secure_endpoint *ep, + grpc_closure_list *closure_list, + const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "SECENDP unref %p : %s %d -> %d", + ep, reason, ep->ref.count, ep->ref.count - 1); if (gpr_unref(&ep->ref)) { - destroy(ep); + destroy(exec_ctx, ep); } } +static void secure_endpoint_ref(secure_endpoint *ep, const char *reason, + const char *file, int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "SECENDP ref %p : %s %d -> %d", + ep, reason, ep->ref.count, ep->ref.count + 1); + gpr_ref(&ep->ref); +} +#else +#define SECURE_ENDPOINT_UNREF(exec_ctx, ep, reason) \ + secure_endpoint_unref((exec_ctx), (ep)) +#define SECURE_ENDPOINT_REF(ep, reason) secure_endpoint_ref((ep)) +static void secure_endpoint_unref(grpc_exec_ctx *exec_ctx, + secure_endpoint *ep) { + if (gpr_unref(&ep->ref)) { + destroy(exec_ctx, ep); + } +} + +static void secure_endpoint_ref(secure_endpoint *ep) { gpr_ref(&ep->ref); } +#endif + static void flush_read_staging_buffer(secure_endpoint *ep, gpr_uint8 **cur, gpr_uint8 **end) { - gpr_slice_buffer_add(&ep->input_buffer, ep->read_staging_buffer); + gpr_slice_buffer_add(ep->read_buffer, ep->read_staging_buffer); ep->read_staging_buffer = gpr_slice_malloc(STAGING_BUFFER_SIZE); *cur = GPR_SLICE_START_PTR(ep->read_staging_buffer); *end = GPR_SLICE_END_PTR(ep->read_staging_buffer); } -static void call_read_cb(secure_endpoint *ep, gpr_slice *slices, size_t nslices, - grpc_endpoint_cb_status error) { +static void call_read_cb(grpc_exec_ctx *exec_ctx, secure_endpoint *ep, + int success) { if (grpc_trace_secure_endpoint) { size_t i; - for (i = 0; i < nslices; i++) { - char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + for (i = 0; i < ep->read_buffer->count; i++) { + char *data = gpr_dump_slice(ep->read_buffer->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "READ %p: %s", ep, data); gpr_free(data); } } - ep->read_cb(ep->read_user_data, slices, nslices, error); - secure_endpoint_unref(ep); + ep->read_buffer = NULL; + grpc_exec_ctx_enqueue(exec_ctx, ep->read_cb, success); + SECURE_ENDPOINT_UNREF(exec_ctx, ep, "read"); } -static void on_read(void *user_data, gpr_slice *slices, size_t nslices, - grpc_endpoint_cb_status error) { +static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, int success) { unsigned i; gpr_uint8 keep_looping = 0; - size_t input_buffer_count = 0; tsi_result result = TSI_OK; secure_endpoint *ep = (secure_endpoint *)user_data; gpr_uint8 *cur = GPR_SLICE_START_PTR(ep->read_staging_buffer); gpr_uint8 *end = GPR_SLICE_END_PTR(ep->read_staging_buffer); + if (!success) { + gpr_slice_buffer_reset_and_unref(ep->read_buffer); + call_read_cb(exec_ctx, ep, 0); + return; + } + /* TODO(yangg) check error, maybe bail out early */ - for (i = 0; i < nslices; i++) { - gpr_slice encrypted = slices[i]; + for (i = 0; i < ep->source_buffer.count; i++) { + gpr_slice encrypted = ep->source_buffer.slices[i]; gpr_uint8 *message_bytes = GPR_SLICE_START_PTR(encrypted); size_t message_size = GPR_SLICE_LENGTH(encrypted); @@ -161,7 +196,7 @@ static void on_read(void *user_data, gpr_slice *slices, size_t nslices, if (cur != GPR_SLICE_START_PTR(ep->read_staging_buffer)) { gpr_slice_buffer_add( - &ep->input_buffer, + ep->read_buffer, gpr_slice_split_head( &ep->read_staging_buffer, (size_t)(cur - GPR_SLICE_START_PTR(ep->read_staging_buffer)))); @@ -169,38 +204,34 @@ static void on_read(void *user_data, gpr_slice *slices, size_t nslices, /* TODO(yangg) experiment with moving this block after read_cb to see if it helps latency */ - for (i = 0; i < nslices; i++) { - gpr_slice_unref(slices[i]); - } + gpr_slice_buffer_reset_and_unref(&ep->source_buffer); if (result != TSI_OK) { - gpr_slice_buffer_reset_and_unref(&ep->input_buffer); - call_read_cb(ep, NULL, 0, GRPC_ENDPOINT_CB_ERROR); + gpr_slice_buffer_reset_and_unref(ep->read_buffer); + call_read_cb(exec_ctx, ep, 0); return; } - /* The upper level will unref the slices. */ - input_buffer_count = ep->input_buffer.count; - ep->input_buffer.count = 0; - call_read_cb(ep, ep->input_buffer.slices, input_buffer_count, error); + + call_read_cb(exec_ctx, ep, 1); } -static void endpoint_notify_on_read(grpc_endpoint *secure_ep, - grpc_endpoint_read_cb cb, void *user_data) { +static void endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, + gpr_slice_buffer *slices, grpc_closure *cb) { secure_endpoint *ep = (secure_endpoint *)secure_ep; ep->read_cb = cb; - ep->read_user_data = user_data; - - secure_endpoint_ref(ep); + ep->read_buffer = slices; + gpr_slice_buffer_reset_and_unref(ep->read_buffer); + SECURE_ENDPOINT_REF(ep, "read"); if (ep->leftover_bytes.count) { - size_t leftover_nslices = ep->leftover_bytes.count; - ep->leftover_bytes.count = 0; - on_read(ep, ep->leftover_bytes.slices, leftover_nslices, - GRPC_ENDPOINT_CB_OK); + gpr_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer); + GPR_ASSERT(ep->leftover_bytes.count == 0); + on_read(exec_ctx, ep, 1); return; } - grpc_endpoint_notify_on_read(ep->wrapped_ep, on_read, ep); + grpc_endpoint_read(exec_ctx, ep->wrapped_ep, &ep->source_buffer, + &ep->on_read); } static void flush_write_staging_buffer(secure_endpoint *ep, gpr_uint8 **cur, @@ -211,36 +242,27 @@ static void flush_write_staging_buffer(secure_endpoint *ep, gpr_uint8 **cur, *end = GPR_SLICE_END_PTR(ep->write_staging_buffer); } -static void on_write(void *data, grpc_endpoint_cb_status error) { - secure_endpoint *ep = data; - ep->write_cb(ep->write_user_data, error); - secure_endpoint_unref(ep); -} - -static grpc_endpoint_write_status endpoint_write(grpc_endpoint *secure_ep, - gpr_slice *slices, - size_t nslices, - grpc_endpoint_write_cb cb, - void *user_data) { +static void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, + gpr_slice_buffer *slices, grpc_closure *cb) { unsigned i; - size_t output_buffer_count = 0; tsi_result result = TSI_OK; secure_endpoint *ep = (secure_endpoint *)secure_ep; gpr_uint8 *cur = GPR_SLICE_START_PTR(ep->write_staging_buffer); gpr_uint8 *end = GPR_SLICE_END_PTR(ep->write_staging_buffer); - grpc_endpoint_write_status status; - GPR_ASSERT(ep->output_buffer.count == 0); + + gpr_slice_buffer_reset_and_unref(&ep->output_buffer); if (grpc_trace_secure_endpoint) { - for (i = 0; i < nslices; i++) { - char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + for (i = 0; i < slices->count; i++) { + char *data = + gpr_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "WRITE %p: %s", ep, data); gpr_free(data); } } - for (i = 0; i < nslices; i++) { - gpr_slice plain = slices[i]; + for (i = 0; i < slices->count; i++) { + gpr_slice plain = slices->slices[i]; gpr_uint8 *message_bytes = GPR_SLICE_START_PTR(plain); size_t message_size = GPR_SLICE_LENGTH(plain); while (message_size > 0) { @@ -290,51 +312,40 @@ static grpc_endpoint_write_status endpoint_write(grpc_endpoint *secure_ep, } } - for (i = 0; i < nslices; i++) { - gpr_slice_unref(slices[i]); - } - if (result != TSI_OK) { /* TODO(yangg) do different things according to the error type? */ gpr_slice_buffer_reset_and_unref(&ep->output_buffer); - return GRPC_ENDPOINT_WRITE_ERROR; + grpc_exec_ctx_enqueue(exec_ctx, cb, 0); + return; } - /* clear output_buffer and let the lower level handle its slices. */ - output_buffer_count = ep->output_buffer.count; - ep->output_buffer.count = 0; - ep->write_cb = cb; - ep->write_user_data = user_data; - /* Need to keep the endpoint alive across a transport */ - secure_endpoint_ref(ep); - status = grpc_endpoint_write(ep->wrapped_ep, ep->output_buffer.slices, - output_buffer_count, on_write, ep); - if (status != GRPC_ENDPOINT_WRITE_PENDING) { - secure_endpoint_unref(ep); - } - return status; + grpc_endpoint_write(exec_ctx, ep->wrapped_ep, &ep->output_buffer, cb); } -static void endpoint_shutdown(grpc_endpoint *secure_ep) { +static void endpoint_shutdown(grpc_exec_ctx *exec_ctx, + grpc_endpoint *secure_ep) { secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_shutdown(ep->wrapped_ep); + grpc_endpoint_shutdown(exec_ctx, ep->wrapped_ep); } -static void endpoint_unref(grpc_endpoint *secure_ep) { +static void endpoint_destroy(grpc_exec_ctx *exec_ctx, + grpc_endpoint *secure_ep) { secure_endpoint *ep = (secure_endpoint *)secure_ep; - secure_endpoint_unref(ep); + SECURE_ENDPOINT_UNREF(exec_ctx, ep, "destroy"); } -static void endpoint_add_to_pollset(grpc_endpoint *secure_ep, +static void endpoint_add_to_pollset(grpc_exec_ctx *exec_ctx, + grpc_endpoint *secure_ep, grpc_pollset *pollset) { secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_add_to_pollset(ep->wrapped_ep, pollset); + grpc_endpoint_add_to_pollset(exec_ctx, ep->wrapped_ep, pollset); } -static void endpoint_add_to_pollset_set(grpc_endpoint *secure_ep, +static void endpoint_add_to_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_endpoint *secure_ep, grpc_pollset_set *pollset_set) { secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_add_to_pollset_set(ep->wrapped_ep, pollset_set); + grpc_endpoint_add_to_pollset_set(exec_ctx, ep->wrapped_ep, pollset_set); } static char *endpoint_get_peer(grpc_endpoint *secure_ep) { @@ -343,9 +354,8 @@ static char *endpoint_get_peer(grpc_endpoint *secure_ep) { } static const grpc_endpoint_vtable vtable = { - endpoint_notify_on_read, endpoint_write, - endpoint_add_to_pollset, endpoint_add_to_pollset_set, - endpoint_shutdown, endpoint_unref, + endpoint_read, endpoint_write, endpoint_add_to_pollset, + endpoint_add_to_pollset_set, endpoint_shutdown, endpoint_destroy, endpoint_get_peer}; grpc_endpoint *grpc_secure_endpoint_create( @@ -363,8 +373,10 @@ grpc_endpoint *grpc_secure_endpoint_create( } ep->write_staging_buffer = gpr_slice_malloc(STAGING_BUFFER_SIZE); ep->read_staging_buffer = gpr_slice_malloc(STAGING_BUFFER_SIZE); - gpr_slice_buffer_init(&ep->input_buffer); gpr_slice_buffer_init(&ep->output_buffer); + gpr_slice_buffer_init(&ep->source_buffer); + ep->read_buffer = NULL; + grpc_closure_init(&ep->on_read, on_read, ep); gpr_mu_init(&ep->protector_mu); gpr_ref_init(&ep->ref, 1); return &ep->base; diff --git a/src/core/security/secure_transport_setup.c b/src/core/security/secure_transport_setup.c deleted file mode 100644 index 0c3572b53c..0000000000 --- a/src/core/security/secure_transport_setup.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/security/secure_transport_setup.h" - -#include <string.h> - -#include "src/core/security/secure_endpoint.h" -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/slice_buffer.h> - -#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 - -typedef struct { - grpc_security_connector *connector; - tsi_handshaker *handshaker; - unsigned char *handshake_buffer; - size_t handshake_buffer_size; - grpc_endpoint *wrapped_endpoint; - grpc_endpoint *secure_endpoint; - gpr_slice_buffer left_overs; - grpc_secure_transport_setup_done_cb cb; - void *user_data; -} grpc_secure_transport_setup; - -static void on_handshake_data_received_from_peer(void *setup, gpr_slice *slices, - size_t nslices, - grpc_endpoint_cb_status error); - -static void on_handshake_data_sent_to_peer(void *setup, - grpc_endpoint_cb_status error); - -static void secure_transport_setup_done(grpc_secure_transport_setup *s, - int is_success) { - if (is_success) { - s->cb(s->user_data, GRPC_SECURITY_OK, s->wrapped_endpoint, - s->secure_endpoint); - } else { - if (s->secure_endpoint != NULL) { - grpc_endpoint_shutdown(s->secure_endpoint); - grpc_endpoint_destroy(s->secure_endpoint); - } else { - grpc_endpoint_destroy(s->wrapped_endpoint); - } - s->cb(s->user_data, GRPC_SECURITY_ERROR, s->wrapped_endpoint, NULL); - } - if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker); - if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer); - gpr_slice_buffer_destroy(&s->left_overs); - GRPC_SECURITY_CONNECTOR_UNREF(s->connector, "secure_transport_setup"); - gpr_free(s); -} - -static void on_peer_checked(void *user_data, grpc_security_status status) { - grpc_secure_transport_setup *s = user_data; - tsi_frame_protector *protector; - tsi_result result; - if (status != GRPC_SECURITY_OK) { - gpr_log(GPR_ERROR, "Error checking peer."); - secure_transport_setup_done(s, 0); - return; - } - result = - tsi_handshaker_create_frame_protector(s->handshaker, NULL, &protector); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Frame protector creation failed with error %s.", - tsi_result_to_string(result)); - secure_transport_setup_done(s, 0); - return; - } - s->secure_endpoint = - grpc_secure_endpoint_create(protector, s->wrapped_endpoint, - s->left_overs.slices, s->left_overs.count); - secure_transport_setup_done(s, 1); - return; -} - -static void check_peer(grpc_secure_transport_setup *s) { - grpc_security_status peer_status; - tsi_peer peer; - tsi_result result = tsi_handshaker_extract_peer(s->handshaker, &peer); - - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Peer extraction failed with error %s", - tsi_result_to_string(result)); - secure_transport_setup_done(s, 0); - return; - } - peer_status = grpc_security_connector_check_peer(s->connector, peer, - on_peer_checked, s); - if (peer_status == GRPC_SECURITY_ERROR) { - gpr_log(GPR_ERROR, "Peer check failed."); - secure_transport_setup_done(s, 0); - return; - } else if (peer_status == GRPC_SECURITY_OK) { - on_peer_checked(s, peer_status); - } -} - -static void send_handshake_bytes_to_peer(grpc_secure_transport_setup *s) { - size_t offset = 0; - tsi_result result = TSI_OK; - gpr_slice to_send; - grpc_endpoint_write_status write_status; - - do { - size_t to_send_size = s->handshake_buffer_size - offset; - result = tsi_handshaker_get_bytes_to_send_to_peer( - s->handshaker, s->handshake_buffer + offset, &to_send_size); - offset += to_send_size; - if (result == TSI_INCOMPLETE_DATA) { - s->handshake_buffer_size *= 2; - s->handshake_buffer = - gpr_realloc(s->handshake_buffer, s->handshake_buffer_size); - } - } while (result == TSI_INCOMPLETE_DATA); - - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshake failed with error %s", - tsi_result_to_string(result)); - secure_transport_setup_done(s, 0); - return; - } - - to_send = - gpr_slice_from_copied_buffer((const char *)s->handshake_buffer, offset); - /* TODO(klempner,jboeuf): This should probably use the client setup - deadline */ - write_status = grpc_endpoint_write(s->wrapped_endpoint, &to_send, 1, - on_handshake_data_sent_to_peer, s); - if (write_status == GRPC_ENDPOINT_WRITE_ERROR) { - gpr_log(GPR_ERROR, "Could not send handshake data to peer."); - secure_transport_setup_done(s, 0); - } else if (write_status == GRPC_ENDPOINT_WRITE_DONE) { - on_handshake_data_sent_to_peer(s, GRPC_ENDPOINT_CB_OK); - } -} - -static void cleanup_slices(gpr_slice *slices, size_t num_slices) { - size_t i; - for (i = 0; i < num_slices; i++) { - gpr_slice_unref(slices[i]); - } -} - -static void on_handshake_data_received_from_peer( - void *setup, gpr_slice *slices, size_t nslices, - grpc_endpoint_cb_status error) { - grpc_secure_transport_setup *s = setup; - size_t consumed_slice_size = 0; - tsi_result result = TSI_OK; - size_t i; - size_t num_left_overs; - int has_left_overs_in_current_slice = 0; - - if (error != GRPC_ENDPOINT_CB_OK) { - gpr_log(GPR_ERROR, "Read failed."); - cleanup_slices(slices, nslices); - secure_transport_setup_done(s, 0); - return; - } - - for (i = 0; i < nslices; i++) { - consumed_slice_size = GPR_SLICE_LENGTH(slices[i]); - result = tsi_handshaker_process_bytes_from_peer( - s->handshaker, GPR_SLICE_START_PTR(slices[i]), &consumed_slice_size); - if (!tsi_handshaker_is_in_progress(s->handshaker)) break; - } - - if (tsi_handshaker_is_in_progress(s->handshaker)) { - /* We may need more data. */ - if (result == TSI_INCOMPLETE_DATA) { - /* TODO(klempner,jboeuf): This should probably use the client setup - deadline */ - grpc_endpoint_notify_on_read(s->wrapped_endpoint, - on_handshake_data_received_from_peer, setup); - cleanup_slices(slices, nslices); - return; - } else { - send_handshake_bytes_to_peer(s); - cleanup_slices(slices, nslices); - return; - } - } - - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshake failed with error %s", - tsi_result_to_string(result)); - cleanup_slices(slices, nslices); - secure_transport_setup_done(s, 0); - return; - } - - /* Handshake is done and successful this point. */ - has_left_overs_in_current_slice = - (consumed_slice_size < GPR_SLICE_LENGTH(slices[i])); - num_left_overs = (has_left_overs_in_current_slice ? 1 : 0) + nslices - i - 1; - if (num_left_overs == 0) { - cleanup_slices(slices, nslices); - check_peer(s); - return; - } - cleanup_slices(slices, nslices - num_left_overs); - - /* Put the leftovers in our buffer (ownership transfered). */ - if (has_left_overs_in_current_slice) { - gpr_slice_buffer_add(&s->left_overs, - gpr_slice_split_tail(&slices[i], consumed_slice_size)); - gpr_slice_unref(slices[i]); /* split_tail above increments refcount. */ - } - gpr_slice_buffer_addn( - &s->left_overs, &slices[i + 1], - num_left_overs - (size_t)has_left_overs_in_current_slice); - check_peer(s); -} - -/* If setup is NULL, the setup is done. */ -static void on_handshake_data_sent_to_peer(void *setup, - grpc_endpoint_cb_status error) { - grpc_secure_transport_setup *s = setup; - - /* Make sure that write is OK. */ - if (error != GRPC_ENDPOINT_CB_OK) { - gpr_log(GPR_ERROR, "Write failed with error %d.", error); - if (setup != NULL) secure_transport_setup_done(s, 0); - return; - } - - /* We may be done. */ - if (tsi_handshaker_is_in_progress(s->handshaker)) { - /* TODO(klempner,jboeuf): This should probably use the client setup - deadline */ - grpc_endpoint_notify_on_read(s->wrapped_endpoint, - on_handshake_data_received_from_peer, setup); - } else { - check_peer(s); - } -} - -void grpc_setup_secure_transport(grpc_security_connector *connector, - grpc_endpoint *nonsecure_endpoint, - grpc_secure_transport_setup_done_cb cb, - void *user_data) { - grpc_security_status result = GRPC_SECURITY_OK; - grpc_secure_transport_setup *s = - gpr_malloc(sizeof(grpc_secure_transport_setup)); - memset(s, 0, sizeof(grpc_secure_transport_setup)); - result = grpc_security_connector_create_handshaker(connector, &s->handshaker); - if (result != GRPC_SECURITY_OK) { - secure_transport_setup_done(s, 0); - return; - } - s->connector = - GRPC_SECURITY_CONNECTOR_REF(connector, "secure_transport_setup"); - s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; - s->handshake_buffer = gpr_malloc(s->handshake_buffer_size); - s->wrapped_endpoint = nonsecure_endpoint; - s->user_data = user_data; - s->cb = cb; - gpr_slice_buffer_init(&s->left_overs); - send_handshake_bytes_to_peer(s); -} diff --git a/src/core/security/security_connector.c b/src/core/security/security_connector.c index ba9ac68c5f..7c4cf6f04d 100644 --- a/src/core/security/security_connector.c +++ b/src/core/security/security_connector.c @@ -36,6 +36,7 @@ #include <string.h> #include "src/core/security/credentials.h" +#include "src/core/security/handshake.h" #include "src/core/security/secure_endpoint.h" #include "src/core/security/security_context.h" #include "src/core/support/env.h" @@ -101,10 +102,16 @@ const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, return NULL; } -grpc_security_status grpc_security_connector_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { - if (sc == NULL || handshaker == NULL) return GRPC_SECURITY_ERROR; - return sc->vtable->create_handshaker(sc, handshaker); +void grpc_security_connector_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { + if (sc == NULL || nonsecure_endpoint == NULL) { + cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + } else { + sc->vtable->do_handshake(exec_ctx, sc, nonsecure_endpoint, cb, user_data); + } } grpc_security_status grpc_security_connector_check_peer( @@ -118,10 +125,10 @@ grpc_security_status grpc_security_connector_check_peer( } grpc_security_status grpc_channel_security_connector_check_call_host( - grpc_channel_security_connector *sc, const char *host, - grpc_security_check_cb cb, void *user_data) { + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + const char *host, grpc_security_check_cb cb, void *user_data) { if (sc == NULL || sc->check_call_host == NULL) return GRPC_SECURITY_ERROR; - return sc->check_call_host(sc, host, cb, user_data); + return sc->check_call_host(exec_ctx, sc, host, cb, user_data); } #ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG @@ -225,18 +232,6 @@ static void fake_server_destroy(grpc_security_connector *sc) { gpr_free(sc); } -static grpc_security_status fake_channel_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { - *handshaker = tsi_create_fake_handshaker(1); - return GRPC_SECURITY_OK; -} - -static grpc_security_status fake_server_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { - *handshaker = tsi_create_fake_handshaker(0); - return GRPC_SECURITY_OK; -} - static grpc_security_status fake_check_peer(grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb, @@ -274,23 +269,41 @@ end: } static grpc_security_status fake_channel_check_call_host( - grpc_channel_security_connector *sc, const char *host, - grpc_security_check_cb cb, void *user_data) { + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + const char *host, grpc_security_check_cb cb, void *user_data) { grpc_fake_channel_security_connector *c = (grpc_fake_channel_security_connector *)sc; if (c->call_host_check_is_async) { - cb(user_data, GRPC_SECURITY_OK); + cb(exec_ctx, user_data, GRPC_SECURITY_OK); return GRPC_SECURITY_PENDING; } else { return GRPC_SECURITY_OK; } } +static void fake_channel_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { + grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(1), sc, + nonsecure_endpoint, cb, user_data); +} + +static void fake_server_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { + grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(0), sc, + nonsecure_endpoint, cb, user_data); +} + static grpc_security_connector_vtable fake_channel_vtable = { - fake_channel_destroy, fake_channel_create_handshaker, fake_check_peer}; + fake_channel_destroy, fake_channel_do_handshake, fake_check_peer}; static grpc_security_connector_vtable fake_server_vtable = { - fake_server_destroy, fake_server_create_handshaker, fake_check_peer}; + fake_server_destroy, fake_server_do_handshake, fake_check_peer}; grpc_channel_security_connector *grpc_fake_channel_security_connector_create( grpc_credentials *request_metadata_creds, int call_host_check_is_async) { @@ -372,22 +385,43 @@ static grpc_security_status ssl_create_handshaker( return GRPC_SECURITY_OK; } -static grpc_security_status ssl_channel_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { +static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { grpc_ssl_channel_security_connector *c = (grpc_ssl_channel_security_connector *)sc; - return ssl_create_handshaker(c->handshaker_factory, 1, - c->overridden_target_name != NULL - ? c->overridden_target_name - : c->target_name, - handshaker); + tsi_handshaker *handshaker; + grpc_security_status status = ssl_create_handshaker( + c->handshaker_factory, 1, + c->overridden_target_name != NULL ? c->overridden_target_name + : c->target_name, + &handshaker); + if (status != GRPC_SECURITY_OK) { + cb(exec_ctx, user_data, status, nonsecure_endpoint, NULL); + } else { + grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, + user_data); + } } -static grpc_security_status ssl_server_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { +static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { grpc_ssl_server_security_connector *c = (grpc_ssl_server_security_connector *)sc; - return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker); + tsi_handshaker *handshaker; + grpc_security_status status = + ssl_create_handshaker(c->handshaker_factory, 0, NULL, &handshaker); + if (status != GRPC_SECURITY_OK) { + cb(exec_ctx, user_data, status, nonsecure_endpoint, NULL); + } else { + grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, + user_data); + } } static int ssl_host_matches_name(const tsi_peer *peer, const char *peer_name) { @@ -494,8 +528,8 @@ static grpc_security_status ssl_server_check_peer(grpc_security_connector *sc, } static grpc_security_status ssl_channel_check_call_host( - grpc_channel_security_connector *sc, const char *host, - grpc_security_check_cb cb, void *user_data) { + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + const char *host, grpc_security_check_cb cb, void *user_data) { grpc_ssl_channel_security_connector *c = (grpc_ssl_channel_security_connector *)sc; @@ -512,10 +546,10 @@ static grpc_security_status ssl_channel_check_call_host( } static grpc_security_connector_vtable ssl_channel_vtable = { - ssl_channel_destroy, ssl_channel_create_handshaker, ssl_channel_check_peer}; + ssl_channel_destroy, ssl_channel_do_handshake, ssl_channel_check_peer}; static grpc_security_connector_vtable ssl_server_vtable = { - ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer}; + ssl_server_destroy, ssl_server_do_handshake, ssl_server_check_peer}; static gpr_slice default_pem_root_certs; @@ -614,12 +648,12 @@ grpc_security_status grpc_ssl_channel_security_connector_create( goto error; } *sc = &c->base; - gpr_free(alpn_protocol_strings); + gpr_free((void *)alpn_protocol_strings); gpr_free(alpn_protocol_string_lengths); return GRPC_SECURITY_OK; error: - gpr_free(alpn_protocol_strings); + gpr_free((void *)alpn_protocol_strings); gpr_free(alpn_protocol_string_lengths); return GRPC_SECURITY_ERROR; } @@ -669,12 +703,12 @@ grpc_security_status grpc_ssl_server_security_connector_create( goto error; } *sc = &c->base; - gpr_free(alpn_protocol_strings); + gpr_free((void *)alpn_protocol_strings); gpr_free(alpn_protocol_string_lengths); return GRPC_SECURITY_OK; error: - gpr_free(alpn_protocol_strings); + gpr_free((void *)alpn_protocol_strings); gpr_free(alpn_protocol_string_lengths); return GRPC_SECURITY_ERROR; } diff --git a/src/core/security/security_connector.h b/src/core/security/security_connector.h index 2c9aa1c5a4..9218a3caab 100644 --- a/src/core/security/security_connector.h +++ b/src/core/security/security_connector.h @@ -60,13 +60,21 @@ typedef struct grpc_security_connector grpc_security_connector; #define GRPC_SECURITY_CONNECTOR_ARG "grpc.security_connector" -typedef void (*grpc_security_check_cb)(void *user_data, +typedef void (*grpc_security_check_cb)(grpc_exec_ctx *exec_ctx, void *user_data, grpc_security_status status); +/* Ownership of the secure_endpoint is transfered. */ +typedef void (*grpc_security_handshake_done_cb)(grpc_exec_ctx *exec_ctx, + void *user_data, + grpc_security_status status, + grpc_endpoint *wrapped_endpoint, + grpc_endpoint *secure_endpoint); + typedef struct { void (*destroy)(grpc_security_connector *sc); - grpc_security_status (*create_handshaker)(grpc_security_connector *sc, - tsi_handshaker **handshaker); + void (*do_handshake)(grpc_exec_ctx *exec_ctx, grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, void *user_data); grpc_security_status (*check_peer)(grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb, void *user_data); @@ -100,9 +108,12 @@ grpc_security_connector *grpc_security_connector_ref( void grpc_security_connector_unref(grpc_security_connector *policy); #endif -/* Handshake creation. */ -grpc_security_status grpc_security_connector_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker); +/* Handshake. */ +void grpc_security_connector_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_security_connector *connector, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data); /* Check the peer. Implementations can choose to check the peer either synchronously or @@ -135,7 +146,8 @@ typedef struct grpc_channel_security_connector grpc_channel_security_connector; struct grpc_channel_security_connector { grpc_security_connector base; /* requires is_client_side to be non 0. */ grpc_credentials *request_metadata_creds; - grpc_security_status (*check_call_host)(grpc_channel_security_connector *sc, + grpc_security_status (*check_call_host)(grpc_exec_ctx *exec_ctx, + grpc_channel_security_connector *sc, const char *host, grpc_security_check_cb cb, void *user_data); @@ -147,8 +159,8 @@ struct grpc_channel_security_connector { GRPC_SECURITY_OK. In the asynchronous case, the call will return GRPC_SECURITY_PENDING unless an error is detected early on. */ grpc_security_status grpc_channel_security_connector_check_call_host( - grpc_channel_security_connector *sc, const char *host, - grpc_security_check_cb cb, void *user_data); + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + const char *host, grpc_security_check_cb cb, void *user_data); /* --- Creation security connectors. --- */ diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c index 95d80ba122..f544c1d943 100644 --- a/src/core/security/security_context.c +++ b/src/core/security/security_context.c @@ -34,6 +34,7 @@ #include <string.h> #include "src/core/security/security_context.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/call.h" #include "src/core/support/string.h" @@ -47,6 +48,8 @@ grpc_call_error grpc_call_set_credentials(grpc_call *call, grpc_credentials *creds) { grpc_client_security_context *ctx = NULL; + GRPC_API_TRACE("grpc_call_set_credentials(call=%p, creds=%p)", 2, + (call, creds)); if (!grpc_call_is_client(call)) { gpr_log(GPR_ERROR, "Method is client-side only."); return GRPC_CALL_ERROR_NOT_ON_SERVER; @@ -71,6 +74,7 @@ grpc_call_error grpc_call_set_credentials(grpc_call *call, grpc_auth_context *grpc_call_auth_context(grpc_call *call) { void *sec_ctx = grpc_call_context_get(call, GRPC_CONTEXT_SECURITY); + GRPC_API_TRACE("grpc_call_auth_context(call=%p)", 1, (call)); if (sec_ctx == NULL) return NULL; return grpc_call_is_client(call) ? GRPC_AUTH_CONTEXT_REF( @@ -82,6 +86,7 @@ grpc_auth_context *grpc_call_auth_context(grpc_call *call) { } void grpc_auth_context_release(grpc_auth_context *context) { + GRPC_API_TRACE("grpc_auth_context_release(context=%p)", 1, (context)); GRPC_AUTH_CONTEXT_UNREF(context, "grpc_auth_context_unref"); } @@ -174,6 +179,8 @@ void grpc_auth_context_unref(grpc_auth_context *ctx) { const char *grpc_auth_context_peer_identity_property_name( const grpc_auth_context *ctx) { + GRPC_API_TRACE("grpc_auth_context_peer_identity_property_name(ctx=%p)", 1, + (ctx)); return ctx->peer_identity_property_name; } @@ -182,6 +189,9 @@ int grpc_auth_context_set_peer_identity_property_name(grpc_auth_context *ctx, grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name(ctx, name); const grpc_auth_property *prop = grpc_auth_property_iterator_next(&it); + GRPC_API_TRACE( + "grpc_auth_context_set_peer_identity_property_name(ctx=%p, name=%s)", 2, + (ctx, name)); if (prop == NULL) { gpr_log(GPR_ERROR, "Property name %s not found in auth context.", name != NULL ? name : "NULL"); @@ -192,12 +202,14 @@ int grpc_auth_context_set_peer_identity_property_name(grpc_auth_context *ctx, } int grpc_auth_context_peer_is_authenticated(const grpc_auth_context *ctx) { + GRPC_API_TRACE("grpc_auth_context_peer_is_authenticated(ctx=%p)", 1, (ctx)); return ctx->peer_identity_property_name == NULL ? 0 : 1; } grpc_auth_property_iterator grpc_auth_context_property_iterator( const grpc_auth_context *ctx) { grpc_auth_property_iterator it = empty_iterator; + GRPC_API_TRACE("grpc_auth_context_property_iterator(ctx=%p)", 1, (ctx)); if (ctx == NULL) return it; it.ctx = ctx; return it; @@ -205,6 +217,7 @@ grpc_auth_property_iterator grpc_auth_context_property_iterator( const grpc_auth_property *grpc_auth_property_iterator_next( grpc_auth_property_iterator *it) { + GRPC_API_TRACE("grpc_auth_property_iterator_next(it=%p)", 1, (it)); if (it == NULL || it->ctx == NULL) return NULL; while (it->index == it->ctx->properties.count) { if (it->ctx->chained == NULL) return NULL; @@ -229,6 +242,8 @@ const grpc_auth_property *grpc_auth_property_iterator_next( grpc_auth_property_iterator grpc_auth_context_find_properties_by_name( const grpc_auth_context *ctx, const char *name) { grpc_auth_property_iterator it = empty_iterator; + GRPC_API_TRACE("grpc_auth_context_find_properties_by_name(ctx=%p, name=%s)", + 2, (ctx, name)); if (ctx == NULL || name == NULL) return empty_iterator; it.ctx = ctx; it.name = name; @@ -237,6 +252,7 @@ grpc_auth_property_iterator grpc_auth_context_find_properties_by_name( grpc_auth_property_iterator grpc_auth_context_peer_identity( const grpc_auth_context *ctx) { + GRPC_API_TRACE("grpc_auth_context_peer_identity(ctx=%p)", 1, (ctx)); if (ctx == NULL) return empty_iterator; return grpc_auth_context_find_properties_by_name( ctx, ctx->peer_identity_property_name); @@ -255,6 +271,11 @@ static void ensure_auth_context_capacity(grpc_auth_context *ctx) { void grpc_auth_context_add_property(grpc_auth_context *ctx, const char *name, const char *value, size_t value_length) { grpc_auth_property *prop; + GRPC_API_TRACE( + "grpc_auth_context_add_property(ctx=%p, name=%s, value=%*.*s, " + "value_length=%lu)", + 6, (ctx, name, (int)value_length, (int)value_length, value, + (unsigned long)value_length)); ensure_auth_context_capacity(ctx); prop = &ctx->properties.array[ctx->properties.count++]; prop->name = gpr_strdup(name); @@ -268,6 +289,9 @@ void grpc_auth_context_add_cstring_property(grpc_auth_context *ctx, const char *name, const char *value) { grpc_auth_property *prop; + GRPC_API_TRACE( + "grpc_auth_context_add_cstring_property(ctx=%p, name=%s, value=%s)", 3, + (ctx, name, value)); ensure_auth_context_capacity(ctx); prop = &ctx->properties.array[ctx->properties.count++]; prop->name = gpr_strdup(name); @@ -281,33 +305,43 @@ void grpc_auth_property_reset(grpc_auth_property *property) { memset(property, 0, sizeof(grpc_auth_property)); } -grpc_arg grpc_auth_metadata_processor_to_arg(grpc_auth_metadata_processor *p) { +static void auth_context_pointer_arg_destroy(void *p) { + GRPC_AUTH_CONTEXT_UNREF(p, "auth_context_pointer_arg"); +} + +static void *auth_context_pointer_arg_copy(void *p) { + return GRPC_AUTH_CONTEXT_REF(p, "auth_context_pointer_arg"); +} + +grpc_arg grpc_auth_context_to_arg(grpc_auth_context *p) { grpc_arg arg; memset(&arg, 0, sizeof(grpc_arg)); arg.type = GRPC_ARG_POINTER; - arg.key = GRPC_AUTH_METADATA_PROCESSOR_ARG; + arg.key = GRPC_AUTH_CONTEXT_ARG; arg.value.pointer.p = p; + arg.value.pointer.copy = auth_context_pointer_arg_copy; + arg.value.pointer.destroy = auth_context_pointer_arg_destroy; return arg; } -grpc_auth_metadata_processor *grpc_auth_metadata_processor_from_arg( +grpc_auth_context *grpc_auth_context_from_arg( const grpc_arg *arg) { - if (strcmp(arg->key, GRPC_AUTH_METADATA_PROCESSOR_ARG) != 0) return NULL; + if (strcmp(arg->key, GRPC_AUTH_CONTEXT_ARG) != 0) return NULL; if (arg->type != GRPC_ARG_POINTER) { gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, - GRPC_AUTH_METADATA_PROCESSOR_ARG); + GRPC_AUTH_CONTEXT_ARG); return NULL; } return arg->value.pointer.p; } -grpc_auth_metadata_processor *grpc_find_auth_metadata_processor_in_args( +grpc_auth_context *grpc_find_auth_context_in_args( const grpc_channel_args *args) { size_t i; if (args == NULL) return NULL; for (i = 0; i < args->num_args; i++) { - grpc_auth_metadata_processor *p = - grpc_auth_metadata_processor_from_arg(&args->args[i]); + grpc_auth_context *p = + grpc_auth_context_from_arg(&args->args[i]); if (p != NULL) return p; } return NULL; diff --git a/src/core/security/security_context.h b/src/core/security/security_context.h index a9a0306410..2bbdc4be97 100644 --- a/src/core/security/security_context.h +++ b/src/core/security/security_context.h @@ -103,13 +103,12 @@ typedef struct { grpc_server_security_context *grpc_server_security_context_create(void); void grpc_server_security_context_destroy(void *ctx); -/* --- Auth metadata processing. --- */ -#define GRPC_AUTH_METADATA_PROCESSOR_ARG "grpc.auth_metadata_processor" +/* --- Channel args for auth context --- */ +#define GRPC_AUTH_CONTEXT_ARG "grpc.auth_context" -grpc_arg grpc_auth_metadata_processor_to_arg(grpc_auth_metadata_processor *p); -grpc_auth_metadata_processor *grpc_auth_metadata_processor_from_arg( - const grpc_arg *arg); -grpc_auth_metadata_processor *grpc_find_auth_metadata_processor_in_args( +grpc_arg grpc_auth_context_to_arg(grpc_auth_context *c); +grpc_auth_context *grpc_auth_context_from_arg(const grpc_arg *arg); +grpc_auth_context *grpc_find_auth_context_in_args( const grpc_channel_args *args); #endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */ diff --git a/src/core/security/server_auth_filter.c b/src/core/security/server_auth_filter.c index b767f85498..2e18369fe8 100644 --- a/src/core/security/server_auth_filter.c +++ b/src/core/security/server_auth_filter.c @@ -34,7 +34,7 @@ #include <string.h> #include "src/core/security/auth_filters.h" -#include "src/core/security/security_connector.h" +#include "src/core/security/credentials.h" #include "src/core/security/security_context.h" #include <grpc/support/alloc.h> @@ -44,11 +44,11 @@ typedef struct call_data { gpr_uint8 got_client_metadata; grpc_stream_op_buffer *recv_ops; /* Closure to call when finished with the auth_on_recv hook. */ - grpc_iomgr_closure *on_done_recv; + grpc_closure *on_done_recv; /* Receive closures are chained: we inject this closure as the on_done_recv up-call on transport_op, and remember to call our on_done_recv member after handling it. */ - grpc_iomgr_closure auth_on_recv; + grpc_closure auth_on_recv; grpc_transport_stream_op transport_op; grpc_metadata_array md; const grpc_metadata *consumed_md; @@ -58,8 +58,8 @@ typedef struct call_data { } call_data; typedef struct channel_data { - grpc_security_connector *security_connector; - grpc_auth_metadata_processor processor; + grpc_auth_context *auth_context; + grpc_server_credentials *creds; grpc_mdctx *mdctx; } channel_data; @@ -109,12 +109,14 @@ static grpc_mdelem *remove_consumed_md(void *user_data, grpc_mdelem *md) { return md; } +/* called from application code */ static void on_md_processing_done( void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md, const grpc_metadata *response_md, size_t num_response_md, grpc_status_code status, const char *error_details) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; /* TODO(jboeuf): Implement support for response_md. */ if (response_md != NULL && num_response_md > 0) { @@ -128,21 +130,25 @@ static void on_md_processing_done( calld->num_consumed_md = num_consumed_md; grpc_metadata_batch_filter(&calld->md_op->data.metadata, remove_consumed_md, elem); - calld->on_done_recv->cb(calld->on_done_recv->cb_arg, 1); + grpc_metadata_array_destroy(&calld->md); + calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 1); } else { gpr_slice message; + grpc_metadata_array_destroy(&calld->md); error_details = error_details != NULL - ? error_details - : "Authentication metadata processing failed."; + ? error_details + : "Authentication metadata processing failed."; message = gpr_slice_from_copied_string(error_details); grpc_sopb_reset(calld->recv_ops); grpc_transport_stream_op_add_close(&calld->transport_op, status, &message); - grpc_call_next_op(elem, &calld->transport_op); + grpc_call_next_op(&exec_ctx, elem, &calld->transport_op); } - grpc_metadata_array_destroy(&calld->md); + + grpc_exec_ctx_finish(&exec_ctx); } -static void auth_on_recv(void *user_data, int success) { +static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, + int success) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; @@ -154,16 +160,16 @@ static void auth_on_recv(void *user_data, int success) { grpc_stream_op *op = &ops[i]; if (op->type != GRPC_OP_METADATA || calld->got_client_metadata) continue; calld->got_client_metadata = 1; - if (chand->processor.process == NULL) continue; + if (chand->creds->processor.process == NULL) continue; calld->md_op = op; calld->md = metadata_batch_to_md_array(&op->data.metadata); - chand->processor.process(chand->processor.state, calld->auth_context, - calld->md.metadata, calld->md.count, - on_md_processing_done, elem); + chand->creds->processor.process( + chand->creds->processor.state, calld->auth_context, + calld->md.metadata, calld->md.count, on_md_processing_done, elem); return; } } - calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); + calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success); } static void set_recv_ops_md_callbacks(grpc_call_element *elem, @@ -184,14 +190,15 @@ static void set_recv_ops_md_callbacks(grpc_call_element *elem, - a network event (or similar) from below, to receive something op contains type and call direction information, in addition to the data that is being sent or received. */ -static void auth_start_transport_op(grpc_call_element *elem, +static void auth_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { set_recv_ops_md_callbacks(elem, op); - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); } /* Constructor for call_data */ -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { /* grab pointers to our data from the call element */ @@ -201,7 +208,7 @@ static void init_call_elem(grpc_call_element *elem, /* initialize members */ memset(calld, 0, sizeof(*calld)); - grpc_iomgr_closure_init(&calld->auth_on_recv, auth_on_recv, elem); + grpc_closure_init(&calld->auth_on_recv, auth_on_recv, elem); GPR_ASSERT(initial_op && initial_op->context != NULL && initial_op->context[GRPC_CONTEXT_SECURITY].value == NULL); @@ -214,7 +221,7 @@ static void init_call_elem(grpc_call_element *elem, } server_ctx = grpc_server_security_context_create(); server_ctx->auth_context = - grpc_auth_context_create(chand->security_connector->auth_context); + grpc_auth_context_create(chand->auth_context); server_ctx->auth_context->pollset = initial_op->bind_pollset; initial_op->context[GRPC_CONTEXT_SECURITY].value = server_ctx; initial_op->context[GRPC_CONTEXT_SECURITY].destroy = @@ -226,15 +233,16 @@ static void init_call_elem(grpc_call_element *elem, } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) {} +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) {} /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { - grpc_security_connector *sc = grpc_find_security_connector_in_args(args); - grpc_auth_metadata_processor *processor = - grpc_find_auth_metadata_processor_in_args(args); + grpc_auth_context *auth_context = grpc_find_auth_context_in_args(args); + grpc_server_credentials *creds = grpc_find_server_credentials_in_args(args); /* grab pointers to our data from the channel element */ channel_data *chand = elem->channel_data; @@ -243,28 +251,27 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, path */ GPR_ASSERT(!is_first); GPR_ASSERT(!is_last); - GPR_ASSERT(sc != NULL); - GPR_ASSERT(processor != NULL); + GPR_ASSERT(auth_context != NULL); + GPR_ASSERT(creds != NULL); /* initialize members */ - GPR_ASSERT(!sc->is_client_side); - chand->security_connector = - GRPC_SECURITY_CONNECTOR_REF(sc, "server_auth_filter"); + chand->auth_context = + GRPC_AUTH_CONTEXT_REF(auth_context, "server_auth_filter"); + chand->creds = grpc_server_credentials_ref(creds); chand->mdctx = mdctx; - chand->processor = *processor; } /* Destructor for channel data */ -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { /* grab pointers to our data from the channel element */ channel_data *chand = elem->channel_data; - GRPC_SECURITY_CONNECTOR_UNREF(chand->security_connector, - "server_auth_filter"); + GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "server_auth_filter"); + grpc_server_credentials_unref(chand->creds); } const grpc_channel_filter grpc_server_auth_filter = { - auth_start_transport_op, grpc_channel_next_op, - sizeof(call_data), init_call_elem, - destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, - grpc_call_next_get_peer, "server-auth"}; + auth_start_transport_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer, + "server-auth"}; diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c index 4749f5f516..82c639e830 100644 --- a/src/core/security/server_secure_chttp2.c +++ b/src/core/security/server_secure_chttp2.c @@ -44,7 +44,7 @@ #include "src/core/security/credentials.h" #include "src/core/security/security_connector.h" #include "src/core/security/security_context.h" -#include "src/core/security/secure_transport_setup.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/server.h" #include "src/core/transport/chttp2_transport.h" #include <grpc/support/alloc.h> @@ -66,6 +66,8 @@ typedef struct grpc_server_secure_state { int is_shutdown; gpr_mu mu; gpr_refcount refcount; + grpc_closure destroy_closure; + grpc_closure *destroy_callback; } grpc_server_secure_state; static void state_ref(grpc_server_secure_state *state) { @@ -84,20 +86,20 @@ static void state_unref(grpc_server_secure_state *state) { } } -static void setup_transport(void *statep, grpc_transport *transport, - grpc_mdctx *mdctx) { +static void setup_transport(grpc_exec_ctx *exec_ctx, void *statep, + grpc_transport *transport, grpc_mdctx *mdctx) { static grpc_channel_filter const *extra_filters[] = { &grpc_server_auth_filter, &grpc_http_server_filter}; grpc_server_secure_state *state = statep; grpc_channel_args *args_copy; grpc_arg args_to_add[2]; - args_to_add[0] = grpc_security_connector_to_arg(state->sc); + args_to_add[0] = grpc_server_credentials_to_arg(state->creds); args_to_add[1] = - grpc_auth_metadata_processor_to_arg(&state->creds->processor); + grpc_auth_context_to_arg(state->sc->auth_context); args_copy = grpc_channel_args_copy_and_add( grpc_server_get_channel_args(state->server), args_to_add, GPR_ARRAY_SIZE(args_to_add)); - grpc_server_setup_transport(state->server, transport, extra_filters, + grpc_server_setup_transport(exec_ctx, state->server, transport, extra_filters, GPR_ARRAY_SIZE(extra_filters), mdctx, args_copy); grpc_channel_args_destroy(args_copy); } @@ -123,10 +125,10 @@ static int remove_tcp_from_list_locked(grpc_server_secure_state *state, return -1; } -static void on_secure_transport_setup_done(void *statep, - grpc_security_status status, - grpc_endpoint *wrapped_endpoint, - grpc_endpoint *secure_endpoint) { +static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *statep, + grpc_security_status status, + grpc_endpoint *wrapped_endpoint, + grpc_endpoint *secure_endpoint) { grpc_server_secure_state *state = statep; grpc_transport *transport; grpc_mdctx *mdctx; @@ -136,14 +138,14 @@ static void on_secure_transport_setup_done(void *statep, if (!state->is_shutdown) { mdctx = grpc_mdctx_create(); transport = grpc_create_chttp2_transport( - grpc_server_get_channel_args(state->server), secure_endpoint, mdctx, - 0); - setup_transport(state, transport, mdctx); - grpc_chttp2_transport_start_reading(transport, NULL, 0); + exec_ctx, grpc_server_get_channel_args(state->server), + secure_endpoint, mdctx, 0); + setup_transport(exec_ctx, state, transport, mdctx); + grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0); } else { /* We need to consume this here, because the server may already have gone * away. */ - grpc_endpoint_destroy(secure_endpoint); + grpc_endpoint_destroy(exec_ctx, secure_endpoint); } gpr_mu_unlock(&state->mu); } else { @@ -155,7 +157,8 @@ static void on_secure_transport_setup_done(void *statep, state_unref(state); } -static void on_accept(void *statep, grpc_endpoint *tcp) { +static void on_accept(grpc_exec_ctx *exec_ctx, void *statep, + grpc_endpoint *tcp) { grpc_server_secure_state *state = statep; tcp_endpoint_list *node; state_ref(state); @@ -165,23 +168,26 @@ static void on_accept(void *statep, grpc_endpoint *tcp) { node->next = state->handshaking_tcp_endpoints; state->handshaking_tcp_endpoints = node; gpr_mu_unlock(&state->mu); - grpc_setup_secure_transport(state->sc, tcp, on_secure_transport_setup_done, - state); + grpc_security_connector_do_handshake(exec_ctx, state->sc, tcp, + on_secure_handshake_done, state); } /* Server callback: start listening on our ports */ -static void start(grpc_server *server, void *statep, grpc_pollset **pollsets, - size_t pollset_count) { +static void start(grpc_exec_ctx *exec_ctx, grpc_server *server, void *statep, + grpc_pollset **pollsets, size_t pollset_count) { grpc_server_secure_state *state = statep; - grpc_tcp_server_start(state->tcp, pollsets, pollset_count, on_accept, state); + grpc_tcp_server_start(exec_ctx, state->tcp, pollsets, pollset_count, + on_accept, state); } -static void destroy_done(void *statep) { +static void destroy_done(grpc_exec_ctx *exec_ctx, void *statep, int success) { grpc_server_secure_state *state = statep; - grpc_server_listener_destroy_done(state->server); + state->destroy_callback->cb(exec_ctx, state->destroy_callback->cb_arg, + success); gpr_mu_lock(&state->mu); while (state->handshaking_tcp_endpoints != NULL) { - grpc_endpoint_shutdown(state->handshaking_tcp_endpoints->tcp_endpoint); + grpc_endpoint_shutdown(exec_ctx, + state->handshaking_tcp_endpoints->tcp_endpoint); remove_tcp_from_list_locked(state, state->handshaking_tcp_endpoints->tcp_endpoint); } @@ -191,14 +197,17 @@ static void destroy_done(void *statep) { /* Server callback: destroy the tcp listener (so we don't generate further callbacks) */ -static void destroy(grpc_server *server, void *statep) { +static void destroy(grpc_exec_ctx *exec_ctx, grpc_server *server, void *statep, + grpc_closure *callback) { grpc_server_secure_state *state = statep; grpc_tcp_server *tcp; gpr_mu_lock(&state->mu); state->is_shutdown = 1; + state->destroy_callback = callback; tcp = state->tcp; gpr_mu_unlock(&state->mu); - grpc_tcp_server_destroy(tcp, destroy_done, state); + grpc_closure_init(&state->destroy_closure, destroy_done, state); + grpc_tcp_server_destroy(exec_ctx, tcp, &state->destroy_closure); } int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, @@ -212,6 +221,12 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, int port_temp; grpc_security_status status = GRPC_SECURITY_ERROR; grpc_security_connector *sc = NULL; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE( + "grpc_server_add_secure_http2_port(" + "server=%p, addr=%s, creds=%p)", + 3, (server, addr, creds)); /* create security context */ if (creds == NULL) goto error; @@ -272,8 +287,9 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, gpr_ref_init(&state->refcount, 1); /* Register with the server only upon success */ - grpc_server_add_listener(server, state, start, destroy); + grpc_server_add_listener(&exec_ctx, server, state, start, destroy); + grpc_exec_ctx_finish(&exec_ctx); return port_num; /* Error path: cleanup and return */ @@ -285,10 +301,11 @@ error: grpc_resolved_addresses_destroy(resolved); } if (tcp) { - grpc_tcp_server_destroy(tcp, NULL, NULL); + grpc_tcp_server_destroy(&exec_ctx, tcp, NULL); } if (state) { gpr_free(state); } + grpc_exec_ctx_finish(&exec_ctx); return 0; } diff --git a/src/core/statistics/census_interface.h b/src/core/statistics/census_interface.h index ac1ff24866..e870357276 100644 --- a/src/core/statistics/census_interface.h +++ b/src/core/statistics/census_interface.h @@ -61,11 +61,11 @@ void census_shutdown(void); TODO(hongyu): Figure out valid characters set for service name and command name and document requirements here.*/ -int census_add_method_tag(census_op_id op_id, const char* method_name); +int census_add_method_tag(census_op_id op_id, const char *method_name); /* Annotates tracing information to a specific op_id. Up to CENSUS_MAX_ANNOTATION_LENGTH bytes are recorded. */ -void census_tracing_print(census_op_id op_id, const char* annotation); +void census_tracing_print(census_op_id op_id, const char *annotation); /* Starts tracing for an RPC. Returns a locally unique census_op_id */ census_op_id census_tracing_start_op(void); diff --git a/src/core/statistics/census_log.c b/src/core/statistics/census_log.c index ec56ce38df..88e338038d 100644 --- a/src/core/statistics/census_log.c +++ b/src/core/statistics/census_log.c @@ -102,14 +102,14 @@ /* End of platform specific code */ typedef struct census_log_block_list_struct { - struct census_log_block_list_struct* next; - struct census_log_block_list_struct* prev; - struct census_log_block* block; + struct census_log_block_list_struct *next; + struct census_log_block_list_struct *prev; + struct census_log_block *block; } cl_block_list_struct; typedef struct census_log_block { /* Pointer to underlying buffer */ - char* buffer; + char *buffer; gpr_atm writer_lock; gpr_atm reader_lock; /* Keeps completely written bytes. Declared atomic because accessed @@ -176,8 +176,8 @@ struct census_log { unsigned num_cores; /* number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log */ gpr_int32 num_blocks; - cl_block* blocks; /* Block metadata. */ - cl_core_local_block* core_local_blocks; /* Keeps core to block mappings. */ + cl_block *blocks; /* Block metadata. */ + cl_core_local_block *core_local_blocks; /* Keeps core to block mappings. */ gpr_mu lock; int initialized; /* has log been initialized? */ /* Keeps the state of the reader iterator. A value of 0 indicates that @@ -186,10 +186,10 @@ struct census_log { gpr_uint32 read_iterator_state; /* Points to the block being read. If non-NULL, the block is locked for reading (block_being_read_->reader_lock is held). */ - cl_block* block_being_read; + cl_block *block_being_read; /* A non-zero value indicates that log is full. */ gpr_atm is_full; - char* buffer; + char *buffer; cl_block_list free_block_list; cl_block_list dirty_block_list; gpr_atm out_of_space_count; @@ -201,44 +201,44 @@ static struct census_log g_log; /* Functions that operate on an atomic memory location used as a lock */ /* Returns non-zero if lock is acquired */ -static int cl_try_lock(gpr_atm* lock) { return gpr_atm_acq_cas(lock, 0, 1); } +static int cl_try_lock(gpr_atm *lock) { return gpr_atm_acq_cas(lock, 0, 1); } -static void cl_unlock(gpr_atm* lock) { gpr_atm_rel_store(lock, 0); } +static void cl_unlock(gpr_atm *lock) { gpr_atm_rel_store(lock, 0); } /* Functions that operate on cl_core_local_block's */ -static void cl_core_local_block_set_block(cl_core_local_block* clb, - cl_block* block) { +static void cl_core_local_block_set_block(cl_core_local_block *clb, + cl_block *block) { gpr_atm_rel_store(&clb->block, (gpr_atm)block); } -static cl_block* cl_core_local_block_get_block(cl_core_local_block* clb) { - return (cl_block*)gpr_atm_acq_load(&clb->block); +static cl_block *cl_core_local_block_get_block(cl_core_local_block *clb) { + return (cl_block *)gpr_atm_acq_load(&clb->block); } /* Functions that operate on cl_block_list_struct's */ -static void cl_block_list_struct_initialize(cl_block_list_struct* bls, - cl_block* block) { +static void cl_block_list_struct_initialize(cl_block_list_struct *bls, + cl_block *block) { bls->next = bls->prev = bls; bls->block = block; } /* Functions that operate on cl_block_list's */ -static void cl_block_list_initialize(cl_block_list* list) { +static void cl_block_list_initialize(cl_block_list *list) { list->count = 0; cl_block_list_struct_initialize(&list->ht, NULL); } /* Returns head of *this, or NULL if empty. */ -static cl_block* cl_block_list_head(cl_block_list* list) { +static cl_block *cl_block_list_head(cl_block_list *list) { return list->ht.next->block; } /* Insert element *e after *pos. */ -static void cl_block_list_insert(cl_block_list* list, cl_block_list_struct* pos, - cl_block_list_struct* e) { +static void cl_block_list_insert(cl_block_list *list, cl_block_list_struct *pos, + cl_block_list_struct *e) { list->count++; e->next = pos->next; e->prev = pos; @@ -247,17 +247,17 @@ static void cl_block_list_insert(cl_block_list* list, cl_block_list_struct* pos, } /* Insert block at the head of the list */ -static void cl_block_list_insert_at_head(cl_block_list* list, cl_block* block) { +static void cl_block_list_insert_at_head(cl_block_list *list, cl_block *block) { cl_block_list_insert(list, &list->ht, &block->link); } /* Insert block at the tail of the list */ -static void cl_block_list_insert_at_tail(cl_block_list* list, cl_block* block) { +static void cl_block_list_insert_at_tail(cl_block_list *list, cl_block *block) { cl_block_list_insert(list, list->ht.prev, &block->link); } /* Removes block *b. Requires *b be in the list. */ -static void cl_block_list_remove(cl_block_list* list, cl_block* b) { +static void cl_block_list_remove(cl_block_list *list, cl_block *b) { list->count--; b->link.next->prev = b->link.prev; b->link.prev->next = b->link.next; @@ -265,7 +265,7 @@ static void cl_block_list_remove(cl_block_list* list, cl_block* b) { /* Functions that operate on cl_block's */ -static void cl_block_initialize(cl_block* block, char* buffer) { +static void cl_block_initialize(cl_block *block, char *buffer) { block->buffer = buffer; gpr_atm_rel_store(&block->writer_lock, 0); gpr_atm_rel_store(&block->reader_lock, 0); @@ -275,12 +275,12 @@ static void cl_block_initialize(cl_block* block, char* buffer) { } /* Guards against exposing partially written buffer to the reader. */ -static void cl_block_set_bytes_committed(cl_block* block, +static void cl_block_set_bytes_committed(cl_block *block, gpr_int32 bytes_committed) { gpr_atm_rel_store(&block->bytes_committed, bytes_committed); } -static gpr_int32 cl_block_get_bytes_committed(cl_block* block) { +static gpr_int32 cl_block_get_bytes_committed(cl_block *block) { return gpr_atm_acq_load(&block->bytes_committed); } @@ -291,7 +291,7 @@ static gpr_int32 cl_block_get_bytes_committed(cl_block* block) { On success, clears the block state and returns with writer_lock_ and reader_lock_ held. These locks are released by a subsequent cl_block_access_enable() call. */ -static int cl_block_try_disable_access(cl_block* block, int discard_data) { +static int cl_block_try_disable_access(cl_block *block, int discard_data) { if (!cl_try_lock(&block->writer_lock)) { return 0; } @@ -310,13 +310,13 @@ static int cl_block_try_disable_access(cl_block* block, int discard_data) { return 1; } -static void cl_block_enable_access(cl_block* block) { +static void cl_block_enable_access(cl_block *block) { cl_unlock(&block->reader_lock); cl_unlock(&block->writer_lock); } /* Returns with writer_lock held. */ -static void* cl_block_start_write(cl_block* block, size_t size) { +static void *cl_block_start_write(cl_block *block, size_t size) { gpr_int32 bytes_committed; if (!cl_try_lock(&block->writer_lock)) { return NULL; @@ -332,7 +332,7 @@ static void* cl_block_start_write(cl_block* block, size_t size) { /* Releases writer_lock and increments committed bytes by 'bytes_written'. 'bytes_written' must be <= 'size' specified in the corresponding StartWrite() call. This function is thread-safe. */ -static void cl_block_end_write(cl_block* block, size_t bytes_written) { +static void cl_block_end_write(cl_block *block, size_t bytes_written) { cl_block_set_bytes_committed( block, cl_block_get_bytes_committed(block) + bytes_written); cl_unlock(&block->writer_lock); @@ -343,8 +343,8 @@ static void cl_block_end_write(cl_block* block, size_t bytes_written) { released by a subsequent cl_block_end_read() call. Returns NULL if: - read in progress - no data available */ -static void* cl_block_start_read(cl_block* block, size_t* bytes_available) { - void* record; +static void *cl_block_start_read(cl_block *block, size_t *bytes_available) { + void *record; if (!cl_try_lock(&block->reader_lock)) { return NULL; } @@ -360,7 +360,7 @@ static void* cl_block_start_read(cl_block* block, size_t* bytes_available) { return record; } -static void cl_block_end_read(cl_block* block) { +static void cl_block_end_read(cl_block *block) { cl_unlock(&block->reader_lock); } @@ -368,8 +368,8 @@ static void cl_block_end_read(cl_block* block) { /* Allocates a new free block (or recycles an available dirty block if log is configured to discard old records). Returns NULL if out-of-space. */ -static cl_block* cl_allocate_block(void) { - cl_block* block = cl_block_list_head(&g_log.free_block_list); +static cl_block *cl_allocate_block(void) { + cl_block *block = cl_block_list_head(&g_log.free_block_list); if (block != NULL) { cl_block_list_remove(&g_log.free_block_list, block); return block; @@ -396,10 +396,10 @@ static cl_block* cl_allocate_block(void) { - 'core_id' => 'old_block' mapping changed (another thread allocated a block before lock was acquired). */ static int cl_allocate_core_local_block(gpr_int32 core_id, - cl_block* old_block) { + cl_block *old_block) { /* Now that we have the lock, check if core-local mapping has changed. */ - cl_core_local_block* core_local_block = &g_log.core_local_blocks[core_id]; - cl_block* block = cl_core_local_block_get_block(core_local_block); + cl_core_local_block *core_local_block = &g_log.core_local_blocks[core_id]; + cl_block *block = cl_core_local_block_get_block(core_local_block); if ((block != NULL) && (block != old_block)) { return 1; } @@ -417,16 +417,16 @@ static int cl_allocate_core_local_block(gpr_int32 core_id, return 1; } -static cl_block* cl_get_block(void* record) { - gpr_uintptr p = (gpr_uintptr)((char*)record - g_log.buffer); +static cl_block *cl_get_block(void *record) { + gpr_uintptr p = (gpr_uintptr)((char *)record - g_log.buffer); gpr_uintptr index = p >> CENSUS_LOG_2_MAX_RECORD_SIZE; return &g_log.blocks[index]; } /* Gets the next block to read and tries to free 'prev' block (if not NULL). Returns NULL if reached the end. */ -static cl_block* cl_next_block_to_read(cl_block* prev) { - cl_block* block = NULL; +static cl_block *cl_next_block_to_read(cl_block *prev) { + cl_block *block = NULL; if (g_log.read_iterator_state == g_log.num_cores) { /* We are traversing dirty list; find the next dirty block. */ if (prev != NULL) { @@ -474,11 +474,11 @@ void census_log_initialize(size_t size_in_mb, int discard_old_records) { g_log.read_iterator_state = 0; g_log.block_being_read = NULL; gpr_atm_rel_store(&g_log.is_full, 0); - g_log.core_local_blocks = (cl_core_local_block*)gpr_malloc_aligned( + g_log.core_local_blocks = (cl_core_local_block *)gpr_malloc_aligned( g_log.num_cores * sizeof(cl_core_local_block), GPR_CACHELINE_SIZE_LOG); memset(g_log.core_local_blocks, 0, g_log.num_cores * sizeof(cl_core_local_block)); - g_log.blocks = (cl_block*)gpr_malloc_aligned( + g_log.blocks = (cl_block *)gpr_malloc_aligned( g_log.num_blocks * sizeof(cl_block), GPR_CACHELINE_SIZE_LOG); memset(g_log.blocks, 0, g_log.num_blocks * sizeof(cl_block)); g_log.buffer = gpr_malloc(g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); @@ -486,7 +486,7 @@ void census_log_initialize(size_t size_in_mb, int discard_old_records) { cl_block_list_initialize(&g_log.free_block_list); cl_block_list_initialize(&g_log.dirty_block_list); for (ix = 0; ix < g_log.num_blocks; ++ix) { - cl_block* block = g_log.blocks + ix; + cl_block *block = g_log.blocks + ix; cl_block_initialize(block, g_log.buffer + (CENSUS_LOG_MAX_RECORD_SIZE * ix)); cl_block_try_disable_access(block, 1 /* discard data */); @@ -508,7 +508,7 @@ void census_log_shutdown(void) { g_log.initialized = 0; } -void* census_log_start_write(size_t size) { +void *census_log_start_write(size_t size) { /* Used to bound number of times block allocation is attempted. */ gpr_int32 attempts_remaining = g_log.num_blocks; /* TODO(aveitch): move this inside the do loop when current_cpu is fixed */ @@ -519,8 +519,8 @@ void* census_log_start_write(size_t size) { } do { int allocated; - void* record = NULL; - cl_block* block = + void *record = NULL; + cl_block *block = cl_core_local_block_get_block(&g_log.core_local_blocks[core_id]); if (block && (record = cl_block_start_write(block, size))) { return record; @@ -546,7 +546,7 @@ void* census_log_start_write(size_t size) { return NULL; } -void census_log_end_write(void* record, size_t bytes_written) { +void census_log_end_write(void *record, size_t bytes_written) { GPR_ASSERT(g_log.initialized); cl_block_end_write(cl_get_block(record), bytes_written); } @@ -563,7 +563,7 @@ void census_log_init_reader(void) { gpr_mu_unlock(&g_log.lock); } -const void* census_log_read_next(size_t* bytes_available) { +const void *census_log_read_next(size_t *bytes_available) { GPR_ASSERT(g_log.initialized); gpr_mu_lock(&g_log.lock); if (g_log.block_being_read != NULL) { @@ -572,7 +572,7 @@ const void* census_log_read_next(size_t* bytes_available) { do { g_log.block_being_read = cl_next_block_to_read(g_log.block_being_read); if (g_log.block_being_read != NULL) { - void* record = + void *record = cl_block_start_read(g_log.block_being_read, bytes_available); if (record != NULL) { gpr_mu_unlock(&g_log.lock); diff --git a/src/core/statistics/census_log.h b/src/core/statistics/census_log.h index 60b6d597df..356437c346 100644 --- a/src/core/statistics/census_log.h +++ b/src/core/statistics/census_log.h @@ -62,9 +62,9 @@ void census_log_shutdown(void); - log is configured to keep old records OR - all blocks are pinned by incomplete records. */ -void* census_log_start_write(size_t size); +void *census_log_start_write(size_t size); -void census_log_end_write(void* record, size_t bytes_written); +void census_log_end_write(void *record, size_t bytes_written); /* census_log_read_next() iterates over blocks with data and for each block returns a pointer to the first unread byte. The number of bytes that can be @@ -75,7 +75,7 @@ void census_log_end_write(void* record, size_t bytes_written); current iteration. */ void census_log_init_reader(void); -const void* census_log_read_next(size_t* bytes_available); +const void *census_log_read_next(size_t *bytes_available); /* Returns estimated remaining space across all blocks, in bytes. If log is configured to discard old records, returns total log space. Otherwise, diff --git a/src/core/statistics/census_rpc_stats.c b/src/core/statistics/census_rpc_stats.c index b836987cf0..d6638ed641 100644 --- a/src/core/statistics/census_rpc_stats.c +++ b/src/core/statistics/census_rpc_stats.c @@ -56,8 +56,8 @@ typedef census_per_method_rpc_stats per_method_stats; static gpr_once g_stats_store_mu_init = GPR_ONCE_INIT; /* Guards two stats stores. */ static gpr_mu g_mu; -static census_ht* g_client_stats_store = NULL; -static census_ht* g_server_stats_store = NULL; +static census_ht *g_client_stats_store = NULL; +static census_ht *g_server_stats_store = NULL; static void init_mutex(void) { gpr_mu_init(&g_mu); } @@ -65,37 +65,37 @@ static void init_mutex_once(void) { gpr_once_init(&g_stats_store_mu_init, init_mutex); } -static int cmp_str_keys(const void* k1, const void* k2) { - return strcmp((const char*)k1, (const char*)k2); +static int cmp_str_keys(const void *k1, const void *k2) { + return strcmp((const char *)k1, (const char *)k2); } /* TODO(hongyu): replace it with cityhash64 */ -static gpr_uint64 simple_hash(const void* k) { +static gpr_uint64 simple_hash(const void *k) { size_t len = strlen(k); - gpr_uint64 higher = gpr_murmur_hash3((const char*)k, len / 2, 0); + gpr_uint64 higher = gpr_murmur_hash3((const char *)k, len / 2, 0); return higher << 32 | - gpr_murmur_hash3((const char*)k + len / 2, len - len / 2, 0); + gpr_murmur_hash3((const char *)k + len / 2, len - len / 2, 0); } -static void delete_stats(void* stats) { - census_window_stats_destroy((struct census_window_stats*)stats); +static void delete_stats(void *stats) { + census_window_stats_destroy((struct census_window_stats *)stats); } -static void delete_key(void* key) { gpr_free(key); } +static void delete_key(void *key) { gpr_free(key); } static const census_ht_option ht_opt = { CENSUS_HT_POINTER /* key type */, 1999 /* n_of_buckets */, - simple_hash /* hash function */, cmp_str_keys /* key comparator */, - delete_stats /* data deleter */, delete_key /* key deleter */ + simple_hash /* hash function */, cmp_str_keys /* key comparator */, + delete_stats /* data deleter */, delete_key /* key deleter */ }; -static void init_rpc_stats(void* stats) { +static void init_rpc_stats(void *stats) { memset(stats, 0, sizeof(census_rpc_stats)); } -static void stat_add_proportion(double p, void* base, const void* addme) { - census_rpc_stats* b = (census_rpc_stats*)base; - census_rpc_stats* a = (census_rpc_stats*)addme; +static void stat_add_proportion(double p, void *base, const void *addme) { + census_rpc_stats *b = (census_rpc_stats *)base; + census_rpc_stats *a = (census_rpc_stats *)addme; b->cnt += p * a->cnt; b->rpc_error_cnt += p * a->rpc_error_cnt; b->app_error_cnt += p * a->app_error_cnt; @@ -106,7 +106,7 @@ static void stat_add_proportion(double p, void* base, const void* addme) { b->wire_response_bytes += p * a->wire_response_bytes; } -static void stat_add(void* base, const void* addme) { +static void stat_add(void *base, const void *addme) { stat_add_proportion(1.0, base, addme); } @@ -116,18 +116,18 @@ static gpr_timespec min_hour_total_intervals[3] = { static const census_window_stats_stat_info window_stats_settings = { sizeof(census_rpc_stats), init_rpc_stats, stat_add, stat_add_proportion}; -census_rpc_stats* census_rpc_stats_create_empty(void) { - census_rpc_stats* ret = - (census_rpc_stats*)gpr_malloc(sizeof(census_rpc_stats)); +census_rpc_stats *census_rpc_stats_create_empty(void) { + census_rpc_stats *ret = + (census_rpc_stats *)gpr_malloc(sizeof(census_rpc_stats)); memset(ret, 0, sizeof(census_rpc_stats)); return ret; } -void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats* data) { +void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats *data) { int i = 0; for (i = 0; i < data->num_entries; i++) { if (data->stats[i].method != NULL) { - gpr_free((void*)data->stats[i].method); + gpr_free((void *)data->stats[i].method); } } if (data->stats != NULL) { @@ -137,25 +137,25 @@ void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats* data) { data->stats = NULL; } -static void record_stats(census_ht* store, census_op_id op_id, - const census_rpc_stats* stats) { +static void record_stats(census_ht *store, census_op_id op_id, + const census_rpc_stats *stats) { gpr_mu_lock(&g_mu); if (store != NULL) { - census_trace_obj* trace = NULL; + census_trace_obj *trace = NULL; census_internal_lock_trace_store(); trace = census_get_trace_obj_locked(op_id); if (trace != NULL) { - const char* method_name = census_get_trace_method_name(trace); - struct census_window_stats* window_stats = NULL; + const char *method_name = census_get_trace_method_name(trace); + struct census_window_stats *window_stats = NULL; census_ht_key key; - key.ptr = (void*)method_name; + key.ptr = (void *)method_name; window_stats = census_ht_find(store, key); census_internal_unlock_trace_store(); if (window_stats == NULL) { window_stats = census_window_stats_create(3, min_hour_total_intervals, 30, &window_stats_settings); key.ptr = gpr_strdup(key.ptr); - census_ht_insert(store, key, (void*)window_stats); + census_ht_insert(store, key, (void *)window_stats); } census_window_stats_add(window_stats, gpr_now(GPR_CLOCK_REALTIME), stats); } else { @@ -166,17 +166,17 @@ static void record_stats(census_ht* store, census_op_id op_id, } void census_record_rpc_client_stats(census_op_id op_id, - const census_rpc_stats* stats) { + const census_rpc_stats *stats) { record_stats(g_client_stats_store, op_id, stats); } void census_record_rpc_server_stats(census_op_id op_id, - const census_rpc_stats* stats) { + const census_rpc_stats *stats) { record_stats(g_server_stats_store, op_id, stats); } /* Get stats from input stats store */ -static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) { +static void get_stats(census_ht *store, census_aggregated_rpc_stats *data) { GPR_ASSERT(data != NULL); if (data->num_entries != 0) { census_aggregated_rpc_stats_set_empty(data); @@ -186,23 +186,24 @@ static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) { size_t n; unsigned i, j; gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - census_ht_kv* kv = census_ht_get_all_elements(store, &n); + census_ht_kv *kv = census_ht_get_all_elements(store, &n); if (kv != NULL) { data->num_entries = n; - data->stats = (per_method_stats*)gpr_malloc(sizeof(per_method_stats) * n); + data->stats = + (per_method_stats *)gpr_malloc(sizeof(per_method_stats) * n); for (i = 0; i < n; i++) { census_window_stats_sums sums[NUM_INTERVALS]; for (j = 0; j < NUM_INTERVALS; j++) { - sums[j].statistic = (void*)census_rpc_stats_create_empty(); + sums[j].statistic = (void *)census_rpc_stats_create_empty(); } data->stats[i].method = gpr_strdup(kv[i].k.ptr); census_window_stats_get_sums(kv[i].v, now, sums); data->stats[i].minute_stats = - *(census_rpc_stats*)sums[MINUTE_INTERVAL].statistic; + *(census_rpc_stats *)sums[MINUTE_INTERVAL].statistic; data->stats[i].hour_stats = - *(census_rpc_stats*)sums[HOUR_INTERVAL].statistic; + *(census_rpc_stats *)sums[HOUR_INTERVAL].statistic; data->stats[i].total_stats = - *(census_rpc_stats*)sums[TOTAL_INTERVAL].statistic; + *(census_rpc_stats *)sums[TOTAL_INTERVAL].statistic; for (j = 0; j < NUM_INTERVALS; j++) { gpr_free(sums[j].statistic); } @@ -213,11 +214,11 @@ static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) { gpr_mu_unlock(&g_mu); } -void census_get_client_stats(census_aggregated_rpc_stats* data) { +void census_get_client_stats(census_aggregated_rpc_stats *data) { get_stats(g_client_stats_store, data); } -void census_get_server_stats(census_aggregated_rpc_stats* data) { +void census_get_server_stats(census_aggregated_rpc_stats *data) { get_stats(g_server_stats_store, data); } diff --git a/src/core/statistics/census_rpc_stats.h b/src/core/statistics/census_rpc_stats.h index aec31c1971..5edbe9f478 100644 --- a/src/core/statistics/census_rpc_stats.h +++ b/src/core/statistics/census_rpc_stats.h @@ -53,10 +53,10 @@ struct census_rpc_stats { }; /* Creates an empty rpc stats object on heap. */ -census_rpc_stats* census_rpc_stats_create_empty(void); +census_rpc_stats *census_rpc_stats_create_empty(void); typedef struct census_per_method_rpc_stats { - const char* method; + const char *method; census_rpc_stats minute_stats; /* cumulative stats in the past minute */ census_rpc_stats hour_stats; /* cumulative stats in the past hour */ census_rpc_stats total_stats; /* cumulative stats from last gc */ @@ -64,19 +64,19 @@ typedef struct census_per_method_rpc_stats { typedef struct census_aggregated_rpc_stats { int num_entries; - census_per_method_rpc_stats* stats; + census_per_method_rpc_stats *stats; } census_aggregated_rpc_stats; /* Initializes an aggregated rpc stats object to an empty state. */ -void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats* data); +void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats *data); /* Records client side stats of a rpc. */ void census_record_rpc_client_stats(census_op_id op_id, - const census_rpc_stats* stats); + const census_rpc_stats *stats); /* Records server side stats of a rpc. */ void census_record_rpc_server_stats(census_op_id op_id, - const census_rpc_stats* stats); + const census_rpc_stats *stats); /* The following two functions are intended for inprocess query of per-service per-method stats from grpc implementations. */ @@ -84,12 +84,12 @@ void census_record_rpc_server_stats(census_op_id op_id, /* Populates *data_map with server side aggregated per-service per-method stats. DO NOT CALL from outside of grpc code. */ -void census_get_server_stats(census_aggregated_rpc_stats* data_map); +void census_get_server_stats(census_aggregated_rpc_stats *data_map); /* Populates *data_map with client side aggregated per-service per-method stats. DO NOT CALL from outside of grpc code. */ -void census_get_client_stats(census_aggregated_rpc_stats* data_map); +void census_get_client_stats(census_aggregated_rpc_stats *data_map); void census_stats_store_init(void); void census_stats_store_shutdown(void); diff --git a/src/core/statistics/census_tracing.c b/src/core/statistics/census_tracing.c index f2a09dc06e..ecbe27e536 100644 --- a/src/core/statistics/census_tracing.c +++ b/src/core/statistics/census_tracing.c @@ -44,10 +44,10 @@ #include <grpc/support/port_platform.h> #include <grpc/support/sync.h> -void census_trace_obj_destroy(census_trace_obj* obj) { - census_trace_annotation* p = obj->annotations; +void census_trace_obj_destroy(census_trace_obj *obj) { + census_trace_annotation *p = obj->annotations; while (p != NULL) { - census_trace_annotation* next = p->next; + census_trace_annotation *next = p->next; gpr_free(p); p = next; } @@ -55,29 +55,26 @@ void census_trace_obj_destroy(census_trace_obj* obj) { gpr_free(obj); } -static void delete_trace_obj(void* obj) { - census_trace_obj_destroy((census_trace_obj*)obj); +static void delete_trace_obj(void *obj) { + census_trace_obj_destroy((census_trace_obj *)obj); } static const census_ht_option ht_opt = { - CENSUS_HT_UINT64 /* key type*/, - 571 /* n_of_buckets */, - NULL /* hash */, - NULL /* compare_keys */, - delete_trace_obj /* delete data */, + CENSUS_HT_UINT64 /* key type */, 571 /* n_of_buckets */, NULL /* hash */, + NULL /* compare_keys */, delete_trace_obj /* delete data */, NULL /* delete key */ }; static gpr_once g_init_mutex_once = GPR_ONCE_INIT; static gpr_mu g_mu; /* Guards following two static variables. */ -static census_ht* g_trace_store = NULL; +static census_ht *g_trace_store = NULL; static gpr_uint64 g_id = 0; -static census_ht_key op_id_as_key(census_op_id* id) { - return *(census_ht_key*)id; +static census_ht_key op_id_as_key(census_op_id *id) { + return *(census_ht_key *)id; } -static gpr_uint64 op_id_2_uint64(census_op_id* id) { +static gpr_uint64 op_id_2_uint64(census_op_id *id) { gpr_uint64 ret; memcpy(&ret, id, sizeof(census_op_id)); return ret; @@ -92,22 +89,22 @@ static void init_mutex_once(void) { census_op_id census_tracing_start_op(void) { gpr_mu_lock(&g_mu); { - census_trace_obj* ret = gpr_malloc(sizeof(census_trace_obj)); + census_trace_obj *ret = gpr_malloc(sizeof(census_trace_obj)); memset(ret, 0, sizeof(census_trace_obj)); g_id++; memcpy(&ret->id, &g_id, sizeof(census_op_id)); ret->rpc_stats.cnt = 1; ret->ts = gpr_now(GPR_CLOCK_REALTIME); - census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void*)ret); + census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void *)ret); gpr_log(GPR_DEBUG, "Start tracing for id %lu", g_id); gpr_mu_unlock(&g_mu); return ret->id; } } -int census_add_method_tag(census_op_id op_id, const char* method) { +int census_add_method_tag(census_op_id op_id, const char *method) { int ret = 0; - census_trace_obj* trace = NULL; + census_trace_obj *trace = NULL; gpr_mu_lock(&g_mu); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); if (trace == NULL) { @@ -119,16 +116,16 @@ int census_add_method_tag(census_op_id op_id, const char* method) { return ret; } -void census_tracing_print(census_op_id op_id, const char* anno_txt) { - census_trace_obj* trace = NULL; +void census_tracing_print(census_op_id op_id, const char *anno_txt) { + census_trace_obj *trace = NULL; gpr_mu_lock(&g_mu); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); if (trace != NULL) { - census_trace_annotation* anno = gpr_malloc(sizeof(census_trace_annotation)); + census_trace_annotation *anno = gpr_malloc(sizeof(census_trace_annotation)); anno->ts = gpr_now(GPR_CLOCK_REALTIME); { - char* d = anno->txt; - const char* s = anno_txt; + char *d = anno->txt; + const char *s = anno_txt; int n = 0; for (; n < CENSUS_MAX_ANNOTATION_LENGTH && *s != '\0'; ++n) { *d++ = *s++; @@ -142,7 +139,7 @@ void census_tracing_print(census_op_id op_id, const char* anno_txt) { } void census_tracing_end_op(census_op_id op_id) { - census_trace_obj* trace = NULL; + census_trace_obj *trace = NULL; gpr_mu_lock(&g_mu); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); if (trace != NULL) { @@ -183,22 +180,23 @@ void census_internal_lock_trace_store(void) { gpr_mu_lock(&g_mu); } void census_internal_unlock_trace_store(void) { gpr_mu_unlock(&g_mu); } -census_trace_obj* census_get_trace_obj_locked(census_op_id op_id) { +census_trace_obj *census_get_trace_obj_locked(census_op_id op_id) { if (g_trace_store == NULL) { gpr_log(GPR_ERROR, "Census trace store is not initialized."); return NULL; } - return (census_trace_obj*)census_ht_find(g_trace_store, op_id_as_key(&op_id)); + return (census_trace_obj *)census_ht_find(g_trace_store, + op_id_as_key(&op_id)); } -const char* census_get_trace_method_name(const census_trace_obj* trace) { +const char *census_get_trace_method_name(const census_trace_obj *trace) { return trace->method; } -static census_trace_annotation* dup_annotation_chain( - census_trace_annotation* from) { - census_trace_annotation* ret = NULL; - census_trace_annotation** to = &ret; +static census_trace_annotation *dup_annotation_chain( + census_trace_annotation *from) { + census_trace_annotation *ret = NULL; + census_trace_annotation **to = &ret; for (; from != NULL; from = from->next) { *to = gpr_malloc(sizeof(census_trace_annotation)); memcpy(*to, from, sizeof(census_trace_annotation)); @@ -207,8 +205,8 @@ static census_trace_annotation* dup_annotation_chain( return ret; } -static census_trace_obj* trace_obj_dup(census_trace_obj* from) { - census_trace_obj* to = NULL; +static census_trace_obj *trace_obj_dup(census_trace_obj *from) { + census_trace_obj *to = NULL; GPR_ASSERT(from != NULL); to = gpr_malloc(sizeof(census_trace_obj)); to->id = from->id; @@ -219,18 +217,18 @@ static census_trace_obj* trace_obj_dup(census_trace_obj* from) { return to; } -census_trace_obj** census_get_active_ops(int* num_active_ops) { - census_trace_obj** ret = NULL; +census_trace_obj **census_get_active_ops(int *num_active_ops) { + census_trace_obj **ret = NULL; gpr_mu_lock(&g_mu); if (g_trace_store != NULL) { size_t n = 0; - census_ht_kv* all_kvs = census_ht_get_all_elements(g_trace_store, &n); + census_ht_kv *all_kvs = census_ht_get_all_elements(g_trace_store, &n); *num_active_ops = (int)n; if (n != 0) { size_t i = 0; - ret = gpr_malloc(sizeof(census_trace_obj*) * n); + ret = gpr_malloc(sizeof(census_trace_obj *) * n); for (i = 0; i < n; i++) { - ret[i] = trace_obj_dup((census_trace_obj*)all_kvs[i].v); + ret[i] = trace_obj_dup((census_trace_obj *)all_kvs[i].v); } } gpr_free(all_kvs); diff --git a/src/core/statistics/census_tracing.h b/src/core/statistics/census_tracing.h index 08305c2469..bb3f2556d2 100644 --- a/src/core/statistics/census_tracing.h +++ b/src/core/statistics/census_tracing.h @@ -50,19 +50,19 @@ extern "C" { typedef struct census_trace_annotation { gpr_timespec ts; /* timestamp of the annotation */ char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */ - struct census_trace_annotation* next; + struct census_trace_annotation *next; } census_trace_annotation; typedef struct census_trace_obj { census_op_id id; gpr_timespec ts; census_rpc_stats rpc_stats; - char* method; - census_trace_annotation* annotations; + char *method; + census_trace_annotation *annotations; } census_trace_obj; /* Deletes trace object. */ -void census_trace_obj_destroy(census_trace_obj* obj); +void census_trace_obj_destroy(census_trace_obj *obj); /* Initializes trace store. This function is thread safe. */ void census_tracing_init(void); @@ -73,7 +73,7 @@ void census_tracing_shutdown(void); /* Gets trace obj corresponding to the input op_id. Returns NULL if trace store is not initialized or trace obj is not found. Requires trace store being locked before calling this function. */ -census_trace_obj* census_get_trace_obj_locked(census_op_id op_id); +census_trace_obj *census_get_trace_obj_locked(census_op_id op_id); /* The following two functions acquire and release the trace store global lock. They are for census internal use only. */ @@ -81,13 +81,13 @@ void census_internal_lock_trace_store(void); void census_internal_unlock_trace_store(void); /* Gets method name associated with the input trace object. */ -const char* census_get_trace_method_name(const census_trace_obj* trace); +const char *census_get_trace_method_name(const census_trace_obj *trace); /* Returns an array of pointers to trace objects of currently active operations and fills in number of active operations. Returns NULL if there are no active operations. Caller owns the returned objects. */ -census_trace_obj** census_get_active_ops(int* num_active_ops); +census_trace_obj **census_get_active_ops(int *num_active_ops); #ifdef __cplusplus } diff --git a/src/core/statistics/hash_table.c b/src/core/statistics/hash_table.c index 56bdcc2fff..39b760f0e0 100644 --- a/src/core/statistics/hash_table.c +++ b/src/core/statistics/hash_table.c @@ -45,14 +45,14 @@ /* A single hash table data entry */ typedef struct ht_entry { census_ht_key key; - void* data; - struct ht_entry* next; + void *data; + struct ht_entry *next; } ht_entry; /* hash table bucket */ typedef struct bucket { /* NULL if bucket is empty */ - ht_entry* next; + ht_entry *next; /* -1 if all buckets are empty. */ gpr_int32 prev_non_empty_bucket; /* -1 if all buckets are empty. */ @@ -66,7 +66,7 @@ struct unresizable_hash_table { gpr_uint32 num_buckets; /* Array of buckets initialized at creation time. Memory consumption is 16 bytes per bucket on a 64-bit platform. */ - bucket* buckets; + bucket *buckets; /* Index of the first non-empty bucket. -1 iff size == 0. */ gpr_int32 first_non_empty_bucket; /* Index of the last non_empty bucket. -1 iff size == 0. */ @@ -79,11 +79,11 @@ typedef struct entry_locator { gpr_int32 bucket_idx; int is_first_in_chain; int found; - ht_entry* prev_entry; + ht_entry *prev_entry; } entry_locator; /* Asserts if option is not valid. */ -void check_options(const census_ht_option* option) { +void check_options(const census_ht_option *option) { GPR_ASSERT(option != NULL); GPR_ASSERT(option->num_buckets > 0); GPR_ASSERT(option->key_type == CENSUS_HT_UINT64 || @@ -98,12 +98,12 @@ void check_options(const census_ht_option* option) { #define REMOVE_NEXT(options, ptr) \ do { \ - ht_entry* tmp = (ptr)->next; \ + ht_entry *tmp = (ptr)->next; \ (ptr)->next = tmp->next; \ delete_entry(options, tmp); \ } while (0) -static void delete_entry(const census_ht_option* opt, ht_entry* p) { +static void delete_entry(const census_ht_option *opt, ht_entry *p) { if (opt->delete_data != NULL) { opt->delete_data(p->data); } @@ -113,18 +113,18 @@ static void delete_entry(const census_ht_option* opt, ht_entry* p) { gpr_free(p); } -static gpr_uint64 hash(const census_ht_option* opt, census_ht_key key) { +static gpr_uint64 hash(const census_ht_option *opt, census_ht_key key) { return opt->key_type == CENSUS_HT_UINT64 ? key.val : opt->hash(key.ptr); } -census_ht* census_ht_create(const census_ht_option* option) { +census_ht *census_ht_create(const census_ht_option *option) { int i; - census_ht* ret = NULL; + census_ht *ret = NULL; check_options(option); - ret = (census_ht*)gpr_malloc(sizeof(census_ht)); + ret = (census_ht *)gpr_malloc(sizeof(census_ht)); ret->size = 0; ret->num_buckets = option->num_buckets; - ret->buckets = (bucket*)gpr_malloc(sizeof(bucket) * ret->num_buckets); + ret->buckets = (bucket *)gpr_malloc(sizeof(bucket) * ret->num_buckets); ret->options = *option; /* initialize each bucket */ for (i = 0; i < ret->options.num_buckets; i++) { @@ -135,11 +135,11 @@ census_ht* census_ht_create(const census_ht_option* option) { return ret; } -static gpr_int32 find_bucket_idx(const census_ht* ht, census_ht_key key) { +static gpr_int32 find_bucket_idx(const census_ht *ht, census_ht_key key) { return hash(&ht->options, key) % ht->num_buckets; } -static int keys_match(const census_ht_option* opt, const ht_entry* p, +static int keys_match(const census_ht_option *opt, const ht_entry *p, const census_ht_key key) { GPR_ASSERT(opt->key_type == CENSUS_HT_UINT64 || opt->key_type == CENSUS_HT_POINTER); @@ -147,10 +147,10 @@ static int keys_match(const census_ht_option* opt, const ht_entry* p, return !opt->compare_keys((p->key).ptr, key.ptr); } -static entry_locator ht_find(const census_ht* ht, census_ht_key key) { +static entry_locator ht_find(const census_ht *ht, census_ht_key key) { entry_locator loc = {0, 0, 0, NULL}; gpr_int32 idx = 0; - ht_entry* ptr = NULL; + ht_entry *ptr = NULL; GPR_ASSERT(ht != NULL); idx = find_bucket_idx(ht, key); ptr = ht->buckets[idx].next; @@ -178,7 +178,7 @@ static entry_locator ht_find(const census_ht* ht, census_ht_key key) { return loc; } -void* census_ht_find(const census_ht* ht, census_ht_key key) { +void *census_ht_find(const census_ht *ht, census_ht_key key) { entry_locator loc = ht_find(ht, key); if (loc.found == 0) { return NULL; @@ -187,9 +187,9 @@ void* census_ht_find(const census_ht* ht, census_ht_key key) { : loc.prev_entry->next->data; } -void census_ht_insert(census_ht* ht, census_ht_key key, void* data) { +void census_ht_insert(census_ht *ht, census_ht_key key, void *data) { gpr_int32 idx = find_bucket_idx(ht, key); - ht_entry* ptr = NULL; + ht_entry *ptr = NULL; entry_locator loc = ht_find(ht, key); if (loc.found) { /* Replace old value with new value. */ @@ -215,7 +215,7 @@ void census_ht_insert(census_ht* ht, census_ht_key key, void* data) { ht->buckets[idx].next_non_empty_bucket = -1; ht->last_non_empty_bucket = idx; } - ptr = (ht_entry*)gpr_malloc(sizeof(ht_entry)); + ptr = (ht_entry *)gpr_malloc(sizeof(ht_entry)); ptr->key = key; ptr->data = data; ptr->next = ht->buckets[idx].next; @@ -223,7 +223,7 @@ void census_ht_insert(census_ht* ht, census_ht_key key, void* data) { ht->size++; } -void census_ht_erase(census_ht* ht, census_ht_key key) { +void census_ht_erase(census_ht *ht, census_ht_key key) { entry_locator loc = ht_find(ht, key); if (loc.found == 0) { /* noop if not found */ @@ -231,7 +231,7 @@ void census_ht_erase(census_ht* ht, census_ht_key key) { } ht->size--; if (loc.is_first_in_chain) { - bucket* b = &ht->buckets[loc.bucket_idx]; + bucket *b = &ht->buckets[loc.bucket_idx]; GPR_ASSERT(b->next != NULL); /* The only entry in the bucket */ if (b->next->next == NULL) { @@ -256,8 +256,8 @@ void census_ht_erase(census_ht* ht, census_ht_key key) { } /* Returns NULL if input table is empty. */ -census_ht_kv* census_ht_get_all_elements(const census_ht* ht, size_t* num) { - census_ht_kv* ret = NULL; +census_ht_kv *census_ht_get_all_elements(const census_ht *ht, size_t *num) { + census_ht_kv *ret = NULL; int i = 0; gpr_int32 idx = -1; GPR_ASSERT(ht != NULL && num != NULL); @@ -266,10 +266,10 @@ census_ht_kv* census_ht_get_all_elements(const census_ht* ht, size_t* num) { return NULL; } - ret = (census_ht_kv*)gpr_malloc(sizeof(census_ht_kv) * ht->size); + ret = (census_ht_kv *)gpr_malloc(sizeof(census_ht_kv) * ht->size); idx = ht->first_non_empty_bucket; while (idx >= 0) { - ht_entry* ptr = ht->buckets[idx].next; + ht_entry *ptr = ht->buckets[idx].next; for (; ptr != NULL; ptr = ptr->next) { ret[i].k = ptr->key; ret[i].v = ptr->data; @@ -280,8 +280,8 @@ census_ht_kv* census_ht_get_all_elements(const census_ht* ht, size_t* num) { return ret; } -static void ht_delete_entry_chain(const census_ht_option* options, - ht_entry* first) { +static void ht_delete_entry_chain(const census_ht_option *options, + ht_entry *first) { if (first == NULL) { return; } @@ -291,7 +291,7 @@ static void ht_delete_entry_chain(const census_ht_option* options, delete_entry(options, first); } -void census_ht_destroy(census_ht* ht) { +void census_ht_destroy(census_ht *ht) { unsigned i; for (i = 0; i < ht->num_buckets; ++i) { ht_delete_entry_chain(&ht->options, ht->buckets[i].next); @@ -300,4 +300,4 @@ void census_ht_destroy(census_ht* ht) { gpr_free(ht); } -size_t census_ht_get_size(const census_ht* ht) { return ht->size; } +size_t census_ht_get_size(const census_ht *ht) { return ht->size; } diff --git a/src/core/statistics/hash_table.h b/src/core/statistics/hash_table.h index b7f8e11af4..8b39f536fd 100644 --- a/src/core/statistics/hash_table.h +++ b/src/core/statistics/hash_table.h @@ -61,7 +61,7 @@ typedef struct unresizable_hash_table census_ht; store and const char* for stats store). */ typedef union { gpr_uint64 val; - void* ptr; + void *ptr; } census_ht_key; typedef enum census_ht_key_type { @@ -76,56 +76,56 @@ typedef struct census_ht_option { gpr_int32 num_buckets; /* Fucntion to calculate uint64 hash value of the key. Only takes effect if key_type is POINTER. */ - gpr_uint64 (*hash)(const void*); + gpr_uint64 (*hash)(const void *); /* Function to compare two keys, returns 0 iff equal. Only takes effect if key_type is POINTER */ - int (*compare_keys)(const void* k1, const void* k2); + int (*compare_keys)(const void *k1, const void *k2); /* Value deleter. NULL if no specialized delete function is needed. */ - void (*delete_data)(void*); + void (*delete_data)(void *); /* Key deleter. NULL if table does not own the key. (e.g. key is part of the value or key is not owned by the table.) */ - void (*delete_key)(void*); + void (*delete_key)(void *); } census_ht_option; /* Creates a hashtable with fixed number of buckets according to the settings specified in 'options' arg. Function pointers "hash" and "compare_keys" must be provided if key_type is POINTER. Asserts if fail to create. */ -census_ht* census_ht_create(const census_ht_option* options); +census_ht *census_ht_create(const census_ht_option *options); /* Deletes hash table instance. Frees all dynamic memory owned by ht.*/ -void census_ht_destroy(census_ht* ht); +void census_ht_destroy(census_ht *ht); /* Inserts the input key-val pair into hash_table. If an entry with the same key exists in the table, the corresponding value will be overwritten by the input val. */ -void census_ht_insert(census_ht* ht, census_ht_key key, void* val); +void census_ht_insert(census_ht *ht, census_ht_key key, void *val); /* Returns pointer to data, returns NULL if not found. */ -void* census_ht_find(const census_ht* ht, census_ht_key key); +void *census_ht_find(const census_ht *ht, census_ht_key key); /* Erase hash table entry with input key. Noop if key is not found. */ -void census_ht_erase(census_ht* ht, census_ht_key key); +void census_ht_erase(census_ht *ht, census_ht_key key); typedef struct census_ht_kv { census_ht_key k; - void* v; + void *v; } census_ht_kv; /* Returns an array of pointers to all values in the hash table. Order of the elements can be arbitrary. Sets 'num' to the size of returned array. Caller owns returned array. */ -census_ht_kv* census_ht_get_all_elements(const census_ht* ht, size_t* num); +census_ht_kv *census_ht_get_all_elements(const census_ht *ht, size_t *num); /* Returns number of elements kept. */ -size_t census_ht_get_size(const census_ht* ht); +size_t census_ht_get_size(const census_ht *ht); /* Functor applied on each key-value pair while iterating through entries in the table. The functor should not mutate data. */ -typedef void (*census_ht_itr_cb)(census_ht_key key, const void* val_ptr, - void* state); +typedef void (*census_ht_itr_cb)(census_ht_key key, const void *val_ptr, + void *state); /* Iterates through all key-value pairs in the hash_table. The callback function should not invalidate data entries. */ -gpr_uint64 census_ht_for_all(const census_ht* ht, census_ht_itr_cb); +gpr_uint64 census_ht_for_all(const census_ht *ht, census_ht_itr_cb); #endif /* GRPC_INTERNAL_CORE_STATISTICS_HASH_TABLE_H */ diff --git a/src/core/statistics/window_stats.c b/src/core/statistics/window_stats.c index a64e080565..4d0d3cca4a 100644 --- a/src/core/statistics/window_stats.c +++ b/src/core/statistics/window_stats.c @@ -48,14 +48,14 @@ typedef struct census_window_stats_sum cws_sum; entries and a single statistic */ typedef struct census_window_stats_bucket { gpr_int64 count; - void* statistic; + void *statistic; } cws_bucket; /* Each interval has a set of buckets, and the variables needed to keep track of their current state */ typedef struct census_window_stats_interval_stats { /* The buckets. There will be 'granularity' + 1 of these. */ - cws_bucket* buckets; + cws_bucket *buckets; /* Index of the bucket containing the smallest time interval. */ int bottom_bucket; /* The smallest time storable in the current window. */ @@ -74,7 +74,7 @@ typedef struct census_window_stats { /* Record of stat_info. */ cws_stat_info stat_info; /* Stats for each interval. */ - cws_interval_stats* interval_stats; + cws_interval_stats *interval_stats; /* The time the newset stat was recorded. */ gpr_int64 newest_time; } window_stats; @@ -97,8 +97,8 @@ static gpr_int64 timespec_to_ns(const gpr_timespec ts) { return (gpr_int64)ts.tv_sec * GPR_NS_PER_SEC + ts.tv_nsec; } -static void cws_initialize_statistic(void* statistic, - const cws_stat_info* stat_info) { +static void cws_initialize_statistic(void *statistic, + const cws_stat_info *stat_info) { if (stat_info->stat_initialize == NULL) { memset(statistic, 0, stat_info->stat_size); } else { @@ -107,17 +107,17 @@ static void cws_initialize_statistic(void* statistic, } /* Create and initialize a statistic */ -static void* cws_create_statistic(const cws_stat_info* stat_info) { - void* stat = gpr_malloc(stat_info->stat_size); +static void *cws_create_statistic(const cws_stat_info *stat_info) { + void *stat = gpr_malloc(stat_info->stat_size); cws_initialize_statistic(stat, stat_info); return stat; } -window_stats* census_window_stats_create(int nintervals, +window_stats *census_window_stats_create(int nintervals, const gpr_timespec intervals[], int granularity, - const cws_stat_info* stat_info) { - window_stats* ret; + const cws_stat_info *stat_info) { + window_stats *ret; int i; /* validate inputs */ GPR_ASSERT(nintervals > 0 && granularity > 2 && intervals != NULL && @@ -129,17 +129,17 @@ window_stats* census_window_stats_create(int nintervals, granularity * 10 <= ns); } /* Allocate and initialize relevant data structures */ - ret = (window_stats*)gpr_malloc(sizeof(window_stats)); + ret = (window_stats *)gpr_malloc(sizeof(window_stats)); ret->nintervals = nintervals; ret->nbuckets = granularity + 1; ret->stat_info = *stat_info; ret->interval_stats = - (cws_interval_stats*)gpr_malloc(nintervals * sizeof(cws_interval_stats)); + (cws_interval_stats *)gpr_malloc(nintervals * sizeof(cws_interval_stats)); for (i = 0; i < nintervals; i++) { gpr_int64 size_ns = timespec_to_ns(intervals[i]); - cws_interval_stats* is = ret->interval_stats + i; - cws_bucket* buckets = is->buckets = - (cws_bucket*)gpr_malloc(ret->nbuckets * sizeof(cws_bucket)); + cws_interval_stats *is = ret->interval_stats + i; + cws_bucket *buckets = is->buckets = + (cws_bucket *)gpr_malloc(ret->nbuckets * sizeof(cws_bucket)); int b; for (b = 0; b < ret->nbuckets; b++) { buckets[b].statistic = cws_create_statistic(stat_info); @@ -168,8 +168,8 @@ window_stats* census_window_stats_create(int nintervals, /* When we try adding a measurement above the current interval range, we need to "shift" the buckets sufficiently to cover the new range. */ -static void cws_shift_buckets(const window_stats* wstats, - cws_interval_stats* is, gpr_int64 when_ns) { +static void cws_shift_buckets(const window_stats *wstats, + cws_interval_stats *is, gpr_int64 when_ns) { int i; /* number of bucket time widths to "shift" */ int shift; @@ -191,14 +191,14 @@ static void cws_shift_buckets(const window_stats* wstats, is->bottom += shift * is->width; } -void census_window_stats_add(window_stats* wstats, const gpr_timespec when, - const void* stat_value) { +void census_window_stats_add(window_stats *wstats, const gpr_timespec when, + const void *stat_value) { int i; gpr_int64 when_ns = timespec_to_ns(when); GPR_ASSERT(wstats->interval_stats != NULL); for (i = 0; i < wstats->nintervals; i++) { - cws_interval_stats* is = wstats->interval_stats + i; - cws_bucket* bucket; + cws_interval_stats *is = wstats->interval_stats + i; + cws_bucket *bucket; if (when_ns < is->bottom) { /* Below smallest time in interval: drop */ continue; } @@ -218,21 +218,21 @@ void census_window_stats_add(window_stats* wstats, const gpr_timespec when, } /* Add a specific bucket contents to an accumulating total. */ -static void cws_add_bucket_to_sum(cws_sum* sum, const cws_bucket* bucket, - const cws_stat_info* stat_info) { +static void cws_add_bucket_to_sum(cws_sum *sum, const cws_bucket *bucket, + const cws_stat_info *stat_info) { sum->count += bucket->count; stat_info->stat_add(sum->statistic, bucket->statistic); } /* Add a proportion to an accumulating sum. */ -static void cws_add_proportion_to_sum(double p, cws_sum* sum, - const cws_bucket* bucket, - const cws_stat_info* stat_info) { +static void cws_add_proportion_to_sum(double p, cws_sum *sum, + const cws_bucket *bucket, + const cws_stat_info *stat_info) { sum->count += p * bucket->count; stat_info->stat_add_proportion(p, sum->statistic, bucket->statistic); } -void census_window_stats_get_sums(const window_stats* wstats, +void census_window_stats_get_sums(const window_stats *wstats, const gpr_timespec when, cws_sum sums[]) { int i; gpr_int64 when_ns = timespec_to_ns(when); @@ -242,8 +242,8 @@ void census_window_stats_get_sums(const window_stats* wstats, int new_bucket; double last_proportion = 1.0; double bottom_proportion; - cws_interval_stats* is = wstats->interval_stats + i; - cws_sum* sum = sums + i; + cws_interval_stats *is = wstats->interval_stats + i; + cws_sum *sum = sums + i; sum->count = 0; cws_initialize_statistic(sum->statistic, &wstats->stat_info); if (when_ns < is->bottom) { @@ -255,12 +255,12 @@ void census_window_stats_get_sums(const window_stats* wstats, /* Calculating the appropriate amount of which buckets to use can get complicated. Essentially there are two cases: 1) if the "top" bucket (new_bucket, where the newest additions to the - stats recorded are entered) corresponds to 'when', then we need - to take a proportion of it - (if when < newest_time) or the full - thing. We also (possibly) need to take a corresponding - proportion of the bottom bucket. + stats recorded are entered) corresponds to 'when', then we need + to take a proportion of it - (if when < newest_time) or the full + thing. We also (possibly) need to take a corresponding + proportion of the bottom bucket. 2) Other cases, we just take a straight proportion. - */ + */ when_bucket = (when_ns - is->bottom) / is->width; new_bucket = (wstats->newest_time - is->bottom) / is->width; if (new_bucket == when_bucket) { @@ -300,7 +300,7 @@ void census_window_stats_get_sums(const window_stats* wstats, } } -void census_window_stats_destroy(window_stats* wstats) { +void census_window_stats_destroy(window_stats *wstats) { int i; GPR_ASSERT(wstats->interval_stats != NULL); for (i = 0; i < wstats->nintervals; i++) { diff --git a/src/core/statistics/window_stats.h b/src/core/statistics/window_stats.h index 0020f6b44c..f4732e96a0 100644 --- a/src/core/statistics/window_stats.h +++ b/src/core/statistics/window_stats.h @@ -120,13 +120,13 @@ typedef struct census_window_stats_stat_info { size_t stat_size; /* Function to initialize a user-defined statistics object. If this is set * to NULL, then the object will be zero-initialized. */ - void (*stat_initialize)(void* stat); + void (*stat_initialize)(void *stat); /* Function to add one user-defined statistics object ('addme') to 'base' */ - void (*stat_add)(void* base, const void* addme); + void (*stat_add)(void *base, const void *addme); /* As for previous function, but only add a proportion 'p'. This API will currently only use 'p' values in the range [0,1], but other values are possible in the future, and should be supported. */ - void (*stat_add_proportion)(double p, void* base, const void* addme); + void (*stat_add_proportion)(double p, void *base, const void *addme); } census_window_stats_stat_info; /* Create a new window_stats object. 'nintervals' is the number of @@ -138,29 +138,29 @@ typedef struct census_window_stats_stat_info { years will be treated as essentially infinite in size. This function will GPR_ASSERT() if the object cannot be created or any of the parameters have invalid values. This function is thread-safe. */ -struct census_window_stats* census_window_stats_create( +struct census_window_stats *census_window_stats_create( int nintervals, const gpr_timespec intervals[], int granularity, - const census_window_stats_stat_info* stat_info); + const census_window_stats_stat_info *stat_info); /* Add a new measurement (in 'stat_value'), as of a given time ('when'). This function is thread-compatible. */ -void census_window_stats_add(struct census_window_stats* wstats, - const gpr_timespec when, const void* stat_value); +void census_window_stats_add(struct census_window_stats *wstats, + const gpr_timespec when, const void *stat_value); /* Structure used to record a single intervals sum for a given statistic */ typedef struct census_window_stats_sum { /* Total count of samples. Note that because some internal interpolation - is performed, the count of samples returned for each interval may not be an - integral value. */ + is performed, the count of samples returned for each interval may not be an + integral value. */ double count; /* Sum for statistic */ - void* statistic; + void *statistic; } census_window_stats_sums; /* Retrieve a set of all values stored in a window_stats object 'wstats'. The number of 'sums' MUST be the same as the number 'nintervals' used in census_window_stats_create(). This function is thread-compatible. */ -void census_window_stats_get_sums(const struct census_window_stats* wstats, +void census_window_stats_get_sums(const struct census_window_stats *wstats, const gpr_timespec when, struct census_window_stats_sum sums[]); @@ -168,6 +168,6 @@ void census_window_stats_get_sums(const struct census_window_stats* wstats, object will no longer be usable from any of the above functions (and calling them will most likely result in a NULL-pointer dereference or assertion failure). This function is thread-compatible. */ -void census_window_stats_destroy(struct census_window_stats* wstats); +void census_window_stats_destroy(struct census_window_stats *wstats); #endif /* GRPC_INTERNAL_CORE_STATISTICS_WINDOW_STATS_H */ diff --git a/src/core/support/alloc.c b/src/core/support/alloc.c index d2ed82e771..bfcb77956b 100644 --- a/src/core/support/alloc.c +++ b/src/core/support/alloc.c @@ -35,22 +35,32 @@ #include <stdlib.h> #include <grpc/support/port_platform.h> +#include "src/core/profiling/timers.h" void *gpr_malloc(size_t size) { - void *p = malloc(size); + void *p; + GPR_TIMER_BEGIN("gpr_malloc", 0); + p = malloc(size); if (!p) { abort(); } + GPR_TIMER_END("gpr_malloc", 0); return p; } -void gpr_free(void *p) { free(p); } +void gpr_free(void *p) { + GPR_TIMER_BEGIN("gpr_free", 0); + free(p); + GPR_TIMER_END("gpr_free", 0); +} void *gpr_realloc(void *p, size_t size) { + GPR_TIMER_BEGIN("gpr_realloc", 0); p = realloc(p, size); if (!p) { abort(); } + GPR_TIMER_END("gpr_realloc", 0); return p; } diff --git a/src/core/support/block_annotate.h b/src/core/support/block_annotate.h new file mode 100644 index 0000000000..3cd8eee272 --- /dev/null +++ b/src/core/support/block_annotate.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_SUPPORT_BLOCK_ANNOTATE_H +#define GRPC_INTERNAL_CORE_SUPPORT_BLOCK_ANNOTATE_H + +/* These annotations identify the beginning and end of regions where + the code may block for reasons other than synchronization functions. + These include poll, epoll, and getaddrinfo. */ + +#define GRPC_SCHEDULING_START_BLOCKING_REGION \ + do { \ + } while (0) +#define GRPC_SCHEDULING_END_BLOCKING_REGION \ + do { \ + } while (0) + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_BLOCK_ANNOTATE_H */ diff --git a/src/core/support/cmdline.c b/src/core/support/cmdline.c index 45a3182f73..87f60bca2e 100644 --- a/src/core/support/cmdline.c +++ b/src/core/support/cmdline.c @@ -192,9 +192,9 @@ static void print_usage_and_die(gpr_cmdline *cl) { exit(1); } -static void extra_state(gpr_cmdline *cl, char *arg) { +static void extra_state(gpr_cmdline *cl, char *str) { if (!cl->extra_arg) print_usage_and_die(cl); - cl->extra_arg(cl->extra_arg_user_data, arg); + cl->extra_arg(cl->extra_arg_user_data, str); } static arg *find_arg(gpr_cmdline *cl, char *name) { @@ -214,7 +214,7 @@ static arg *find_arg(gpr_cmdline *cl, char *name) { return a; } -static void value_state(gpr_cmdline *cl, char *arg) { +static void value_state(gpr_cmdline *cl, char *str) { long intval; char *end; @@ -222,80 +222,80 @@ static void value_state(gpr_cmdline *cl, char *arg) { switch (cl->cur_arg->type) { case ARGTYPE_INT: - intval = strtol(arg, &end, 0); + intval = strtol(str, &end, 0); if (*end || intval < INT_MIN || intval > INT_MAX) { - fprintf(stderr, "expected integer, got '%s' for %s\n", arg, + fprintf(stderr, "expected integer, got '%s' for %s\n", str, cl->cur_arg->name); print_usage_and_die(cl); } *(int *)cl->cur_arg->value = (int)intval; break; case ARGTYPE_BOOL: - if (0 == strcmp(arg, "1") || 0 == strcmp(arg, "true")) { + if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) { *(int *)cl->cur_arg->value = 1; - } else if (0 == strcmp(arg, "0") || 0 == strcmp(arg, "false")) { + } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) { *(int *)cl->cur_arg->value = 0; } else { - fprintf(stderr, "expected boolean, got '%s' for %s\n", arg, + fprintf(stderr, "expected boolean, got '%s' for %s\n", str, cl->cur_arg->name); print_usage_and_die(cl); } break; case ARGTYPE_STRING: - *(char **)cl->cur_arg->value = arg; + *(char **)cl->cur_arg->value = str; break; } cl->state = normal_state; } -static void normal_state(gpr_cmdline *cl, char *arg) { +static void normal_state(gpr_cmdline *cl, char *str) { char *eq = NULL; char *tmp = NULL; char *arg_name = NULL; - if (0 == strcmp(arg, "-help") || 0 == strcmp(arg, "--help") || - 0 == strcmp(arg, "-h")) { + if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") || + 0 == strcmp(str, "-h")) { print_usage_and_die(cl); } cl->cur_arg = NULL; - if (arg[0] == '-') { - if (arg[1] == '-') { - if (arg[2] == 0) { + if (str[0] == '-') { + if (str[1] == '-') { + if (str[2] == 0) { /* handle '--' to move to just extra args */ cl->state = extra_state; return; } - arg += 2; + str += 2; } else { - arg += 1; + str += 1; } - /* first byte of arg is now past the leading '-' or '--' */ - if (arg[0] == 'n' && arg[1] == 'o' && arg[2] == '-') { - /* arg is of the form '--no-foo' - it's a flag disable */ - arg += 3; - cl->cur_arg = find_arg(cl, arg); + /* first byte of str is now past the leading '-' or '--' */ + if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') { + /* str is of the form '--no-foo' - it's a flag disable */ + str += 3; + cl->cur_arg = find_arg(cl, str); if (cl->cur_arg->type != ARGTYPE_BOOL) { - fprintf(stderr, "%s is not a flag argument\n", arg); + fprintf(stderr, "%s is not a flag argument\n", str); print_usage_and_die(cl); } *(int *)cl->cur_arg->value = 0; return; /* early out */ } - eq = strchr(arg, '='); + eq = strchr(str, '='); if (eq != NULL) { /* copy the string into a temp buffer and extract the name */ - tmp = arg_name = gpr_malloc((size_t)(eq - arg + 1)); - memcpy(arg_name, arg, (size_t)(eq - arg)); - arg_name[eq - arg] = 0; + tmp = arg_name = gpr_malloc((size_t)(eq - str + 1)); + memcpy(arg_name, str, (size_t)(eq - str)); + arg_name[eq - str] = 0; } else { - arg_name = arg; + arg_name = str; } cl->cur_arg = find_arg(cl, arg_name); if (eq != NULL) { - /* arg was of the type --foo=value, parse the value */ + /* str was of the type --foo=value, parse the value */ value_state(cl, eq + 1); } else if (cl->cur_arg->type != ARGTYPE_BOOL) { /* flag types don't have a '--foo value' variant, other types do */ @@ -305,7 +305,7 @@ static void normal_state(gpr_cmdline *cl, char *arg) { *(int *)cl->cur_arg->value = 1; } } else { - extra_state(cl, arg); + extra_state(cl, str); } gpr_free(tmp); diff --git a/src/core/support/cpu_posix.c b/src/core/support/cpu_posix.c index 99484e37fb..55d92c0555 100644 --- a/src/core/support/cpu_posix.c +++ b/src/core/support/cpu_posix.c @@ -44,11 +44,11 @@ static __thread char magic_thread_local; -static int ncpus = 0; +static long ncpus = 0; static void init_ncpus() { ncpus = sysconf(_SC_NPROCESSORS_ONLN); - if (ncpus < 1) { + if (ncpus < 1 || ncpus > GPR_UINT32_MAX) { gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); ncpus = 1; } @@ -57,7 +57,7 @@ static void init_ncpus() { unsigned gpr_cpu_num_cores(void) { static gpr_once once = GPR_ONCE_INIT; gpr_once_init(&once, init_ncpus); - return ncpus; + return (unsigned)ncpus; } /* This is a cheap, but good enough, pointer hash for sharding things: */ @@ -71,7 +71,7 @@ unsigned gpr_cpu_current_cpu(void) { most code that's using this is using it to shard across work queues though, so here we use thread identity instead to achieve a similar though not identical effect */ - return shard_ptr(&magic_thread_local); + return (unsigned)shard_ptr(&magic_thread_local); } #endif /* GPR_CPU_POSIX */ diff --git a/src/core/support/file.c b/src/core/support/file.c index c1361d8a9e..8c673dbcc6 100644 --- a/src/core/support/file.c +++ b/src/core/support/file.c @@ -40,6 +40,7 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> +#include "src/core/support/block_annotate.h" #include "src/core/support/string.h" gpr_slice gpr_load_file(const char *filename, int add_null_terminator, @@ -48,9 +49,11 @@ gpr_slice gpr_load_file(const char *filename, int add_null_terminator, size_t contents_size = 0; char *error_msg = NULL; gpr_slice result = gpr_empty_slice(); - FILE *file = fopen(filename, "rb"); + FILE *file; size_t bytes_read = 0; + GRPC_SCHEDULING_START_BLOCKING_REGION; + file = fopen(filename, "rb"); if (file == NULL) { gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename, strerror(errno)); @@ -83,5 +86,6 @@ end: if (success != NULL) *success = 0; } if (file != NULL) fclose(file); + GRPC_SCHEDULING_END_BLOCKING_REGION; return result; } diff --git a/src/core/support/histogram.c b/src/core/support/histogram.c index 78dbf98684..8a1a9d9233 100644 --- a/src/core/support/histogram.c +++ b/src/core/support/histogram.c @@ -212,7 +212,7 @@ double gpr_histogram_percentile(gpr_histogram *h, double percentile) { } double gpr_histogram_mean(gpr_histogram *h) { - GPR_ASSERT(h->count); + GPR_ASSERT(h->count != 0); return h->sum / h->count; } diff --git a/src/core/support/host_port.c b/src/core/support/host_port.c index a28f04df9c..23f65b1581 100644 --- a/src/core/support/host_port.c +++ b/src/core/support/host_port.c @@ -79,7 +79,7 @@ int gpr_split_host_port(const char *name, char **host, char **port) { host_len = (size_t)(rbracket - host_start); if (memchr(host_start, ':', host_len) == NULL) { /* Require all bracketed hosts to contain a colon, because a hostname or - IPv4 address should never use brackets. */ + IPv4 address should never use brackets. */ return 0; } } else { diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c index 940ee20f15..8b050dbee7 100644 --- a/src/core/support/log_posix.c +++ b/src/core/support/log_posix.c @@ -62,9 +62,9 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, } else if ((size_t)ret <= sizeof(buf) - 1) { message = buf; } else { - message = allocated = gpr_malloc(ret + 1); + message = allocated = gpr_malloc((size_t)ret + 1); va_start(args, format); - vsnprintf(message, ret + 1, format, args); + vsnprintf(message, (size_t)(ret + 1), format, args); va_end(args); } gpr_log_message(file, line, severity, message); diff --git a/src/core/support/slice_buffer.c b/src/core/support/slice_buffer.c index 987d5cb9b5..310fbe1350 100644 --- a/src/core/support/slice_buffer.c +++ b/src/core/support/slice_buffer.c @@ -69,7 +69,7 @@ void gpr_slice_buffer_destroy(gpr_slice_buffer *sb) { } } -gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned n) { +gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, size_t n) { gpr_slice *back; gpr_uint8 *out; @@ -207,3 +207,27 @@ void gpr_slice_buffer_move_into(gpr_slice_buffer *src, gpr_slice_buffer *dst) { src->count = 0; src->length = 0; } + +void gpr_slice_buffer_trim_end(gpr_slice_buffer *sb, size_t n, + gpr_slice_buffer *garbage) { + GPR_ASSERT(n <= sb->length); + sb->length -= n; + for (;;) { + size_t idx = sb->count - 1; + gpr_slice slice = sb->slices[idx]; + size_t slice_len = GPR_SLICE_LENGTH(slice); + if (slice_len > n) { + sb->slices[idx] = gpr_slice_split_head(&slice, slice_len - n); + gpr_slice_buffer_add_indexed(garbage, slice); + return; + } else if (slice_len == n) { + gpr_slice_buffer_add_indexed(garbage, slice); + sb->count = idx; + return; + } else { + gpr_slice_buffer_add_indexed(garbage, slice); + n -= slice_len; + sb->count = idx; + } + } +} diff --git a/src/core/support/stack_lockfree.c b/src/core/support/stack_lockfree.c index 27ecf62280..df9a09894c 100644 --- a/src/core/support/stack_lockfree.c +++ b/src/core/support/stack_lockfree.c @@ -67,7 +67,7 @@ typedef union lockfree_node { #define ENTRY_ALIGNMENT_BITS 3 /* make sure that entries aligned to 8-bytes */ #define INVALID_ENTRY_INDEX \ ((1 << 16) - 1) /* reserve this entry as invalid \ - */ + */ struct gpr_stack_lockfree { lockfree_node *entries; @@ -79,7 +79,7 @@ struct gpr_stack_lockfree { #endif }; -gpr_stack_lockfree *gpr_stack_lockfree_create(int entries) { +gpr_stack_lockfree *gpr_stack_lockfree_create(size_t entries) { gpr_stack_lockfree *stack; stack = gpr_malloc(sizeof(*stack)); /* Since we only allocate 16 bits to represent an entry number, @@ -123,13 +123,13 @@ int gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) { #ifndef NDEBUG /* Check for double push */ { - int pushed_index = entry / (8 * sizeof(gpr_atm)); - int pushed_bit = entry % (8 * sizeof(gpr_atm)); + int pushed_index = entry / (int)(8 * sizeof(gpr_atm)); + int pushed_bit = entry % (int)(8 * sizeof(gpr_atm)); gpr_atm old_val; old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index], (gpr_atm)(1UL << pushed_bit)); - GPR_ASSERT((old_val & (1UL << pushed_bit)) == 0); + GPR_ASSERT((old_val & (gpr_atm)(1UL << pushed_bit)) == 0); } #endif @@ -167,7 +167,7 @@ int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack) { old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index], -(gpr_atm)(1UL << pushed_bit)); - GPR_ASSERT((old_val & (1UL << pushed_bit)) != 0); + GPR_ASSERT((old_val & (gpr_atm)(1UL << pushed_bit)) != 0); } #endif diff --git a/src/core/support/stack_lockfree.h b/src/core/support/stack_lockfree.h index eec960fbb0..ca58dd007a 100644 --- a/src/core/support/stack_lockfree.h +++ b/src/core/support/stack_lockfree.h @@ -34,18 +34,20 @@ #ifndef GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H #define GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H +#include <stddef.h> + typedef struct gpr_stack_lockfree gpr_stack_lockfree; /* This stack must specify the maximum number of entries to track. The current implementation only allows up to 65534 entries */ -gpr_stack_lockfree* gpr_stack_lockfree_create(int entries); -void gpr_stack_lockfree_destroy(gpr_stack_lockfree* stack); +gpr_stack_lockfree *gpr_stack_lockfree_create(size_t entries); +void gpr_stack_lockfree_destroy(gpr_stack_lockfree *stack); /* Pass in a valid entry number for the next stack entry */ /* Returns 1 if this is the first element on the stack, 0 otherwise */ -int gpr_stack_lockfree_push(gpr_stack_lockfree*, int entry); +int gpr_stack_lockfree_push(gpr_stack_lockfree *, int entry); /* Returns -1 on empty or the actual entry number */ -int gpr_stack_lockfree_pop(gpr_stack_lockfree* stack); +int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack); #endif /* GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H */ diff --git a/src/core/support/string.c b/src/core/support/string.c index af0389ea83..e0ffeb8a4a 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -101,7 +101,7 @@ static void asciidump(dump_out *out, const char *buf, size_t len) { dump_out_append(out, '\''); } for (cur = beg; cur != end; ++cur) { - dump_out_append(out, isprint(*cur) ? *(char *)cur : '.'); + dump_out_append(out, (char)(isprint(*cur) ? *(char *)cur : '.')); } if (!out_was_empty) { dump_out_append(out, '\''); diff --git a/src/core/support/string_win32.c b/src/core/support/string_win32.c index 8ffb0a225e..914ba8771c 100644 --- a/src/core/support/string_win32.c +++ b/src/core/support/string_win32.c @@ -81,7 +81,8 @@ int gpr_asprintf(char **strp, const char *format, ...) { } #if defined UNICODE || defined _UNICODE -LPTSTR gpr_char_to_tchar(LPCSTR input) { +LPTSTR +gpr_char_to_tchar(LPCSTR input) { LPTSTR ret; int needed = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); if (needed == 0) return NULL; @@ -90,7 +91,8 @@ LPTSTR gpr_char_to_tchar(LPCSTR input) { return ret; } -LPSTR gpr_tchar_to_char(LPCTSTR input) { +LPSTR +gpr_tchar_to_char(LPCTSTR input) { LPSTR ret; int needed = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL); if (needed == 0) return NULL; diff --git a/src/core/support/sync_posix.c b/src/core/support/sync_posix.c index 6f078cd4bb..39c96feb13 100644 --- a/src/core/support/sync_posix.c +++ b/src/core/support/sync_posix.c @@ -40,16 +40,25 @@ #include <grpc/support/log.h> #include <grpc/support/sync.h> #include <grpc/support/time.h> +#include "src/core/profiling/timers.h" -void gpr_mu_init(gpr_mu *mu) { GPR_ASSERT(pthread_mutex_init(mu, NULL) == 0); } +void gpr_mu_init(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_init(mu, NULL) == 0); } -void gpr_mu_destroy(gpr_mu *mu) { GPR_ASSERT(pthread_mutex_destroy(mu) == 0); } +void gpr_mu_destroy(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_destroy(mu) == 0); } -void gpr_mu_lock(gpr_mu *mu) { GPR_ASSERT(pthread_mutex_lock(mu) == 0); } +void gpr_mu_lock(gpr_mu* mu) { + GPR_TIMER_BEGIN("gpr_mu_lock", 0); + GPR_ASSERT(pthread_mutex_lock(mu) == 0); + GPR_TIMER_END("gpr_mu_lock", 0); +} -void gpr_mu_unlock(gpr_mu *mu) { GPR_ASSERT(pthread_mutex_unlock(mu) == 0); } +void gpr_mu_unlock(gpr_mu* mu) { + GPR_TIMER_BEGIN("gpr_mu_unlock", 0); + GPR_ASSERT(pthread_mutex_unlock(mu) == 0); + GPR_TIMER_END("gpr_mu_unlock", 0); +} -int gpr_mu_trylock(gpr_mu *mu) { +int gpr_mu_trylock(gpr_mu* mu) { int err = pthread_mutex_trylock(mu); GPR_ASSERT(err == 0 || err == EBUSY); return err == 0; @@ -57,11 +66,11 @@ int gpr_mu_trylock(gpr_mu *mu) { /*----------------------------------------*/ -void gpr_cv_init(gpr_cv *cv) { GPR_ASSERT(pthread_cond_init(cv, NULL) == 0); } +void gpr_cv_init(gpr_cv* cv) { GPR_ASSERT(pthread_cond_init(cv, NULL) == 0); } -void gpr_cv_destroy(gpr_cv *cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); } +void gpr_cv_destroy(gpr_cv* cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); } -int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { +int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) { int err = 0; if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == 0) { @@ -77,15 +86,15 @@ int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { return err == ETIMEDOUT; } -void gpr_cv_signal(gpr_cv *cv) { GPR_ASSERT(pthread_cond_signal(cv) == 0); } +void gpr_cv_signal(gpr_cv* cv) { GPR_ASSERT(pthread_cond_signal(cv) == 0); } -void gpr_cv_broadcast(gpr_cv *cv) { +void gpr_cv_broadcast(gpr_cv* cv) { GPR_ASSERT(pthread_cond_broadcast(cv) == 0); } /*----------------------------------------*/ -void gpr_once_init(gpr_once *once, void (*init_function)(void)) { +void gpr_once_init(gpr_once* once, void (*init_function)(void)) { GPR_ASSERT(pthread_once(once, init_function) == 0); } diff --git a/src/core/support/sync_win32.c b/src/core/support/sync_win32.c index df23492171..69dd46399a 100644 --- a/src/core/support/sync_win32.c +++ b/src/core/support/sync_win32.c @@ -88,9 +88,9 @@ int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { SleepConditionVariableCS(cv, &mu->cs, INFINITE); } else { gpr_timespec now = gpr_now(abs_deadline.clock_type); - gpr_int64 now_ms = now.tv_sec * 1000 + now.tv_nsec / 1000000; + gpr_int64 now_ms = (gpr_int64)now.tv_sec * 1000 + now.tv_nsec / 1000000; gpr_int64 deadline_ms = - abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; + (gpr_int64)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; if (now_ms >= deadline_ms) { timeout = 1; } else { diff --git a/src/core/support/thd.c b/src/core/support/thd.c index 32c0db5b66..41daeb5d0e 100644 --- a/src/core/support/thd.c +++ b/src/core/support/thd.c @@ -45,20 +45,20 @@ gpr_thd_options gpr_thd_options_default(void) { return options; } -void gpr_thd_options_set_detached(gpr_thd_options *options) { +void gpr_thd_options_set_detached(gpr_thd_options* options) { options->flags &= ~GPR_THD_JOINABLE; } -void gpr_thd_options_set_joinable(gpr_thd_options *options) { +void gpr_thd_options_set_joinable(gpr_thd_options* options) { options->flags |= GPR_THD_JOINABLE; } -int gpr_thd_options_is_detached(const gpr_thd_options *options) { +int gpr_thd_options_is_detached(const gpr_thd_options* options) { if (!options) return 1; return (options->flags & GPR_THD_JOINABLE) == 0; } -int gpr_thd_options_is_joinable(const gpr_thd_options *options) { +int gpr_thd_options_is_joinable(const gpr_thd_options* options) { if (!options) return 0; return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE; } diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c index a274400243..02cfca8555 100644 --- a/src/core/support/time_posix.c +++ b/src/core/support/time_posix.c @@ -41,6 +41,7 @@ #include <unistd.h> #include <grpc/support/log.h> #include <grpc/support/time.h> +#include "src/core/support/block_annotate.h" static struct timespec timespec_from_gpr(gpr_timespec gts) { struct timespec rv; @@ -51,29 +52,29 @@ static struct timespec timespec_from_gpr(gpr_timespec gts) { #if _POSIX_TIMERS > 0 static gpr_timespec gpr_from_timespec(struct timespec ts, - gpr_clock_type clock) { + gpr_clock_type clock_type) { gpr_timespec rv; rv.tv_sec = ts.tv_sec; rv.tv_nsec = (int)ts.tv_nsec; - rv.clock_type = clock; + rv.clock_type = clock_type; return rv; } /** maps gpr_clock_type --> clockid_t for clock_gettime */ static clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC, CLOCK_REALTIME}; -void gpr_time_init(void) {} +void gpr_time_init(void) { gpr_precise_clock_init(); } -gpr_timespec gpr_now(gpr_clock_type clock) { +gpr_timespec gpr_now(gpr_clock_type clock_type) { struct timespec now; - GPR_ASSERT(clock != GPR_TIMESPAN); - if (clock == GPR_CLOCK_PRECISE) { + GPR_ASSERT(clock_type != GPR_TIMESPAN); + if (clock_type == GPR_CLOCK_PRECISE) { gpr_timespec ret; gpr_precise_clock_now(&ret); return ret; } else { - clock_gettime(clockid_for_gpr_clock[clock], &now); - return gpr_from_timespec(now, clock); + clock_gettime(clockid_for_gpr_clock[clock_type], &now); + return gpr_from_timespec(now, clock_type); } } #else @@ -88,6 +89,7 @@ static uint64_t g_time_start; void gpr_time_init(void) { mach_timebase_info_data_t tb = {0, 1}; + gpr_precise_clock_init(); mach_timebase_info(&tb); g_time_scale = tb.numer; g_time_scale /= tb.denom; @@ -108,8 +110,8 @@ gpr_timespec gpr_now(gpr_clock_type clock) { break; case GPR_CLOCK_MONOTONIC: now_dbl = (mach_absolute_time() - g_time_start) * g_time_scale; - now.tv_sec = now_dbl * 1e-9; - now.tv_nsec = now_dbl - now.tv_sec * 1e9; + now.tv_sec = (time_t)(now_dbl * 1e-9); + now.tv_nsec = (int)(now_dbl - ((double)now.tv_sec) * 1e9); break; case GPR_CLOCK_PRECISE: gpr_precise_clock_now(&now); @@ -126,6 +128,7 @@ void gpr_sleep_until(gpr_timespec until) { gpr_timespec now; gpr_timespec delta; struct timespec delta_ts; + int ns_result; for (;;) { /* We could simplify by using clock_nanosleep instead, but it might be @@ -137,7 +140,10 @@ void gpr_sleep_until(gpr_timespec until) { delta = gpr_time_sub(until, now); delta_ts = timespec_from_gpr(delta); - if (nanosleep(&delta_ts, NULL) == 0) { + GRPC_SCHEDULING_START_BLOCKING_REGION; + ns_result = nanosleep(&delta_ts, NULL); + GRPC_SCHEDULING_END_BLOCKING_REGION; + if (ns_result == 0) { break; } } diff --git a/src/core/support/time_precise.c b/src/core/support/time_precise.c new file mode 100644 index 0000000000..b37517e639 --- /dev/null +++ b/src/core/support/time_precise.c @@ -0,0 +1,89 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <grpc/support/log.h> +#include <grpc/support/time.h> +#include <stdio.h> + +#ifdef GRPC_TIMERS_RDTSC +#if defined(__i386__) +static void gpr_get_cycle_counter(long long int *clk) { + long long int ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + *clk = ret; +} + +// ---------------------------------------------------------------- +#elif defined(__x86_64__) || defined(__amd64__) +static void gpr_get_cycle_counter(long long int *clk) { + unsigned long long low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + *clk = (long long)(high << 32) | (long long)low; +} +#endif + +static double cycles_per_second = 0; +static long long int start_cycle; +void gpr_precise_clock_init(void) { + time_t start; + long long end_cycle; + gpr_log(GPR_DEBUG, "Calibrating timers"); + start = time(NULL); + while (time(NULL) == start) + ; + gpr_get_cycle_counter(&start_cycle); + while (time(NULL) <= start + 10) + ; + gpr_get_cycle_counter(&end_cycle); + cycles_per_second = (double)(end_cycle - start_cycle) / 10.0; + gpr_log(GPR_DEBUG, "... cycles_per_second = %f\n", cycles_per_second); +} + +void gpr_precise_clock_now(gpr_timespec *clk) { + long long int counter; + double secs; + gpr_get_cycle_counter(&counter); + secs = (double)(counter - start_cycle) / cycles_per_second; + clk->clock_type = GPR_CLOCK_PRECISE; + clk->tv_sec = (time_t)secs; + clk->tv_nsec = (int)(1e9 * (secs - (double)clk->tv_sec)); +} + +#else /* GRPC_TIMERS_RDTSC */ +void gpr_precise_clock_init(void) {} + +void gpr_precise_clock_now(gpr_timespec *clk) { + *clk = gpr_now(GPR_CLOCK_REALTIME); + clk->clock_type = GPR_CLOCK_PRECISE; +} +#endif /* GRPC_TIMERS_RDTSC */ diff --git a/src/core/support/time_precise.h b/src/core/support/time_precise.h index 574ebb8448..80c5000123 100644 --- a/src/core/support/time_precise.h +++ b/src/core/support/time_precise.h @@ -34,60 +34,9 @@ #ifndef GRPC_CORE_SUPPORT_TIME_PRECISE_H_ #define GRPC_CORE_SUPPORT_TIME_PRECISE_H_ -#include <grpc/support/sync.h> #include <grpc/support/time.h> -#include <stdio.h> -#ifdef GRPC_TIMERS_RDTSC -#if defined(__i386__) -static void gpr_get_cycle_counter(long long int *clk) { - long long int ret; - __asm__ volatile("rdtsc" : "=A"(ret)); - *clk = ret; -} - -// ---------------------------------------------------------------- -#elif defined(__x86_64__) || defined(__amd64__) -static void gpr_get_cycle_counter(long long int *clk) { - unsigned long long low, high; - __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); - *clk = (high << 32) | low; -} -#endif - -static gpr_once precise_clock_init = GPR_ONCE_INIT; -static long long cycles_per_second = 0; -static void gpr_precise_clock_init() { - time_t start = time(NULL); - gpr_precise_clock start_cycle; - gpr_precise_clock end_cycle; - while (time(NULL) == start) - ; - gpr_get_cycle_counter(&start_cycle); - while (time(NULL) == start + 1) - ; - gpr_get_cycle_counter(&end_cycle); - cycles_per_second = end_cycle - start_cycle; -} - -static double grpc_precise_clock_scaling_factor() { - gpr_once_init(&precise_clock_init, grpc_precise_clock_init); - return 1e6 / cycles_per_second; -} - -static void gpr_precise_clock_now(gpr_timespec *clk) { - long long int counter; - gpr_get_cycle_counter(&counter); - clk->clock = GPR_CLOCK_REALTIME; - clk->tv_sec = counter / cycles_per_second; - clk->tv_nsec = counter % cycles_per_second; -} - -#else /* GRPC_TIMERS_RDTSC */ -static void gpr_precise_clock_now(gpr_timespec *clk) { - *clk = gpr_now(GPR_CLOCK_REALTIME); - clk->clock_type = GPR_CLOCK_PRECISE; -} -#endif /* GRPC_TIMERS_RDTSC */ +void gpr_precise_clock_init(void); +void gpr_precise_clock_now(gpr_timespec *clk); #endif /* GRPC_CORE_SUPPORT_TIME_PRECISE_ */ diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c index f794855429..bc0586d069 100644 --- a/src/core/support/time_win32.c +++ b/src/core/support/time_win32.c @@ -41,6 +41,8 @@ #include <src/core/support/time_precise.h> #include <sys/timeb.h> +#include "src/core/support/block_annotate.h" + static LARGE_INTEGER g_start_time; static double g_time_scale; @@ -92,7 +94,9 @@ void gpr_sleep_until(gpr_timespec until) { delta = gpr_time_sub(until, now); sleep_millis = (DWORD)delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; + GRPC_SCHEDULING_START_BLOCKING_REGION; Sleep(sleep_millis); + GRPC_SCHEDULING_END_BLOCKING_REGION; } } diff --git a/src/core/surface/alarm.c b/src/core/surface/alarm.c new file mode 100644 index 0000000000..7c47dd56f8 --- /dev/null +++ b/src/core/surface/alarm.c @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/iomgr/timer.h" +#include "src/core/surface/completion_queue.h" +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> + +struct grpc_alarm { + grpc_timer alarm; + grpc_cq_completion completion; + /** completion queue where events about this alarm will be posted */ + grpc_completion_queue *cq; + /** user supplied tag */ + void *tag; +}; + +static void do_nothing_end_completion(grpc_exec_ctx *exec_ctx, void *arg, + grpc_cq_completion *c) {} + +static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, int success) { + grpc_alarm *alarm = arg; + grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, success, + do_nothing_end_completion, NULL, &alarm->completion); +} + +grpc_alarm *grpc_alarm_create(grpc_completion_queue *cq, gpr_timespec deadline, + void *tag) { + grpc_alarm *alarm = gpr_malloc(sizeof(grpc_alarm)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_CQ_INTERNAL_REF(cq, "alarm"); + alarm->cq = cq; + alarm->tag = tag; + + grpc_timer_init(&exec_ctx, &alarm->alarm, deadline, alarm_cb, alarm, + gpr_now(GPR_CLOCK_MONOTONIC)); + grpc_cq_begin_op(cq); + grpc_exec_ctx_finish(&exec_ctx); + return alarm; +} + +void grpc_alarm_cancel(grpc_alarm *alarm) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_timer_cancel(&exec_ctx, &alarm->alarm); + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_alarm_destroy(grpc_alarm *alarm) { + grpc_alarm_cancel(alarm); + GRPC_CQ_INTERNAL_UNREF(alarm->cq, "alarm"); + gpr_free(alarm); +} diff --git a/src/core/surface/surface_trace.c b/src/core/surface/api_trace.c index 57a0053162..9f0b900d46 100644 --- a/src/core/surface/surface_trace.c +++ b/src/core/surface/api_trace.c @@ -31,6 +31,6 @@ * */ -#include "src/core/surface/surface_trace.h" +#include "src/core/surface/api_trace.h" -int grpc_surface_trace = 0; +int grpc_api_trace = 0; diff --git a/src/core/surface/api_trace.h b/src/core/surface/api_trace.h new file mode 100644 index 0000000000..82bbf3b62b --- /dev/null +++ b/src/core/surface/api_trace.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_SURFACE_API_TRACE_H +#define GRPC_INTERNAL_CORE_SURFACE_API_TRACE_H + +#include "src/core/debug/trace.h" +#include <grpc/support/log.h> + +extern int grpc_api_trace; + +/* Provide unwrapping macros because we're in C89 and variadic macros weren't + introduced until C99... */ +#define GRPC_API_TRACE_UNWRAP0() +#define GRPC_API_TRACE_UNWRAP1(a) , a +#define GRPC_API_TRACE_UNWRAP2(a, b) , a, b +#define GRPC_API_TRACE_UNWRAP3(a, b, c) , a, b, c +#define GRPC_API_TRACE_UNWRAP4(a, b, c, d) , a, b, c, d +#define GRPC_API_TRACE_UNWRAP5(a, b, c, d, e) , a, b, c, d, e +#define GRPC_API_TRACE_UNWRAP6(a, b, c, d, e, f) , a, b, c, d, e, f +#define GRPC_API_TRACE_UNWRAP7(a, b, c, d, e, f, g) , a, b, c, d, e, f, g +#define GRPC_API_TRACE_UNWRAP8(a, b, c, d, e, f, g, h) , a, b, c, d, e, f, g, h +#define GRPC_API_TRACE_UNWRAP9(a, b, c, d, e, f, g, h, i) \ + , a, b, c, d, e, f, g, h, i +#define GRPC_API_TRACE_UNWRAP10(a, b, c, d, e, f, g, h, i, j) \ + , a, b, c, d, e, f, g, h, i, j + +/* Due to the limitations of C89's preprocessor, the arity of the var-arg list + 'nargs' must be specified. */ +#define GRPC_API_TRACE(fmt, nargs, args) \ + if (grpc_api_trace) { \ + gpr_log(GPR_INFO, fmt GRPC_API_TRACE_UNWRAP##nargs args); \ + } + +#endif /* GRPC_INTERNAL_CORE_SURFACE_API_TRACE_H */ diff --git a/src/core/surface/byte_buffer.c b/src/core/surface/byte_buffer.c index a930949f2d..fb39c4531d 100644 --- a/src/core/surface/byte_buffer.c +++ b/src/core/surface/byte_buffer.c @@ -75,9 +75,7 @@ grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) { return grpc_raw_byte_buffer_create(bb->data.raw.slice_buffer.slices, bb->data.raw.slice_buffer.count); } - gpr_log(GPR_INFO, "should never get here"); - abort(); - return NULL; + GPR_UNREACHABLE_CODE(return NULL); } void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) { @@ -95,6 +93,5 @@ size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) { case GRPC_BB_RAW: return bb->data.raw.slice_buffer.length; } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); + GPR_UNREACHABLE_CODE(return 0); } diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 4426bbbce9..81ff215c0c 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -42,9 +42,10 @@ #include <grpc/support/useful.h> #include "src/core/channel/channel_stack.h" -#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/timer.h" #include "src/core/profiling/timers.h" #include "src/core/support/string.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/byte_buffer_queue.h" #include "src/core/surface/call.h" #include "src/core/surface/channel.h" @@ -61,18 +62,6 @@ - status/close recv (depending on client/server) */ #define MAX_CONCURRENT_COMPLETIONS 6 -typedef enum { REQ_INITIAL = 0, REQ_READY, REQ_DONE } req_state; - -typedef enum { - SEND_NOTHING, - SEND_INITIAL_METADATA, - SEND_BUFFERED_INITIAL_METADATA, - SEND_MESSAGE, - SEND_BUFFERED_MESSAGE, - SEND_TRAILING_METADATA_AND_FINISH, - SEND_FINISH -} send_action; - typedef struct { grpc_ioreq_completion_func on_complete; void *user_data; @@ -175,8 +164,6 @@ struct grpc_call { gpr_uint8 bound_pollset; /* is an error status set */ gpr_uint8 error_status_set; - /** should the alarm be cancelled */ - gpr_uint8 cancel_alarm; /** bitmask of allocated completion events in completions */ gpr_uint8 allocated_completions; /** flag indicating that cancellation is inherited */ @@ -194,15 +181,15 @@ struct grpc_call { request_set[op] is an integer specifying a set of operations to which the request belongs: - - if it is < GRPC_IOREQ_OP_COUNT, then this operation is pending - completion, and the integer represents to which group of operations - the ioreq belongs. Each group is represented by one master, and the - integer in request_set is an index into masters to find the master - data. - - if it is REQSET_EMPTY, the ioreq op is inactive and available to be - started - - finally, if request_set[op] is REQSET_DONE, then the operation is - complete and unavailable to be started again + - if it is < GRPC_IOREQ_OP_COUNT, then this operation is pending + completion, and the integer represents to which group of operations + the ioreq belongs. Each group is represented by one master, and the + integer in request_set is an index into masters to find the master + data. + - if it is REQSET_EMPTY, the ioreq op is inactive and available to be + started + - finally, if request_set[op] is REQSET_DONE, then the operation is + complete and unavailable to be started again request_data[op] is the request data as supplied by the initiator of a request, and is valid iff request_set[op] <= GRPC_IOREQ_OP_COUNT. @@ -250,7 +237,7 @@ struct grpc_call { grpc_call_context_element context[GRPC_CONTEXT_COUNT]; /* Deadline alarm - if have_alarm is non-zero */ - grpc_alarm alarm; + grpc_timer alarm; /* Call refcount - to keep the call alive during asynchronous operations */ gpr_refcount internal_refcount; @@ -268,10 +255,10 @@ struct grpc_call { gpr_slice_buffer incoming_message; gpr_uint32 incoming_message_length; gpr_uint32 incoming_message_flags; - grpc_iomgr_closure destroy_closure; - grpc_iomgr_closure on_done_recv; - grpc_iomgr_closure on_done_send; - grpc_iomgr_closure on_done_bind; + grpc_closure destroy_closure; + grpc_closure on_done_recv; + grpc_closure on_done_send; + grpc_closure on_done_bind; /** completion events - for completion queue use */ grpc_cq_completion completions[MAX_CONCURRENT_COMPLETIONS]; @@ -290,19 +277,22 @@ struct grpc_call { #define CALL_FROM_TOP_ELEM(top_elem) \ CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem)) -static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline); -static void call_on_done_recv(void *call, int success); -static void call_on_done_send(void *call, int success); +static void set_deadline_alarm(grpc_exec_ctx *exec_ctx, grpc_call *call, + gpr_timespec deadline); +static void call_on_done_recv(grpc_exec_ctx *exec_ctx, void *call, int success); +static void call_on_done_send(grpc_exec_ctx *exec_ctx, void *call, int success); static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op); -static void execute_op(grpc_call *call, grpc_transport_stream_op *op); -static void recv_metadata(grpc_call *call, grpc_metadata_batch *metadata); +static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_transport_stream_op *op); +static void recv_metadata(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_metadata_batch *metadata); static void finish_read_ops(grpc_call *call); static grpc_call_error cancel_with_status(grpc_call *c, grpc_status_code status, const char *description); -static void finished_loose_op(void *call, int success); +static void finished_loose_op(grpc_exec_ctx *exec_ctx, void *call, int success); static void lock(grpc_call *call); -static void unlock(grpc_call *call); +static void unlock(grpc_exec_ctx *exec_ctx, grpc_call *call); grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask, @@ -315,8 +305,10 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, grpc_transport_stream_op initial_op; grpc_transport_stream_op *initial_op_ptr = NULL; grpc_channel_stack *channel_stack = grpc_channel_get_channel_stack(channel); - grpc_call *call = - gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_call *call; + GPR_TIMER_BEGIN("grpc_call_create", 0); + call = gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size); memset(call, 0, sizeof(grpc_call)); gpr_mu_init(&call->mu); gpr_mu_init(&call->completion_mu); @@ -345,9 +337,9 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, grpc_sopb_init(&call->send_ops); grpc_sopb_init(&call->recv_ops); gpr_slice_buffer_init(&call->incoming_message); - grpc_iomgr_closure_init(&call->on_done_recv, call_on_done_recv, call); - grpc_iomgr_closure_init(&call->on_done_send, call_on_done_send, call); - grpc_iomgr_closure_init(&call->on_done_bind, finished_loose_op, call); + grpc_closure_init(&call->on_done_recv, call_on_done_recv, call); + grpc_closure_init(&call->on_done_send, call_on_done_send, call); + grpc_closure_init(&call->on_done_bind, finished_loose_op, call); /* dropped in destroy and when READ_STATE_STREAM_CLOSED received */ gpr_ref_init(&call->internal_refcount, 2); /* server hack: start reads immediately so we can get initial metadata. @@ -362,8 +354,8 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, GRPC_CALL_INTERNAL_REF(call, "receiving"); initial_op_ptr = &initial_op; } - grpc_call_stack_init(channel_stack, server_transport_data, initial_op_ptr, - CALL_STACK_FROM_CALL(call)); + grpc_call_stack_init(&exec_ctx, channel_stack, server_transport_data, + initial_op_ptr, CALL_STACK_FROM_CALL(call)); if (parent_call != NULL) { GRPC_CALL_INTERNAL_REF(parent_call, "child"); GPR_ASSERT(call->is_client); @@ -407,19 +399,21 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, } if (gpr_time_cmp(send_deadline, gpr_inf_future(send_deadline.clock_type)) != 0) { - set_deadline_alarm(call, send_deadline); + set_deadline_alarm(&exec_ctx, call, send_deadline); } + grpc_exec_ctx_finish(&exec_ctx); + GPR_TIMER_END("grpc_call_create", 0); return call; } -void grpc_call_set_completion_queue(grpc_call *call, +void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call, grpc_completion_queue *cq) { lock(call); call->cq = cq; if (cq) { GRPC_CQ_INTERNAL_REF(cq, "bind"); } - unlock(call); + unlock(exec_ctx, call); } grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call) { @@ -433,20 +427,27 @@ static grpc_cq_completion *allocate_completion(grpc_call *call) { if (call->allocated_completions & (1u << i)) { continue; } - call->allocated_completions |= 1u << i; + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + call->allocated_completions = + (gpr_uint8)(call->allocated_completions | (1u << i)); gpr_mu_unlock(&call->completion_mu); return &call->completions[i]; } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); + GPR_UNREACHABLE_CODE(return NULL); + return NULL; } -static void done_completion(void *call, grpc_cq_completion *completion) { +static void done_completion(grpc_exec_ctx *exec_ctx, void *call, + grpc_cq_completion *completion) { grpc_call *c = call; gpr_mu_lock(&c->completion_mu); - c->allocated_completions &= ~(1u << (completion - c->completions)); + c->allocated_completions &= + (gpr_uint8) ~(1u << (completion - c->completions)); gpr_mu_unlock(&c->completion_mu); - GRPC_CALL_INTERNAL_UNREF(c, "completion", 1); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, c, "completion"); } #ifdef GRPC_CALL_REF_COUNT_DEBUG @@ -459,11 +460,12 @@ void grpc_call_internal_ref(grpc_call *c) { gpr_ref(&c->internal_refcount); } -static void destroy_call(void *call, int ignored_success) { +static void destroy_call(grpc_exec_ctx *exec_ctx, grpc_call *call) { size_t i; grpc_call *c = call; - grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c)); - GRPC_CHANNEL_INTERNAL_UNREF(c->channel, "call"); + GPR_TIMER_BEGIN("destroy_call", 0); + grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c)); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->channel, "call"); gpr_mu_destroy(&c->mu); gpr_mu_destroy(&c->completion_mu); for (i = 0; i < STATUS_SOURCE_COUNT; i++) { @@ -494,24 +496,19 @@ static void destroy_call(void *call, int ignored_success) { GRPC_CQ_INTERNAL_UNREF(c->cq, "bind"); } gpr_free(c); + GPR_TIMER_END("destroy_call", 0); } #ifdef GRPC_CALL_REF_COUNT_DEBUG -void grpc_call_internal_unref(grpc_call *c, const char *reason, - int allow_immediate_deletion) { +void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c, + const char *reason) { gpr_log(GPR_DEBUG, "CALL: unref %p %d -> %d [%s]", c, c->internal_refcount.count, c->internal_refcount.count - 1, reason); #else -void grpc_call_internal_unref(grpc_call *c, int allow_immediate_deletion) { +void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c) { #endif if (gpr_unref(&c->internal_refcount)) { - if (allow_immediate_deletion) { - destroy_call(c, 1); - } else { - c->destroy_closure.cb = destroy_call; - c->destroy_closure.cb_arg = c; - grpc_iomgr_add_callback(&c->destroy_closure); - } + destroy_call(exec_ctx, c); } } @@ -520,7 +517,7 @@ static void set_status_code(grpc_call *call, status_source source, if (call->status[source].is_set) return; call->status[source].is_set = 1; - call->status[source].code = status; + call->status[source].code = (grpc_status_code)status; call->error_status_set = status != GRPC_STATUS_OK; if (status != GRPC_STATUS_OK && !grpc_bbq_empty(&call->incoming_queue)) { @@ -533,19 +530,35 @@ static void set_compression_algorithm(grpc_call *call, call->compression_algorithm = algo; } -grpc_compression_algorithm grpc_call_get_compression_algorithm( - const grpc_call *call) { - return call->compression_algorithm; +grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm( + grpc_call *call) { + grpc_compression_algorithm algorithm; + gpr_mu_lock(&call->mu); + algorithm = call->compression_algorithm; + gpr_mu_unlock(&call->mu); + return algorithm; } -static void set_encodings_accepted_by_peer( - grpc_call *call, const gpr_slice accept_encoding_slice) { +static void destroy_encodings_accepted_by_peer(void *p) { return; } + +static void set_encodings_accepted_by_peer(grpc_call *call, grpc_mdelem *mdel) { size_t i; grpc_compression_algorithm algorithm; gpr_slice_buffer accept_encoding_parts; + gpr_slice accept_encoding_slice; + void *accepted_user_data; + + accepted_user_data = + grpc_mdelem_get_user_data(mdel, destroy_encodings_accepted_by_peer); + if (accepted_user_data != NULL) { + call->encodings_accepted_by_peer = + (gpr_uint32)(((gpr_uintptr)accepted_user_data) - 1); + return; + } + accept_encoding_slice = mdel->value->slice; gpr_slice_buffer_init(&accept_encoding_parts); - gpr_slice_split(accept_encoding_slice, ", ", &accept_encoding_parts); + gpr_slice_split(accept_encoding_slice, ",", &accept_encoding_parts); /* No need to zero call->encodings_accepted_by_peer: grpc_call_create already * zeroes the whole grpc_call */ @@ -567,14 +580,28 @@ static void set_encodings_accepted_by_peer( gpr_free(accept_encoding_entry_str); } } + + gpr_slice_buffer_destroy(&accept_encoding_parts); + + grpc_mdelem_set_user_data( + mdel, destroy_encodings_accepted_by_peer, + (void *)(((gpr_uintptr)call->encodings_accepted_by_peer) + 1)); } -gpr_uint32 grpc_call_get_encodings_accepted_by_peer(grpc_call *call) { - return call->encodings_accepted_by_peer; +gpr_uint32 grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call) { + gpr_uint32 encodings_accepted_by_peer; + gpr_mu_lock(&call->mu); + encodings_accepted_by_peer = call->encodings_accepted_by_peer; + gpr_mu_unlock(&call->mu); + return encodings_accepted_by_peer; } -gpr_uint32 grpc_call_get_message_flags(const grpc_call *call) { - return call->incoming_message_flags; +gpr_uint32 grpc_call_test_only_get_message_flags(grpc_call *call) { + gpr_uint32 flags; + gpr_mu_lock(&call->mu); + flags = call->incoming_message_flags; + gpr_mu_unlock(&call->mu); + return flags; } static void set_status_details(grpc_call *call, status_source source, @@ -610,15 +637,16 @@ static int need_more_data(grpc_call *call) { (call->cancel_with_status != GRPC_STATUS_OK) || call->destroy_called; } -static void unlock(grpc_call *call) { +static void unlock(grpc_exec_ctx *exec_ctx, grpc_call *call) { grpc_transport_stream_op op; completed_request completed_requests[GRPC_IOREQ_OP_COUNT]; int completing_requests = 0; int start_op = 0; int i; - const gpr_uint32 MAX_RECV_PEEK_AHEAD = 65536; + const size_t MAX_RECV_PEEK_AHEAD = 65536; size_t buffered_bytes; - int cancel_alarm = 0; + + GPR_TIMER_BEGIN("unlock", 0); memset(&op, 0, sizeof(op)); @@ -626,13 +654,7 @@ static void unlock(grpc_call *call) { start_op = op.cancel_with_status != GRPC_STATUS_OK; call->cancel_with_status = GRPC_STATUS_OK; /* reset */ - cancel_alarm = call->cancel_alarm; - call->cancel_alarm = 0; - if (!call->receiving && need_more_data(call)) { - op.recv_ops = &call->recv_ops; - op.recv_state = &call->recv_state; - op.on_done_recv = &call->on_done_recv; if (grpc_bbq_empty(&call->incoming_queue) && call->reading_message) { op.max_recv_bytes = call->incoming_message_length - call->incoming_message.length + MAX_RECV_PEEK_AHEAD; @@ -644,9 +666,16 @@ static void unlock(grpc_call *call) { op.max_recv_bytes = MAX_RECV_PEEK_AHEAD - buffered_bytes; } } - call->receiving = 1; - GRPC_CALL_INTERNAL_REF(call, "receiving"); - start_op = 1; + /* TODO(ctiller): 1024 is basically to cover a bug + I don't understand yet */ + if (op.max_recv_bytes > 1024) { + op.recv_ops = &call->recv_ops; + op.recv_state = &call->recv_state; + op.on_done_recv = &call->on_done_recv; + call->receiving = 1; + GRPC_CALL_INTERNAL_REF(call, "receiving"); + start_op = 1; + } } if (!call->sending) { @@ -674,24 +703,23 @@ static void unlock(grpc_call *call) { gpr_mu_unlock(&call->mu); - if (cancel_alarm) { - grpc_alarm_cancel(&call->alarm); - } - if (start_op) { - execute_op(call, &op); + execute_op(exec_ctx, call, &op); } if (completing_requests > 0) { for (i = 0; i < completing_requests; i++) { - completed_requests[i].on_complete(call, completed_requests[i].success, + completed_requests[i].on_complete(exec_ctx, call, + completed_requests[i].success, completed_requests[i].user_data); } lock(call); call->completing = 0; - unlock(call); - GRPC_CALL_INTERNAL_UNREF(call, "completing", 0); + unlock(exec_ctx, call); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completing"); } + + GPR_TIMER_END("unlock", 0); } static void get_final_status(grpc_call *call, grpc_ioreq_data out) { @@ -751,7 +779,11 @@ static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op, size_t i; /* ioreq is live: we need to do something */ master = &call->masters[master_set]; - master->complete_mask |= 1u << op; + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + master->complete_mask = (gpr_uint16)(master->complete_mask | (1u << op)); if (!success) { master->success = 0; } @@ -835,8 +867,9 @@ static void early_out_write_ops(grpc_call *call) { } } -static void call_on_done_send(void *pc, int success) { +static void call_on_done_send(grpc_exec_ctx *exec_ctx, void *pc, int success) { grpc_call *call = pc; + GPR_TIMER_BEGIN("call_on_done_send", 0); lock(call); if (call->last_send_contains & (1 << GRPC_IOREQ_SEND_INITIAL_METADATA)) { finish_ioreq_op(call, GRPC_IOREQ_SEND_INITIAL_METADATA, success); @@ -858,11 +891,13 @@ static void call_on_done_send(void *pc, int success) { call->send_ops.nops = 0; call->last_send_contains = 0; call->sending = 0; - unlock(call); - GRPC_CALL_INTERNAL_UNREF(call, "sending", 0); + unlock(exec_ctx, call); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "sending"); + GPR_TIMER_END("call_on_done_send", 0); } static void finish_message(grpc_call *call) { + GPR_TIMER_BEGIN("finish_message", 0); if (call->error_status_set == 0) { /* TODO(ctiller): this could be a lot faster if coded directly */ grpc_byte_buffer *byte_buffer; @@ -882,6 +917,7 @@ static void finish_message(grpc_call *call) { gpr_slice_buffer_reset_and_unref(&call->incoming_message); GPR_ASSERT(call->incoming_message.count == 0); call->reading_message = 0; + GPR_TIMER_END("finish_message", 0); } static int begin_message(grpc_call *call, grpc_begin_message msg) { @@ -942,6 +978,7 @@ static int add_slice_to_message(grpc_call *call, gpr_slice slice) { } /* we have to be reading a message to know what to do here */ if (!call->reading_message) { + gpr_slice_unref(slice); cancel_with_status(call, GRPC_STATUS_INVALID_ARGUMENT, "Received payload data while not reading a message"); return 0; @@ -965,12 +1002,12 @@ static int add_slice_to_message(grpc_call *call, gpr_slice slice) { } } -static void call_on_done_recv(void *pc, int success) { +static void call_on_done_recv(grpc_exec_ctx *exec_ctx, void *pc, int success) { grpc_call *call = pc; grpc_call *child_call; grpc_call *next_child_call; size_t i; - GRPC_TIMER_BEGIN(GRPC_PTAG_CALL_ON_DONE_RECV, 0); + GPR_TIMER_BEGIN("call_on_done_recv", 0); lock(call); call->receiving = 0; if (success) { @@ -980,13 +1017,19 @@ static void call_on_done_recv(void *pc, int success) { case GRPC_NO_OP: break; case GRPC_OP_METADATA: - recv_metadata(call, &op->data.metadata); + GPR_TIMER_BEGIN("recv_metadata", 0); + recv_metadata(exec_ctx, call, &op->data.metadata); + GPR_TIMER_END("recv_metadata", 0); break; case GRPC_OP_BEGIN_MESSAGE: + GPR_TIMER_BEGIN("begin_message", 0); success = begin_message(call, op->data.begin_message); + GPR_TIMER_END("begin_message", 0); break; case GRPC_OP_SLICE: + GPR_TIMER_BEGIN("add_slice_to_message", 0); success = add_slice_to_message(call, op->data.slice); + GPR_TIMER_END("add_slice_to_message", 0); break; } } @@ -1001,7 +1044,9 @@ static void call_on_done_recv(void *pc, int success) { if (call->recv_state == GRPC_STREAM_CLOSED) { GPR_ASSERT(call->read_state <= READ_STATE_STREAM_CLOSED); call->read_state = READ_STATE_STREAM_CLOSED; - call->cancel_alarm |= call->have_alarm; + if (call->have_alarm) { + grpc_timer_cancel(exec_ctx, &call->alarm); + } /* propagate cancellation to any interested children */ child_call = call->first_child; if (child_call != NULL) { @@ -1010,12 +1055,12 @@ static void call_on_done_recv(void *pc, int success) { if (child_call->cancellation_is_inherited) { GRPC_CALL_INTERNAL_REF(child_call, "propagate_cancel"); grpc_call_cancel(child_call, NULL); - GRPC_CALL_INTERNAL_UNREF(child_call, "propagate_cancel", 0); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, child_call, "propagate_cancel"); } child_call = next_child_call; } while (child_call != call->first_child); } - GRPC_CALL_INTERNAL_UNREF(call, "closed", 0); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "closed"); } finish_read_ops(call); } else { @@ -1027,10 +1072,10 @@ static void call_on_done_recv(void *pc, int success) { finish_ioreq_op(call, GRPC_IOREQ_RECV_STATUS_DETAILS, 0); } call->recv_ops.nops = 0; - unlock(call); + unlock(exec_ctx, call); - GRPC_CALL_INTERNAL_UNREF(call, "receiving", 0); - GRPC_TIMER_END(GRPC_PTAG_CALL_ON_DONE_RECV, 0); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "receiving"); + GPR_TIMER_END("call_on_done_recv", 0); } static int prepare_application_metadata(grpc_call *call, size_t count, @@ -1044,7 +1089,7 @@ static int prepare_application_metadata(grpc_call *call, size_t count, GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data)); l->md = grpc_mdelem_from_string_and_buffer(call->metadata_context, md->key, (const gpr_uint8 *)md->value, - md->value_length, 1); + md->value_length); if (!grpc_mdstr_is_legal_header(l->md->key)) { gpr_log(GPR_ERROR, "attempt to send invalid metadata key: %s", grpc_mdstr_as_c_string(l->md->key)); @@ -1115,10 +1160,12 @@ static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op) { /* fall through intended */ case WRITE_STATE_STARTED: if (is_op_live(call, GRPC_IOREQ_SEND_MESSAGE)) { + size_t length; data = call->request_data[GRPC_IOREQ_SEND_MESSAGE]; flags = call->request_flags[GRPC_IOREQ_SEND_MESSAGE]; - grpc_sopb_add_begin_message( - &call->send_ops, grpc_byte_buffer_length(data.send_message), flags); + length = grpc_byte_buffer_length(data.send_message); + GPR_ASSERT(length <= GPR_UINT32_MAX); + grpc_sopb_add_begin_message(&call->send_ops, (gpr_uint32)length, flags); copy_byte_buffer_to_stream_ops(data.send_message, &call->send_ops); op->send_ops = &call->send_ops; call->last_send_contains |= 1 << GRPC_IOREQ_SEND_MESSAGE; @@ -1220,7 +1267,7 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs, grpc_ioreq_completion_func completion, void *user_data) { size_t i; - gpr_uint32 have_ops = 0; + gpr_uint16 have_ops = 0; grpc_ioreq_op op; reqinfo_master *master; grpc_ioreq_data data; @@ -1251,13 +1298,17 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs, } if (op == GRPC_IOREQ_SEND_STATUS) { set_status_code(call, STATUS_FROM_SERVER_STATUS, - reqs[i].data.send_status.code); + (gpr_uint32)reqs[i].data.send_status.code); if (reqs[i].data.send_status.details) { set_status_details(call, STATUS_FROM_SERVER_STATUS, GRPC_MDSTR_REF(reqs[i].data.send_status.details)); } } - have_ops |= 1u << op; + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + have_ops = (gpr_uint16)(have_ops | (1u << op)); call->request_data[op] = data; call->request_flags[op] = reqs[i].flags; @@ -1278,18 +1329,21 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs, } grpc_call_error grpc_call_start_ioreq_and_call_back( - grpc_call *call, const grpc_ioreq *reqs, size_t nreqs, - grpc_ioreq_completion_func on_complete, void *user_data) { + grpc_exec_ctx *exec_ctx, grpc_call *call, const grpc_ioreq *reqs, + size_t nreqs, grpc_ioreq_completion_func on_complete, void *user_data) { grpc_call_error err; lock(call); err = start_ioreq(call, reqs, nreqs, on_complete, user_data); - unlock(call); + unlock(exec_ctx, call); return err; } void grpc_call_destroy(grpc_call *c) { int cancel; grpc_call *parent = c->parent; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_call_destroy(c=%p)", 1, (c)); if (parent) { gpr_mu_lock(&parent->mu); @@ -1302,20 +1356,24 @@ void grpc_call_destroy(grpc_call *c) { c->sibling_next->sibling_prev = c->sibling_prev; } gpr_mu_unlock(&parent->mu); - GRPC_CALL_INTERNAL_UNREF(parent, "child", 1); + GRPC_CALL_INTERNAL_UNREF(&exec_ctx, parent, "child"); } lock(c); GPR_ASSERT(!c->destroy_called); c->destroy_called = 1; - c->cancel_alarm |= c->have_alarm; + if (c->have_alarm) { + grpc_timer_cancel(&exec_ctx, &c->alarm); + } cancel = c->read_state != READ_STATE_STREAM_CLOSED; - unlock(c); + unlock(&exec_ctx, c); if (cancel) grpc_call_cancel(c, NULL); - GRPC_CALL_INTERNAL_UNREF(c, "destroy", 1); + GRPC_CALL_INTERNAL_UNREF(&exec_ctx, c, "destroy"); + grpc_exec_ctx_finish(&exec_ctx); } grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved) { + GRPC_API_TRACE("grpc_call_cancel(call=%p, reserved=%p)", 2, (call, reserved)); GPR_ASSERT(!reserved); return grpc_call_cancel_with_status(call, GRPC_STATUS_CANCELLED, "Cancelled", NULL); @@ -1326,22 +1384,28 @@ grpc_call_error grpc_call_cancel_with_status(grpc_call *c, const char *description, void *reserved) { grpc_call_error r; - (void)reserved; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE( + "grpc_call_cancel_with_status(" + "c=%p, status=%d, description=%s, reserved=%p)", + 4, (c, (int)status, description, reserved)); + GPR_ASSERT(reserved == NULL); lock(c); r = cancel_with_status(c, status, description); - unlock(c); + unlock(&exec_ctx, c); + grpc_exec_ctx_finish(&exec_ctx); return r; } static grpc_call_error cancel_with_status(grpc_call *c, grpc_status_code status, const char *description) { grpc_mdstr *details = - description ? grpc_mdstr_from_string(c->metadata_context, description, 0) + description ? grpc_mdstr_from_string(c->metadata_context, description) : NULL; GPR_ASSERT(status != GRPC_STATUS_OK); - set_status_code(c, STATUS_FROM_API_OVERRIDE, status); + set_status_code(c, STATUS_FROM_API_OVERRIDE, (gpr_uint32)status); set_status_details(c, STATUS_FROM_API_OVERRIDE, details); c->cancel_with_status = status; @@ -1349,22 +1413,25 @@ static grpc_call_error cancel_with_status(grpc_call *c, grpc_status_code status, return GRPC_CALL_OK; } -static void finished_loose_op(void *call, int success_ignored) { - GRPC_CALL_INTERNAL_UNREF(call, "loose-op", 0); +static void finished_loose_op(grpc_exec_ctx *exec_ctx, void *call, + int success_ignored) { + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "loose-op"); } typedef struct { grpc_call *call; - grpc_iomgr_closure closure; + grpc_closure closure; } finished_loose_op_allocated_args; -static void finished_loose_op_allocated(void *alloc, int success) { +static void finished_loose_op_allocated(grpc_exec_ctx *exec_ctx, void *alloc, + int success) { finished_loose_op_allocated_args *args = alloc; - finished_loose_op(args->call, success); + finished_loose_op(exec_ctx, args->call, success); gpr_free(args); } -static void execute_op(grpc_call *call, grpc_transport_stream_op *op) { +static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_transport_stream_op *op) { grpc_call_element *elem; GPR_ASSERT(op->on_consumed == NULL); @@ -1375,27 +1442,30 @@ static void execute_op(grpc_call *call, grpc_transport_stream_op *op) { } else { finished_loose_op_allocated_args *args = gpr_malloc(sizeof(*args)); args->call = call; - grpc_iomgr_closure_init(&args->closure, finished_loose_op_allocated, - args); + grpc_closure_init(&args->closure, finished_loose_op_allocated, args); op->on_consumed = &args->closure; } } elem = CALL_ELEM_FROM_CALL(call, 0); op->context = call->context; - elem->filter->start_transport_stream_op(elem, op); + elem->filter->start_transport_stream_op(exec_ctx, elem, op); } char *grpc_call_get_peer(grpc_call *call) { grpc_call_element *elem = CALL_ELEM_FROM_CALL(call, 0); - return elem->filter->get_peer(elem); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + char *result = elem->filter->get_peer(&exec_ctx, elem); + GRPC_API_TRACE("grpc_call_get_peer(%p)", 1, (call)); + grpc_exec_ctx_finish(&exec_ctx); + return result; } grpc_call *grpc_call_from_top_element(grpc_call_element *elem) { return CALL_FROM_TOP_ELEM(elem); } -static void call_alarm(void *arg, int success) { +static void call_alarm(grpc_exec_ctx *exec_ctx, void *arg, int success) { grpc_call *call = arg; lock(call); call->have_alarm = 0; @@ -1404,11 +1474,12 @@ static void call_alarm(void *arg, int success) { "Deadline Exceeded"); } finish_read_ops(call); - unlock(call); - GRPC_CALL_INTERNAL_UNREF(call, "alarm", 1); + unlock(exec_ctx, call); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "alarm"); } -static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline) { +static void set_deadline_alarm(grpc_exec_ctx *exec_ctx, grpc_call *call, + gpr_timespec deadline) { if (call->have_alarm) { gpr_log(GPR_ERROR, "Attempt to set deadline alarm twice"); assert(0); @@ -1417,7 +1488,7 @@ static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline) { GRPC_CALL_INTERNAL_REF(call, "alarm"); call->have_alarm = 1; call->send_deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); - grpc_alarm_init(&call->alarm, call->send_deadline, call_alarm, call, + grpc_timer_init(exec_ctx, &call->alarm, call->send_deadline, call_alarm, call, gpr_now(GPR_CLOCK_MONOTONIC)); } @@ -1469,28 +1540,37 @@ static gpr_uint32 decode_compression(grpc_mdelem *md) { return algorithm; } -static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) { +static void recv_metadata(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_metadata_batch *md) { grpc_linked_mdelem *l; grpc_metadata_array *dest; grpc_metadata *mdusr; int is_trailing; - grpc_mdctx *mdctx = call->metadata_context; is_trailing = call->read_state >= READ_STATE_GOT_INITIAL_METADATA; for (l = md->list.head; l != NULL; l = l->next) { - grpc_mdelem *md = l->md; - grpc_mdstr *key = md->key; + grpc_mdelem *mdel = l->md; + grpc_mdstr *key = mdel->key; if (key == grpc_channel_get_status_string(call->channel)) { - set_status_code(call, STATUS_FROM_WIRE, decode_status(md)); + GPR_TIMER_BEGIN("status", 0); + set_status_code(call, STATUS_FROM_WIRE, decode_status(mdel)); + GPR_TIMER_END("status", 0); } else if (key == grpc_channel_get_message_string(call->channel)) { - set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(md->value)); + GPR_TIMER_BEGIN("status-details", 0); + set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(mdel->value)); + GPR_TIMER_END("status-details", 0); } else if (key == grpc_channel_get_compression_algorithm_string(call->channel)) { - set_compression_algorithm(call, decode_compression(md)); + GPR_TIMER_BEGIN("compression_algorithm", 0); + set_compression_algorithm(call, decode_compression(mdel)); + GPR_TIMER_END("compression_algorithm", 0); } else if (key == grpc_channel_get_encodings_accepted_by_peer_string( call->channel)) { - set_encodings_accepted_by_peer(call, md->value->slice); + GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0); + set_encodings_accepted_by_peer(call, mdel); + GPR_TIMER_END("encodings_accepted_by_peer", 0); } else { + GPR_TIMER_BEGIN("report_up", 0); dest = &call->buffered_metadata[is_trailing]; if (dest->count == dest->capacity) { dest->capacity = GPR_MAX(dest->capacity + 8, dest->capacity * 2); @@ -1498,9 +1578,9 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) { gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity); } mdusr = &dest->metadata[dest->count++]; - mdusr->key = grpc_mdstr_as_c_string(md->key); - mdusr->value = grpc_mdstr_as_c_string(md->value); - mdusr->value_length = GPR_SLICE_LENGTH(md->value->slice); + mdusr->key = grpc_mdstr_as_c_string(mdel->key); + mdusr->value = grpc_mdstr_as_c_string(mdel->value); + mdusr->value_length = GPR_SLICE_LENGTH(mdel->value->slice); if (call->owned_metadata_count == call->owned_metadata_capacity) { call->owned_metadata_capacity = GPR_MAX(call->owned_metadata_capacity + 8, @@ -1509,27 +1589,28 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) { gpr_realloc(call->owned_metadata, sizeof(grpc_mdelem *) * call->owned_metadata_capacity); } - call->owned_metadata[call->owned_metadata_count++] = md; - l->md = 0; + call->owned_metadata[call->owned_metadata_count++] = mdel; + l->md = NULL; + GPR_TIMER_END("report_up", 0); } } if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) != 0 && !call->is_client) { - set_deadline_alarm(call, md->deadline); + GPR_TIMER_BEGIN("set_deadline_alarm", 0); + set_deadline_alarm(exec_ctx, call, md->deadline); + GPR_TIMER_END("set_deadline_alarm", 0); } if (!is_trailing) { call->read_state = READ_STATE_GOT_INITIAL_METADATA; } - grpc_mdctx_lock(mdctx); for (l = md->list.head; l; l = l->next) { - if (l->md) GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md); + if (l->md) GRPC_MDELEM_UNREF(l->md); } for (l = md->garbage.head; l; l = l->next) { - GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md); + GRPC_MDELEM_UNREF(l->md); } - grpc_mdctx_unlock(mdctx); } grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) { @@ -1548,13 +1629,15 @@ static void set_cancelled_value(grpc_status_code status, void *dest) { *(grpc_status_code *)dest = (status != GRPC_STATUS_OK); } -static void finish_batch(grpc_call *call, int success, void *tag) { - grpc_cq_end_op(call->cq, tag, success, done_completion, call, +static void finish_batch(grpc_exec_ctx *exec_ctx, grpc_call *call, int success, + void *tag) { + grpc_cq_end_op(exec_ctx, call->cq, tag, success, done_completion, call, allocate_completion(call)); } -static void finish_batch_with_close(grpc_call *call, int success, void *tag) { - grpc_cq_end_op(call->cq, tag, 1, done_completion, call, +static void finish_batch_with_close(grpc_exec_ctx *exec_ctx, grpc_call *call, + int success, void *tag) { + grpc_cq_end_op(exec_ctx, call->cq, tag, 1, done_completion, call, allocate_completion(call)); } @@ -1573,30 +1656,51 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, size_t out; const grpc_op *op; grpc_ioreq *req; - void (*finish_func)(grpc_call *, int, void *) = finish_batch; + void (*finish_func)(grpc_exec_ctx *, grpc_call *, int, void *) = finish_batch; + grpc_call_error error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GPR_TIMER_BEGIN("grpc_call_start_batch", 0); - if (reserved != NULL) return GRPC_CALL_ERROR; + GRPC_API_TRACE( + "grpc_call_start_batch(call=%p, ops=%p, nops=%lu, tag=%p, reserved=%p)", + 5, (call, ops, (unsigned long)nops, tag, reserved)); + + if (reserved != NULL) { + error = GRPC_CALL_ERROR; + goto done; + } GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, tag); if (nops == 0) { grpc_cq_begin_op(call->cq); GRPC_CALL_INTERNAL_REF(call, "completion"); - grpc_cq_end_op(call->cq, tag, 1, done_completion, call, + grpc_cq_end_op(&exec_ctx, call->cq, tag, 1, done_completion, call, allocate_completion(call)); - return GRPC_CALL_OK; + error = GRPC_CALL_OK; + goto done; } /* rewrite batch ops into ioreq ops */ for (in = 0, out = 0; in < nops; in++) { op = &ops[in]; - if (op->reserved != NULL) return GRPC_CALL_ERROR; + if (op->reserved != NULL) { + error = GRPC_CALL_ERROR; + goto done; + } switch (op->op) { case GRPC_OP_SEND_INITIAL_METADATA: /* Flag validation: currently allow no flags */ - if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done; + } req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_SEND_INITIAL_METADATA; req->data.send_metadata.count = op->data.send_initial_metadata.count; req->data.send_metadata.metadata = @@ -1605,36 +1709,55 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, break; case GRPC_OP_SEND_MESSAGE: if (!are_write_flags_valid(op->flags)) { - return GRPC_CALL_ERROR_INVALID_FLAGS; + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done; } if (op->data.send_message == NULL) { - return GRPC_CALL_ERROR_INVALID_MESSAGE; + error = GRPC_CALL_ERROR_INVALID_MESSAGE; + goto done; } req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_SEND_MESSAGE; req->data.send_message = op->data.send_message; req->flags = op->flags; break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: /* Flag validation: currently allow no flags */ - if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done; + } if (!call->is_client) { - return GRPC_CALL_ERROR_NOT_ON_SERVER; + error = GRPC_CALL_ERROR_NOT_ON_SERVER; + goto done; } req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_SEND_CLOSE; req->flags = op->flags; break; case GRPC_OP_SEND_STATUS_FROM_SERVER: /* Flag validation: currently allow no flags */ - if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done; + } if (call->is_client) { - return GRPC_CALL_ERROR_NOT_ON_CLIENT; + error = GRPC_CALL_ERROR_NOT_ON_CLIENT; + goto done; } req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_SEND_TRAILING_METADATA; req->flags = op->flags; req->data.send_metadata.count = @@ -1642,27 +1765,40 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, req->data.send_metadata.metadata = op->data.send_status_from_server.trailing_metadata; req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_SEND_STATUS; req->data.send_status.code = op->data.send_status_from_server.status; req->data.send_status.details = op->data.send_status_from_server.status_details != NULL ? grpc_mdstr_from_string( call->metadata_context, - op->data.send_status_from_server.status_details, 0) + op->data.send_status_from_server.status_details) : NULL; req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_SEND_CLOSE; break; case GRPC_OP_RECV_INITIAL_METADATA: /* Flag validation: currently allow no flags */ - if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done; + } if (!call->is_client) { - return GRPC_CALL_ERROR_NOT_ON_SERVER; + error = GRPC_CALL_ERROR_NOT_ON_SERVER; + goto done; } req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_RECV_INITIAL_METADATA; req->data.recv_metadata = op->data.recv_initial_metadata; req->data.recv_metadata->count = 0; @@ -1670,55 +1806,86 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, break; case GRPC_OP_RECV_MESSAGE: /* Flag validation: currently allow no flags */ - if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done; + } req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_RECV_MESSAGE; req->data.recv_message = op->data.recv_message; req->flags = op->flags; break; case GRPC_OP_RECV_STATUS_ON_CLIENT: /* Flag validation: currently allow no flags */ - if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done; + } if (!call->is_client) { - return GRPC_CALL_ERROR_NOT_ON_SERVER; + error = GRPC_CALL_ERROR_NOT_ON_SERVER; + goto done; } req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_RECV_STATUS; req->flags = op->flags; req->data.recv_status.set_value = set_status_value_directly; req->data.recv_status.user_data = op->data.recv_status_on_client.status; req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_RECV_STATUS_DETAILS; req->data.recv_status_details.details = op->data.recv_status_on_client.status_details; req->data.recv_status_details.details_capacity = op->data.recv_status_on_client.status_details_capacity; req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_RECV_TRAILING_METADATA; req->data.recv_metadata = op->data.recv_status_on_client.trailing_metadata; req->data.recv_metadata->count = 0; req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_RECV_CLOSE; finish_func = finish_batch_with_close; break; case GRPC_OP_RECV_CLOSE_ON_SERVER: /* Flag validation: currently allow no flags */ - if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done; + } req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_RECV_STATUS; req->flags = op->flags; req->data.recv_status.set_value = set_cancelled_value; req->data.recv_status.user_data = op->data.recv_close_on_server.cancelled; req = &reqs[out++]; - if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + if (out > GRPC_IOREQ_OP_COUNT) { + error = GRPC_CALL_ERROR_BATCH_TOO_BIG; + goto done; + } req->op = GRPC_IOREQ_RECV_CLOSE; finish_func = finish_batch_with_close; break; @@ -1728,7 +1895,12 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, GRPC_CALL_INTERNAL_REF(call, "completion"); grpc_cq_begin_op(call->cq); - return grpc_call_start_ioreq_and_call_back(call, reqs, out, finish_func, tag); + error = grpc_call_start_ioreq_and_call_back(&exec_ctx, call, reqs, out, + finish_func, tag); +done: + grpc_exec_ctx_finish(&exec_ctx); + GPR_TIMER_END("grpc_call_start_batch", 0); + return error; } void grpc_call_context_set(grpc_call *call, grpc_context_index elem, diff --git a/src/core/surface/call.h b/src/core/surface/call.h index 00638e43b5..9b7c6f9bfb 100644 --- a/src/core/surface/call.h +++ b/src/core/surface/call.h @@ -36,6 +36,8 @@ #include "src/core/channel/channel_stack.h" #include "src/core/channel/context.h" +#include "src/core/surface/api_trace.h" +#include "src/core/surface/surface_trace.h" #include <grpc/grpc.h> #ifdef __cplusplus @@ -82,11 +84,13 @@ typedef union { typedef struct { grpc_ioreq_op op; - gpr_uint32 flags; /**< A copy of the write flags from grpc_op */ + gpr_uint32 flags; + /**< A copy of the write flags from grpc_op */ grpc_ioreq_data data; } grpc_ioreq; -typedef void (*grpc_ioreq_completion_func)(grpc_call *call, int success, +typedef void (*grpc_ioreq_completion_func)(grpc_exec_ctx *exec_ctx, + grpc_call *call, int success, void *user_data); grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, @@ -97,36 +101,35 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, size_t add_initial_metadata_count, gpr_timespec send_deadline); -void grpc_call_set_completion_queue(grpc_call *call, grpc_completion_queue *cq); +void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_completion_queue *cq); grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call); #ifdef GRPC_CALL_REF_COUNT_DEBUG void grpc_call_internal_ref(grpc_call *call, const char *reason); -void grpc_call_internal_unref(grpc_call *call, const char *reason, - int allow_immediate_deletion); +void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *call, + const char *reason); #define GRPC_CALL_INTERNAL_REF(call, reason) \ grpc_call_internal_ref(call, reason) -#define GRPC_CALL_INTERNAL_UNREF(call, reason, allow_immediate_deletion) \ - grpc_call_internal_unref(call, reason, allow_immediate_deletion) +#define GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, reason) \ + grpc_call_internal_unref(exec_ctx, call, reason) #else void grpc_call_internal_ref(grpc_call *call); -void grpc_call_internal_unref(grpc_call *call, int allow_immediate_deletion); +void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *call); #define GRPC_CALL_INTERNAL_REF(call, reason) grpc_call_internal_ref(call) -#define GRPC_CALL_INTERNAL_UNREF(call, reason, allow_immediate_deletion) \ - grpc_call_internal_unref(call, allow_immediate_deletion) +#define GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, reason) \ + grpc_call_internal_unref(exec_ctx, call) #endif grpc_call_error grpc_call_start_ioreq_and_call_back( - grpc_call *call, const grpc_ioreq *reqs, size_t nreqs, - grpc_ioreq_completion_func on_complete, void *user_data); + grpc_exec_ctx *exec_ctx, grpc_call *call, const grpc_ioreq *reqs, + size_t nreqs, grpc_ioreq_completion_func on_complete, void *user_data); grpc_call_stack *grpc_call_get_call_stack(grpc_call *call); /* Given the top call_element, get the call object. */ grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element); -extern int grpc_trace_batch; - void grpc_call_log_batch(char *file, int line, gpr_log_severity severity, grpc_call *call, const grpc_op *ops, size_t nops, void *tag); @@ -152,31 +155,20 @@ void grpc_call_context_set(grpc_call *call, grpc_context_index elem, void *grpc_call_context_get(grpc_call *call, grpc_context_index elem); #define GRPC_CALL_LOG_BATCH(sev, call, ops, nops, tag) \ - if (grpc_trace_batch) grpc_call_log_batch(sev, call, ops, nops, tag) + if (grpc_api_trace) grpc_call_log_batch(sev, call, ops, nops, tag) #define GRPC_SERVER_LOG_REQUEST_CALL(sev, server, call, details, \ initial_metadata, cq_bound_to_call, \ cq_for_notifications, tag) \ - if (grpc_trace_batch) \ + if (grpc_api_trace) \ grpc_server_log_request_call(sev, server, call, details, initial_metadata, \ cq_bound_to_call, cq_for_notifications, tag) #define GRPC_SERVER_LOG_SHUTDOWN(sev, server, cq, tag) \ - if (grpc_trace_batch) grpc_server_log_shutdown(sev, server, cq, tag) + if (grpc_api_trace) grpc_server_log_shutdown(sev, server, cq, tag) gpr_uint8 grpc_call_is_client(grpc_call *call); -grpc_compression_algorithm grpc_call_get_compression_algorithm( - const grpc_call *call); - -gpr_uint32 grpc_call_get_message_flags(const grpc_call *call); - -/** Returns a bitset for the encodings (compression algorithms) supported by \a - * call's peer. - * - * To be indexed by grpc_compression_algorithm enum values. */ -gpr_uint32 grpc_call_get_encodings_accepted_by_peer(grpc_call *call); - #ifdef __cplusplus } #endif diff --git a/src/core/surface/call_details.c b/src/core/surface/call_details.c index 67862d7afe..60f0029819 100644 --- a/src/core/surface/call_details.c +++ b/src/core/surface/call_details.c @@ -36,11 +36,15 @@ #include <string.h> -void grpc_call_details_init(grpc_call_details *cd) { +#include "src/core/surface/api_trace.h" + +void grpc_call_details_init(grpc_call_details* cd) { + GRPC_API_TRACE("grpc_call_details_init(cd=%p)", 1, (cd)); memset(cd, 0, sizeof(*cd)); } -void grpc_call_details_destroy(grpc_call_details *cd) { +void grpc_call_details_destroy(grpc_call_details* cd) { + GRPC_API_TRACE("grpc_call_details_destroy(cd=%p)", 1, (cd)); gpr_free(cd->method); gpr_free(cd->host); } diff --git a/src/core/surface/call_log_batch.c b/src/core/surface/call_log_batch.c index 5a3ef1e5f4..2dd9737cf8 100644 --- a/src/core/surface/call_log_batch.c +++ b/src/core/surface/call_log_batch.c @@ -37,8 +37,6 @@ #include <grpc/support/alloc.h> #include <grpc/support/string_util.h> -int grpc_trace_batch = 0; - static void add_metadata(gpr_strvec *b, const grpc_metadata *md, size_t count) { size_t i; for (i = 0; i < count; i++) { diff --git a/src/core/surface/call_test_only.h b/src/core/surface/call_test_only.h new file mode 100644 index 0000000000..df4be3248b --- /dev/null +++ b/src/core/surface/call_test_only.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_CORE_SURFACE_CALL_TEST_ONLY_H +#define GRPC_INTERNAL_CORE_SURFACE_CALL_TEST_ONLY_H + +#include <grpc/grpc.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** Return the compression algorithm from \a call. + * + * \warning This function should \b only be used in test code. */ +grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm( + grpc_call *call); + +/** Return the message flags from \a call. + * + * \warning This function should \b only be used in test code. */ +gpr_uint32 grpc_call_test_only_get_message_flags(grpc_call *call); + +/** Returns a bitset for the encodings (compression algorithms) supported by \a + * call's peer. + * + * To be indexed by grpc_compression_algorithm enum values. */ +gpr_uint32 grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call); + + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_SURFACE_CALL_TEST_ONLY_H */ diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c index 586402e21c..a9a5f828f2 100644 --- a/src/core/surface/channel.c +++ b/src/core/surface/channel.c @@ -43,6 +43,7 @@ #include "src/core/client_config/resolver_registry.h" #include "src/core/iomgr/iomgr.h" #include "src/core/support/string.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/call.h" #include "src/core/surface/init.h" @@ -77,7 +78,6 @@ struct grpc_channel { gpr_mu registered_call_mu; registered_call *registered_calls; - grpc_iomgr_closure destroy_closure; char *target; }; @@ -91,7 +91,8 @@ struct grpc_channel { #define DEFAULT_MAX_MESSAGE_LENGTH (100 * 1024 * 1024) grpc_channel *grpc_channel_create_from_filters( - const char *target, const grpc_channel_filter **filters, size_t num_filters, + grpc_exec_ctx *exec_ctx, const char *target, + const grpc_channel_filter **filters, size_t num_filters, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client) { size_t i; size_t size = @@ -104,22 +105,21 @@ grpc_channel *grpc_channel_create_from_filters( /* decremented by grpc_channel_destroy */ gpr_ref_init(&channel->refs, 1); channel->metadata_context = mdctx; - channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status", 0); + channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status"); channel->grpc_compression_algorithm_string = - grpc_mdstr_from_string(mdctx, "grpc-encoding", 0); + grpc_mdstr_from_string(mdctx, "grpc-encoding"); channel->grpc_encodings_accepted_by_peer_string = - grpc_mdstr_from_string(mdctx, "grpc-accept-encoding", 0); - channel->grpc_message_string = - grpc_mdstr_from_string(mdctx, "grpc-message", 0); + grpc_mdstr_from_string(mdctx, "grpc-accept-encoding"); + channel->grpc_message_string = grpc_mdstr_from_string(mdctx, "grpc-message"); for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) { char buf[GPR_LTOA_MIN_BUFSIZE]; - gpr_ltoa(i, buf); + gpr_ltoa((long)i, buf); channel->grpc_status_elem[i] = grpc_mdelem_from_metadata_strings( mdctx, GRPC_MDSTR_REF(channel->grpc_status_string), - grpc_mdstr_from_string(mdctx, buf, 0)); + grpc_mdstr_from_string(mdctx, buf)); } - channel->path_string = grpc_mdstr_from_string(mdctx, ":path", 0); - channel->authority_string = grpc_mdstr_from_string(mdctx, ":authority", 0); + channel->path_string = grpc_mdstr_from_string(mdctx, ":path"); + channel->authority_string = grpc_mdstr_from_string(mdctx, ":authority"); gpr_mu_init(&channel->registered_call_mu); channel->registered_calls = NULL; @@ -134,7 +134,7 @@ grpc_channel *grpc_channel_create_from_filters( gpr_log(GPR_ERROR, "%s ignored: it must be >= 0", GRPC_ARG_MAX_MESSAGE_LENGTH); } else { - channel->max_message_length = args->args[i].value.integer; + channel->max_message_length = (gpr_uint32)args->args[i].value.integer; } } else if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) { if (args->args[i].type != GRPC_ARG_STRING) { @@ -177,7 +177,7 @@ grpc_channel *grpc_channel_create_from_filters( gpr_free(default_authority); } - grpc_channel_stack_init(filters, num_filters, channel, args, + grpc_channel_stack_init(exec_ctx, filters, num_filters, channel, args, channel->metadata_context, CHANNEL_STACK_FROM_CHANNEL(channel)); @@ -185,6 +185,7 @@ grpc_channel *grpc_channel_create_from_filters( } char *grpc_channel_get_target(grpc_channel *channel) { + GRPC_API_TRACE("grpc_channel_get_target(channel=%p)", 1, (channel)); return gpr_strdup(channel->target); } @@ -193,7 +194,7 @@ static grpc_call *grpc_channel_create_call_internal( grpc_completion_queue *cq, grpc_mdelem *path_mdelem, grpc_mdelem *authority_mdelem, gpr_timespec deadline) { grpc_mdelem *send_metadata[2]; - int num_metadata = 0; + size_t num_metadata = 0; GPR_ASSERT(channel->is_client); @@ -214,16 +215,25 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel, grpc_completion_queue *cq, const char *method, const char *host, gpr_timespec deadline, void *reserved) { + GRPC_API_TRACE( + "grpc_channel_create_call(" + "channel=%p, parent_call=%p, propagation_mask=%x, cq=%p, method=%s, " + "host=%s, " + "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 10, (channel, parent_call, (unsigned)propagation_mask, cq, method, host, + (long)deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, + reserved)); GPR_ASSERT(!reserved); return grpc_channel_create_call_internal( channel, parent_call, propagation_mask, cq, grpc_mdelem_from_metadata_strings( channel->metadata_context, GRPC_MDSTR_REF(channel->path_string), - grpc_mdstr_from_string(channel->metadata_context, method, 0)), + grpc_mdstr_from_string(channel->metadata_context, method)), host ? grpc_mdelem_from_metadata_strings( channel->metadata_context, GRPC_MDSTR_REF(channel->authority_string), - grpc_mdstr_from_string(channel->metadata_context, host, 0)) + grpc_mdstr_from_string(channel->metadata_context, host)) : NULL, deadline); } @@ -231,15 +241,18 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel, void *grpc_channel_register_call(grpc_channel *channel, const char *method, const char *host, void *reserved) { registered_call *rc = gpr_malloc(sizeof(registered_call)); + GRPC_API_TRACE( + "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)", + 4, (channel, method, host, reserved)); GPR_ASSERT(!reserved); rc->path = grpc_mdelem_from_metadata_strings( channel->metadata_context, GRPC_MDSTR_REF(channel->path_string), - grpc_mdstr_from_string(channel->metadata_context, method, 0)); + grpc_mdstr_from_string(channel->metadata_context, method)); rc->authority = host ? grpc_mdelem_from_metadata_strings( channel->metadata_context, GRPC_MDSTR_REF(channel->authority_string), - grpc_mdstr_from_string(channel->metadata_context, host, 0)) + grpc_mdstr_from_string(channel->metadata_context, host)) : NULL; gpr_mu_lock(&channel->registered_call_mu); rc->next = channel->registered_calls; @@ -253,6 +266,15 @@ grpc_call *grpc_channel_create_registered_call( grpc_completion_queue *completion_queue, void *registered_call_handle, gpr_timespec deadline, void *reserved) { registered_call *rc = registered_call_handle; + GRPC_API_TRACE( + "grpc_channel_create_registered_call(" + "channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, " + "registered_call_handle=%p, " + "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 9, (channel, parent_call, (unsigned)propagation_mask, completion_queue, + registered_call_handle, (long)deadline.tv_sec, deadline.tv_nsec, + (int)deadline.clock_type, reserved)); GPR_ASSERT(!reserved); return grpc_channel_create_call_internal( channel, parent_call, propagation_mask, completion_queue, @@ -270,10 +292,9 @@ void grpc_channel_internal_ref(grpc_channel *c) { gpr_ref(&c->refs); } -static void destroy_channel(void *p, int ok) { - grpc_channel *channel = p; +static void destroy_channel(grpc_exec_ctx *exec_ctx, grpc_channel *channel) { size_t i; - grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel)); + grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel)); for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) { GRPC_MDELEM_UNREF(channel->grpc_status_elem[i]); } @@ -302,28 +323,32 @@ static void destroy_channel(void *p, int ok) { } #ifdef GRPC_CHANNEL_REF_COUNT_DEBUG -void grpc_channel_internal_unref(grpc_channel *channel, const char *reason) { +void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, grpc_channel *channel, + const char *reason) { gpr_log(GPR_DEBUG, "CHANNEL: unref %p %d -> %d [%s]", channel, channel->refs.count, channel->refs.count - 1, reason); #else -void grpc_channel_internal_unref(grpc_channel *channel) { +void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, + grpc_channel *channel) { #endif if (gpr_unref(&channel->refs)) { - channel->destroy_closure.cb = destroy_channel; - channel->destroy_closure.cb_arg = channel; - grpc_iomgr_add_callback(&channel->destroy_closure); + destroy_channel(exec_ctx, channel); } } void grpc_channel_destroy(grpc_channel *channel) { grpc_transport_op op; grpc_channel_element *elem; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel)); memset(&op, 0, sizeof(op)); op.disconnect = 1; elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); - elem->filter->start_transport_op(elem, &op); + elem->filter->start_transport_op(&exec_ctx, elem, &op); - GRPC_CHANNEL_INTERNAL_UNREF(channel, "channel"); + GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, channel, "channel"); + + grpc_exec_ctx_finish(&exec_ctx); } grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) { @@ -356,7 +381,7 @@ grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) { gpr_ltoa(i, tmp); return grpc_mdelem_from_metadata_strings( channel->metadata_context, GRPC_MDSTR_REF(channel->grpc_status_string), - grpc_mdstr_from_string(channel->metadata_context, tmp, 0)); + grpc_mdstr_from_string(channel->metadata_context, tmp)); } } diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h index f271616f60..e5030d52d2 100644 --- a/src/core/surface/channel.h +++ b/src/core/surface/channel.h @@ -38,7 +38,8 @@ #include "src/core/client_config/subchannel_factory.h" grpc_channel *grpc_channel_create_from_filters( - const char *target, const grpc_channel_filter **filters, size_t count, + grpc_exec_ctx *exec_ctx, const char *target, + const grpc_channel_filter **filters, size_t count, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client); /** Get a (borrowed) pointer to this channels underlying channel stack */ @@ -63,18 +64,20 @@ gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel); #ifdef GRPC_CHANNEL_REF_COUNT_DEBUG void grpc_channel_internal_ref(grpc_channel *channel, const char *reason); -void grpc_channel_internal_unref(grpc_channel *channel, const char *reason); +void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, grpc_channel *channel, + const char *reason); #define GRPC_CHANNEL_INTERNAL_REF(channel, reason) \ grpc_channel_internal_ref(channel, reason) -#define GRPC_CHANNEL_INTERNAL_UNREF(channel, reason) \ - grpc_channel_internal_unref(channel, reason) +#define GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, reason) \ + grpc_channel_internal_unref(exec_ctx, channel, reason) #else void grpc_channel_internal_ref(grpc_channel *channel); -void grpc_channel_internal_unref(grpc_channel *channel); +void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, + grpc_channel *channel); #define GRPC_CHANNEL_INTERNAL_REF(channel, reason) \ grpc_channel_internal_ref(channel) -#define GRPC_CHANNEL_INTERNAL_UNREF(channel, reason) \ - grpc_channel_internal_unref(channel) +#define GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, reason) \ + grpc_channel_internal_unref(exec_ctx, channel) #endif #endif /* GRPC_INTERNAL_CORE_SURFACE_CHANNEL_H */ diff --git a/src/core/surface/channel_connectivity.c b/src/core/surface/channel_connectivity.c index 88a7c16598..df2774b527 100644 --- a/src/core/surface/channel_connectivity.c +++ b/src/core/surface/channel_connectivity.c @@ -37,7 +37,9 @@ #include <grpc/support/log.h> #include "src/core/channel/client_channel.h" -#include "src/core/iomgr/alarm.h" +#include "src/core/channel/client_uchannel.h" +#include "src/core/iomgr/timer.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/completion_queue.h" grpc_connectivity_state grpc_channel_check_connectivity_state( @@ -45,15 +47,29 @@ grpc_connectivity_state grpc_channel_check_connectivity_state( /* forward through to the underlying client channel */ grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); - if (client_channel_elem->filter != &grpc_client_channel_filter) { - gpr_log(GPR_ERROR, - "grpc_channel_check_connectivity_state called on something that is " - "not a client channel, but '%s'", - client_channel_elem->filter->name); - return GRPC_CHANNEL_FATAL_FAILURE; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_connectivity_state state; + GRPC_API_TRACE( + "grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2, + (channel, try_to_connect)); + if (client_channel_elem->filter == &grpc_client_channel_filter) { + state = grpc_client_channel_check_connectivity_state( + &exec_ctx, client_channel_elem, try_to_connect); + grpc_exec_ctx_finish(&exec_ctx); + return state; + } + if (client_channel_elem->filter == &grpc_client_uchannel_filter) { + state = grpc_client_uchannel_check_connectivity_state( + &exec_ctx, client_channel_elem, try_to_connect); + grpc_exec_ctx_finish(&exec_ctx); + return state; } - return grpc_client_channel_check_connectivity_state(client_channel_elem, - try_to_connect); + gpr_log(GPR_ERROR, + "grpc_channel_check_connectivity_state called on something that is " + "not a (u)client channel, but '%s'", + client_channel_elem->filter->name); + grpc_exec_ctx_finish(&exec_ctx); + return GRPC_CHANNEL_FATAL_FAILURE; } typedef enum { @@ -67,8 +83,9 @@ typedef struct { gpr_mu mu; callback_phase phase; int success; - grpc_iomgr_closure on_complete; - grpc_alarm alarm; + int removed; + grpc_closure on_complete; + grpc_timer alarm; grpc_connectivity_state state; grpc_completion_queue *cq; grpc_cq_completion completion_storage; @@ -76,26 +93,31 @@ typedef struct { void *tag; } state_watcher; -static void delete_state_watcher(state_watcher *w) { +static void delete_state_watcher(grpc_exec_ctx *exec_ctx, state_watcher *w) { grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( grpc_channel_get_channel_stack(w->channel)); - grpc_client_channel_del_interested_party(client_channel_elem, - grpc_cq_pollset(w->cq)); - GRPC_CHANNEL_INTERNAL_UNREF(w->channel, "watch_connectivity"); + if (client_channel_elem->filter == &grpc_client_channel_filter) { + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel, + "watch_channel_connectivity"); + } else if (client_channel_elem->filter == &grpc_client_uchannel_filter) { + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel, + "watch_uchannel_connectivity"); + } else { + abort(); + } gpr_mu_destroy(&w->mu); gpr_free(w); } -static void finished_completion(void *pw, grpc_cq_completion *ignored) { +static void finished_completion(grpc_exec_ctx *exec_ctx, void *pw, + grpc_cq_completion *ignored) { int delete = 0; state_watcher *w = pw; gpr_mu_lock(&w->mu); switch (w->phase) { case WAITING: case CALLED_BACK: - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - break; + GPR_UNREACHABLE_CODE(return ); case CALLING_BACK: w->phase = CALLED_BACK; break; @@ -106,34 +128,48 @@ static void finished_completion(void *pw, grpc_cq_completion *ignored) { gpr_mu_unlock(&w->mu); if (delete) { - delete_state_watcher(w); + delete_state_watcher(exec_ctx, w); } } -static void partly_done(state_watcher *w, int due_to_completion) { +static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w, + int due_to_completion) { int delete = 0; + grpc_channel_element *client_channel_elem = NULL; + gpr_mu_lock(&w->mu); + if (w->removed == 0) { + w->removed = 1; + client_channel_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(w->channel)); + if (client_channel_elem->filter == &grpc_client_channel_filter) { + grpc_client_channel_del_interested_party(exec_ctx, client_channel_elem, + grpc_cq_pollset(w->cq)); + } else { + grpc_client_uchannel_del_interested_party(exec_ctx, client_channel_elem, + grpc_cq_pollset(w->cq)); + } + } + gpr_mu_unlock(&w->mu); if (due_to_completion) { gpr_mu_lock(&w->mu); w->success = 1; gpr_mu_unlock(&w->mu); - grpc_alarm_cancel(&w->alarm); + grpc_timer_cancel(exec_ctx, &w->alarm); } gpr_mu_lock(&w->mu); switch (w->phase) { case WAITING: w->phase = CALLING_BACK; - grpc_cq_end_op(w->cq, w->tag, w->success, finished_completion, w, - &w->completion_storage); + grpc_cq_end_op(exec_ctx, w->cq, w->tag, w->success, finished_completion, + w, &w->completion_storage); break; case CALLING_BACK: w->phase = CALLING_BACK_AND_FINISHED; break; case CALLING_BACK_AND_FINISHED: - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - break; + GPR_UNREACHABLE_CODE(return ); case CALLED_BACK: delete = 1; break; @@ -141,47 +177,63 @@ static void partly_done(state_watcher *w, int due_to_completion) { gpr_mu_unlock(&w->mu); if (delete) { - delete_state_watcher(w); + delete_state_watcher(exec_ctx, w); } } -static void watch_complete(void *pw, int success) { partly_done(pw, 1); } +static void watch_complete(grpc_exec_ctx *exec_ctx, void *pw, int success) { + partly_done(exec_ctx, pw, 1); +} -static void timeout_complete(void *pw, int success) { partly_done(pw, 0); } +static void timeout_complete(grpc_exec_ctx *exec_ctx, void *pw, int success) { + partly_done(exec_ctx, pw, 0); +} void grpc_channel_watch_connectivity_state( grpc_channel *channel, grpc_connectivity_state last_observed_state, gpr_timespec deadline, grpc_completion_queue *cq, void *tag) { grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; state_watcher *w = gpr_malloc(sizeof(*w)); + GRPC_API_TRACE( + "grpc_channel_watch_connectivity_state(" + "channel=%p, last_observed_state=%d, " + "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "cq=%p, tag=%p)", + 7, (channel, (int)last_observed_state, (long)deadline.tv_sec, + deadline.tv_nsec, (int)deadline.clock_type, cq, tag)); + grpc_cq_begin_op(cq); gpr_mu_init(&w->mu); - grpc_iomgr_closure_init(&w->on_complete, watch_complete, w); + grpc_closure_init(&w->on_complete, watch_complete, w); w->phase = WAITING; w->state = last_observed_state; w->success = 0; + w->removed = 0; w->cq = cq; w->tag = tag; w->channel = channel; - grpc_alarm_init(&w->alarm, + grpc_timer_init(&exec_ctx, &w->alarm, gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), timeout_complete, w, gpr_now(GPR_CLOCK_MONOTONIC)); - if (client_channel_elem->filter != &grpc_client_channel_filter) { - gpr_log(GPR_ERROR, - "grpc_channel_watch_connectivity_state called on something that is " - "not a client channel, but '%s'", - client_channel_elem->filter->name); - grpc_iomgr_add_delayed_callback(&w->on_complete, 1); - } else { - GRPC_CHANNEL_INTERNAL_REF(channel, "watch_connectivity"); - grpc_client_channel_add_interested_party(client_channel_elem, + if (client_channel_elem->filter == &grpc_client_channel_filter) { + GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity"); + grpc_client_channel_add_interested_party(&exec_ctx, client_channel_elem, grpc_cq_pollset(cq)); - grpc_client_channel_watch_connectivity_state(client_channel_elem, &w->state, - &w->on_complete); + grpc_client_channel_watch_connectivity_state(&exec_ctx, client_channel_elem, + &w->state, &w->on_complete); + } else if (client_channel_elem->filter == &grpc_client_uchannel_filter) { + GRPC_CHANNEL_INTERNAL_REF(channel, "watch_uchannel_connectivity"); + grpc_client_uchannel_add_interested_party(&exec_ctx, client_channel_elem, + grpc_cq_pollset(cq)); + grpc_client_uchannel_watch_connectivity_state( + &exec_ctx, client_channel_elem, &w->state, &w->on_complete); } + + grpc_exec_ctx_finish(&exec_ctx); } diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c index 707251da89..51d9130b63 100644 --- a/src/core/surface/channel_create.c +++ b/src/core/surface/channel_create.c @@ -45,6 +45,7 @@ #include "src/core/channel/http_client_filter.h" #include "src/core/client_config/resolver_registry.h" #include "src/core/iomgr/tcp_client.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/channel.h" #include "src/core/transport/chttp2_transport.h" @@ -52,9 +53,15 @@ typedef struct { grpc_connector base; gpr_refcount refs; - grpc_iomgr_closure *notify; + grpc_closure *notify; grpc_connect_in_args args; grpc_connect_out_args *result; + + grpc_endpoint *tcp; + + grpc_mdctx *mdctx; + + grpc_closure connected; } connector; static void connector_ref(grpc_connector *con) { @@ -62,20 +69,23 @@ static void connector_ref(grpc_connector *con) { gpr_ref(&c->refs); } -static void connector_unref(grpc_connector *con) { +static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) { connector *c = (connector *)con; if (gpr_unref(&c->refs)) { + grpc_mdctx_unref(c->mdctx); gpr_free(c); } } -static void connected(void *arg, grpc_endpoint *tcp) { +static void connected(grpc_exec_ctx *exec_ctx, void *arg, int success) { connector *c = arg; - grpc_iomgr_closure *notify; + grpc_closure *notify; + grpc_endpoint *tcp = c->tcp; if (tcp != NULL) { c->result->transport = grpc_create_chttp2_transport( - c->args.channel_args, tcp, c->args.metadata_context, 1); - grpc_chttp2_transport_start_reading(c->result->transport, NULL, 0); + exec_ctx, c->args.channel_args, tcp, c->mdctx, 1); + grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL, + 0); GPR_ASSERT(c->result->transport); c->result->filters = gpr_malloc(sizeof(grpc_channel_filter *)); c->result->filters[0] = &grpc_http_client_filter; @@ -85,25 +95,30 @@ static void connected(void *arg, grpc_endpoint *tcp) { } notify = c->notify; c->notify = NULL; - grpc_iomgr_add_callback(notify); + notify->cb(exec_ctx, notify->cb_arg, 1); } -static void connector_connect(grpc_connector *con, +static void connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *con) {} + +static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con, const grpc_connect_in_args *args, grpc_connect_out_args *result, - grpc_iomgr_closure *notify) { + grpc_closure *notify) { connector *c = (connector *)con; GPR_ASSERT(c->notify == NULL); GPR_ASSERT(notify->cb); c->notify = notify; c->args = *args; c->result = result; - grpc_tcp_client_connect(connected, c, args->interested_parties, args->addr, - args->addr_len, args->deadline); + c->tcp = NULL; + grpc_closure_init(&c->connected, connected, c); + grpc_tcp_client_connect(exec_ctx, &c->connected, &c->tcp, + args->interested_parties, args->addr, args->addr_len, + args->deadline); } static const grpc_connector_vtable connector_vtable = { - connector_ref, connector_unref, connector_connect}; + connector_ref, connector_unref, connector_shutdown, connector_connect}; typedef struct { grpc_subchannel_factory base; @@ -118,10 +133,11 @@ static void subchannel_factory_ref(grpc_subchannel_factory *scf) { gpr_ref(&f->refs); } -static void subchannel_factory_unref(grpc_subchannel_factory *scf) { +static void subchannel_factory_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel_factory *scf) { subchannel_factory *f = (subchannel_factory *)scf; if (gpr_unref(&f->refs)) { - GRPC_CHANNEL_INTERNAL_UNREF(f->master, "subchannel_factory"); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory"); grpc_channel_args_destroy(f->merge_args); grpc_mdctx_unref(f->mdctx); gpr_free(f); @@ -129,7 +145,8 @@ static void subchannel_factory_unref(grpc_subchannel_factory *scf) { } static grpc_subchannel *subchannel_factory_create_subchannel( - grpc_subchannel_factory *scf, grpc_subchannel_args *args) { + grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *scf, + grpc_subchannel_args *args) { subchannel_factory *f = (subchannel_factory *)scf; connector *c = gpr_malloc(sizeof(*c)); grpc_channel_args *final_args = @@ -137,12 +154,14 @@ static grpc_subchannel *subchannel_factory_create_subchannel( grpc_subchannel *s; memset(c, 0, sizeof(*c)); c->base.vtable = &connector_vtable; + c->mdctx = f->mdctx; + grpc_mdctx_ref(c->mdctx); gpr_ref_init(&c->refs, 1); args->mdctx = f->mdctx; args->args = final_args; args->master = f->master; s = grpc_subchannel_create(&c->base, args); - grpc_connector_unref(&c->base); + grpc_connector_unref(exec_ctx, &c->base); grpc_channel_args_destroy(final_args); return s; } @@ -164,7 +183,11 @@ grpc_channel *grpc_insecure_channel_create(const char *target, grpc_resolver *resolver; subchannel_factory *f; grpc_mdctx *mdctx = grpc_mdctx_create(); - int n = 0; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + size_t n = 0; + GRPC_API_TRACE( + "grpc_insecure_channel_create(target=%p, args=%p, reserved=%p)", 3, + (target, args, reserved)); GPR_ASSERT(!reserved); if (grpc_channel_args_is_census_enabled(args)) { filters[n++] = &grpc_client_census_filter; @@ -173,8 +196,8 @@ grpc_channel *grpc_insecure_channel_create(const char *target, filters[n++] = &grpc_client_channel_filter; GPR_ASSERT(n <= MAX_FILTERS); - channel = - grpc_channel_create_from_filters(target, filters, n, args, mdctx, 1); + channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n, + args, mdctx, 1); f = gpr_malloc(sizeof(*f)); f->base.vtable = &subchannel_factory_vtable; @@ -189,10 +212,12 @@ grpc_channel *grpc_insecure_channel_create(const char *target, return NULL; } - grpc_client_channel_set_resolver(grpc_channel_get_channel_stack(channel), - resolver); - GRPC_RESOLVER_UNREF(resolver, "create"); - grpc_subchannel_factory_unref(&f->base); + grpc_client_channel_set_resolver( + &exec_ctx, grpc_channel_get_channel_stack(channel), resolver); + GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "create"); + grpc_subchannel_factory_unref(&exec_ctx, &f->base); + + grpc_exec_ctx_finish(&exec_ctx); return channel; } diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c index b58115a93f..aa90b3f7f5 100644 --- a/src/core/surface/completion_queue.c +++ b/src/core/surface/completion_queue.c @@ -36,14 +36,18 @@ #include <stdio.h> #include <string.h> +#include "src/core/iomgr/timer.h" #include "src/core/iomgr/pollset.h" #include "src/core/support/string.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/call.h" #include "src/core/surface/event_string.h" #include "src/core/surface/surface_trace.h" +#include "src/core/profiling/timers.h" #include <grpc/support/alloc.h> #include <grpc/support/atm.h> #include <grpc/support/log.h> +#include <grpc/support/time.h> typedef struct { grpc_pollset_worker *worker; @@ -67,10 +71,24 @@ struct grpc_completion_queue { int is_server_cq; int num_pluckers; plucker pluckers[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS]; + grpc_closure pollset_destroy_done; }; +struct grpc_cq_alarm { + grpc_timer alarm; + grpc_cq_completion completion; + /** completion queue where events about this alarm will be posted */ + grpc_completion_queue *cq; + /** user supplied tag */ + void *tag; +}; + +static void on_pollset_destroy_done(grpc_exec_ctx *exec_ctx, void *cc, + int success); + grpc_completion_queue *grpc_completion_queue_create(void *reserved) { grpc_completion_queue *cc = gpr_malloc(sizeof(grpc_completion_queue)); + GRPC_API_TRACE("grpc_completion_queue_create(reserved=%p)", 1, (reserved)); GPR_ASSERT(!reserved); memset(cc, 0, sizeof(*cc)); /* Initial ref is dropped by grpc_completion_queue_shutdown */ @@ -80,6 +98,7 @@ grpc_completion_queue *grpc_completion_queue_create(void *reserved) { grpc_pollset_init(&cc->pollset); cc->completed_tail = &cc->completed_head; cc->completed_head.next = (gpr_uintptr)cc->completed_tail; + grpc_closure_init(&cc->pollset_destroy_done, on_pollset_destroy_done, cc); return cc; } @@ -94,7 +113,8 @@ void grpc_cq_internal_ref(grpc_completion_queue *cc) { gpr_ref(&cc->owning_refs); } -static void on_pollset_destroy_done(void *arg) { +static void on_pollset_destroy_done(grpc_exec_ctx *exec_ctx, void *arg, + int success) { grpc_completion_queue *cc = arg; GRPC_CQ_INTERNAL_UNREF(cc, "pollset_destroy"); } @@ -126,13 +146,17 @@ void grpc_cq_begin_op(grpc_completion_queue *cc) { /* Signal the end of an operation - if this is the last waiting-to-be-queued event, then enter shutdown mode */ /* Queue a GRPC_OP_COMPLETED operation */ -void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success, - void (*done)(void *done_arg, grpc_cq_completion *storage), +void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, + void *tag, int success, + void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, + grpc_cq_completion *storage), void *done_arg, grpc_cq_completion *storage) { int shutdown; int i; grpc_pollset_worker *pluck_worker; + GPR_TIMER_BEGIN("grpc_cq_end_op", 0); + storage->tag = tag; storage->done = done; storage->done_arg = done_arg; @@ -162,8 +186,10 @@ void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success, GPR_ASSERT(cc->shutdown_called); cc->shutdown = 1; gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc); + grpc_pollset_shutdown(exec_ctx, &cc->pollset, &cc->pollset_destroy_done); } + + GPR_TIMER_END("grpc_cq_end_op", 0); } grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, @@ -172,7 +198,17 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, grpc_pollset_worker worker; int first_loop = 1; gpr_timespec now; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GPR_TIMER_BEGIN("grpc_completion_queue_next", 0); + + GRPC_API_TRACE( + "grpc_completion_queue_next(" + "cc=%p, " + "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 5, (cc, (long)deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, + reserved)); GPR_ASSERT(!reserved); deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); @@ -190,7 +226,7 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, ret.type = GRPC_OP_COMPLETE; ret.success = c->next & 1u; ret.tag = c->tag; - c->done(c->done_arg, c); + c->done(&exec_ctx, c->done_arg, c); break; } if (cc->shutdown) { @@ -207,10 +243,14 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, break; } first_loop = 0; - grpc_pollset_work(&cc->pollset, &worker, now, deadline); + grpc_pollset_work(&exec_ctx, &cc->pollset, &worker, now, deadline); } GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_CQ_INTERNAL_UNREF(cc, "next"); + grpc_exec_ctx_finish(&exec_ctx); + + GPR_TIMER_END("grpc_completion_queue_next", 0); + return ret; } @@ -235,8 +275,7 @@ static void del_plucker(grpc_completion_queue *cc, void *tag, return; } } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); + GPR_UNREACHABLE_CODE(return ); } grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, @@ -247,7 +286,17 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, grpc_pollset_worker worker; gpr_timespec now; int first_loop = 1; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0); + + GRPC_API_TRACE( + "grpc_completion_queue_pluck(" + "cc=%p, tag=%p, " + "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 6, (cc, tag, (long)deadline.tv_sec, deadline.tv_nsec, + (int)deadline.clock_type, reserved)); GPR_ASSERT(!reserved); deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); @@ -268,7 +317,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, ret.type = GRPC_OP_COMPLETE; ret.success = c->next & 1u; ret.tag = c->tag; - c->done(c->done_arg, c); + c->done(&exec_ctx, c->done_arg, c); goto done; } prev = c; @@ -299,18 +348,24 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, break; } first_loop = 0; - grpc_pollset_work(&cc->pollset, &worker, now, deadline); + grpc_pollset_work(&exec_ctx, &cc->pollset, &worker, now, deadline); del_plucker(cc, tag, &worker); } done: GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_CQ_INTERNAL_UNREF(cc, "pluck"); + grpc_exec_ctx_finish(&exec_ctx); + + GPR_TIMER_END("grpc_completion_queue_pluck", 0); + return ret; } /* Shutdown simply drops a ref that we reserved at creation time; if we drop to zero here, then enter shutdown mode and wake up any waiters */ void grpc_completion_queue_shutdown(grpc_completion_queue *cc) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE("grpc_completion_queue_shutdown(cc=%p)", 1, (cc)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); if (cc->shutdown_called) { gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); @@ -324,11 +379,13 @@ void grpc_completion_queue_shutdown(grpc_completion_queue *cc) { GPR_ASSERT(!cc->shutdown); cc->shutdown = 1; gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc); + grpc_pollset_shutdown(&exec_ctx, &cc->pollset, &cc->pollset_destroy_done); } + grpc_exec_ctx_finish(&exec_ctx); } void grpc_completion_queue_destroy(grpc_completion_queue *cc) { + GRPC_API_TRACE("grpc_completion_queue_destroy(cc=%p)", 1, (cc)); grpc_completion_queue_shutdown(cc); GRPC_CQ_INTERNAL_UNREF(cc, "destroy"); } diff --git a/src/core/surface/completion_queue.h b/src/core/surface/completion_queue.h index 8de024aaea..5f8282e542 100644 --- a/src/core/surface/completion_queue.h +++ b/src/core/surface/completion_queue.h @@ -34,7 +34,7 @@ #ifndef GRPC_INTERNAL_CORE_SURFACE_COMPLETION_QUEUE_H #define GRPC_INTERNAL_CORE_SURFACE_COMPLETION_QUEUE_H -/* Internal API for completion channels */ +/* Internal API for completion queues */ #include "src/core/iomgr/pollset.h" #include <grpc/grpc.h> @@ -44,7 +44,8 @@ typedef struct grpc_cq_completion { void *tag; /** done callback - called when this queue element is no longer needed by the completion queue */ - void (*done)(void *done_arg, struct grpc_cq_completion *c); + void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, + struct grpc_cq_completion *c); void *done_arg; /** next pointer; low bit is used to indicate success or not */ gpr_uintptr next; @@ -71,8 +72,10 @@ void grpc_cq_internal_unref(grpc_completion_queue *cc); void grpc_cq_begin_op(grpc_completion_queue *cc); /* Queue a GRPC_OP_COMPLETED operation */ -void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success, - void (*done)(void *done_arg, grpc_cq_completion *storage), +void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, + void *tag, int success, + void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, + grpc_cq_completion *storage), void *done_arg, grpc_cq_completion *storage); grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc); diff --git a/src/core/surface/init.c b/src/core/surface/init.c index 0d48cd42d7..b2e66a830e 100644 --- a/src/core/surface/init.c +++ b/src/core/surface/init.c @@ -40,12 +40,17 @@ #include <grpc/support/alloc.h> #include <grpc/support/time.h> #include "src/core/channel/channel_stack.h" +#include "src/core/client_config/lb_policy_registry.h" +#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/client_config/lb_policies/round_robin.h" #include "src/core/client_config/resolver_registry.h" #include "src/core/client_config/resolvers/dns_resolver.h" #include "src/core/client_config/resolvers/sockaddr_resolver.h" #include "src/core/debug/trace.h" +#include "src/core/iomgr/executor.h" #include "src/core/iomgr/iomgr.h" #include "src/core/profiling/timers.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/call.h" #include "src/core/surface/init.h" #include "src/core/surface/surface_trace.h" @@ -72,6 +77,8 @@ static grpc_plugin g_all_of_the_plugins[MAX_PLUGINS]; static int g_number_of_plugins = 0; void grpc_register_plugin(void (*init)(void), void (*destroy)(void)) { + GRPC_API_TRACE("grpc_register_plugin(init=%lx, destroy=%lx)", 2, + ((unsigned long)init, (unsigned long)destroy)); GPR_ASSERT(g_number_of_plugins != MAX_PLUGINS); g_all_of_the_plugins[g_number_of_plugins].init = init; g_all_of_the_plugins[g_number_of_plugins].destroy = destroy; @@ -85,6 +92,9 @@ void grpc_init(void) { gpr_mu_lock(&g_init_mu); if (++g_initializations == 1) { gpr_time_init(); + grpc_lb_policy_registry_init(grpc_pick_first_lb_factory_create()); + grpc_register_lb_policy(grpc_pick_first_lb_factory_create()); + grpc_register_lb_policy(grpc_round_robin_lb_factory_create()); grpc_resolver_registry_init("dns:///"); grpc_register_resolver_type(grpc_dns_resolver_factory_create()); grpc_register_resolver_type(grpc_ipv4_resolver_factory_create()); @@ -92,14 +102,14 @@ void grpc_init(void) { #ifdef GPR_POSIX_SOCKET grpc_register_resolver_type(grpc_unix_resolver_factory_create()); #endif + grpc_register_tracer("api", &grpc_api_trace); grpc_register_tracer("channel", &grpc_trace_channel); - grpc_register_tracer("surface", &grpc_surface_trace); grpc_register_tracer("http", &grpc_http_trace); grpc_register_tracer("flowctl", &grpc_flowctl_trace); - grpc_register_tracer("batch", &grpc_trace_batch); grpc_register_tracer("connectivity_state", &grpc_connectivity_state_trace); grpc_security_pre_init(); grpc_iomgr_init(); + grpc_executor_init(); grpc_tracer_init("GRPC_TRACE"); /* Only initialize census if noone else has. */ if (census_enabled() == CENSUS_FEATURE_NONE) { @@ -107,7 +117,7 @@ void grpc_init(void) { gpr_log(GPR_ERROR, "Could not initialize census."); } } - grpc_timers_global_init(); + gpr_timers_global_init(); for (i = 0; i < g_number_of_plugins; i++) { if (g_all_of_the_plugins[i].init != NULL) { g_all_of_the_plugins[i].init(); @@ -115,15 +125,18 @@ void grpc_init(void) { } } gpr_mu_unlock(&g_init_mu); + GRPC_API_TRACE("grpc_init(void)", 0, ()); } void grpc_shutdown(void) { int i; + GRPC_API_TRACE("grpc_shutdown(void)", 0, ()); gpr_mu_lock(&g_init_mu); if (--g_initializations == 0) { grpc_iomgr_shutdown(); + grpc_executor_shutdown(); census_shutdown(); - grpc_timers_global_destroy(); + gpr_timers_global_destroy(); grpc_tracer_shutdown(); grpc_resolver_registry_shutdown(); for (i = 0; i < g_number_of_plugins; i++) { diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c index 80704cbf67..e72264fbcd 100644 --- a/src/core/surface/lame_client.c +++ b/src/core/surface/lame_client.c @@ -37,6 +37,7 @@ #include "src/core/channel/channel_stack.h" #include "src/core/support/string.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/channel.h" #include "src/core/surface/call.h" #include <grpc/support/alloc.h> @@ -54,14 +55,15 @@ typedef struct { const char *error_message; } channel_data; -static void lame_start_transport_stream_op(grpc_call_element *elem, +static void lame_start_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; GRPC_CALL_LOG_OP(GPR_INFO, elem, op); if (op->send_ops != NULL) { grpc_stream_ops_unref_owned_objects(op->send_ops->ops, op->send_ops->nops); - op->on_done_send->cb(op->on_done_send->cb_arg, 0); + op->on_done_send->cb(exec_ctx, op->on_done_send->cb_arg, 0); } if (op->recv_ops != NULL) { char tmp[GPR_LTOA_MIN_BUFSIZE]; @@ -80,42 +82,45 @@ static void lame_start_transport_stream_op(grpc_call_element *elem, mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); grpc_sopb_add_metadata(op->recv_ops, mdb); *op->recv_state = GRPC_STREAM_CLOSED; - op->on_done_recv->cb(op->on_done_recv->cb_arg, 1); + op->on_done_recv->cb(exec_ctx, op->on_done_recv->cb_arg, 1); } if (op->on_consumed != NULL) { - op->on_consumed->cb(op->on_consumed->cb_arg, 0); + op->on_consumed->cb(exec_ctx, op->on_consumed->cb_arg, 0); } } -static char *lame_get_peer(grpc_call_element *elem) { +static char *lame_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { channel_data *chand = elem->channel_data; return grpc_channel_get_target(chand->master); } -static void lame_start_transport_op(grpc_channel_element *elem, +static void lame_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_transport_op *op) { if (op->on_connectivity_state_change) { GPR_ASSERT(*op->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE); *op->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE; op->on_connectivity_state_change->cb( - op->on_connectivity_state_change->cb_arg, 1); + exec_ctx, op->on_connectivity_state_change->cb_arg, 1); } if (op->on_consumed != NULL) { - op->on_consumed->cb(op->on_consumed->cb_arg, 1); + op->on_consumed->cb(exec_ctx, op->on_consumed->cb_arg, 1); } } -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *transport_server_data, grpc_transport_stream_op *initial_op) { if (initial_op) { - grpc_transport_stream_op_finish_with_failure(initial_op); + grpc_transport_stream_op_finish_with_failure(exec_ctx, initial_op); } } -static void destroy_call_elem(grpc_call_element *elem) {} +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) {} -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { channel_data *chand = elem->channel_data; @@ -125,19 +130,13 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, chand->master = master; } -static void destroy_channel_elem(grpc_channel_element *elem) {} +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) {} static const grpc_channel_filter lame_filter = { - lame_start_transport_stream_op, - lame_start_transport_op, - sizeof(call_data), - init_call_elem, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - lame_get_peer, - "lame-client", + lame_start_transport_stream_op, lame_start_transport_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, + destroy_channel_elem, lame_get_peer, "lame-client", }; #define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1)) @@ -148,13 +147,19 @@ grpc_channel *grpc_lame_client_channel_create(const char *target, grpc_channel *channel; grpc_channel_element *elem; channel_data *chand; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; static const grpc_channel_filter *filters[] = {&lame_filter}; - channel = grpc_channel_create_from_filters(target, filters, 1, NULL, - grpc_mdctx_create(), 1); + channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, 1, + NULL, grpc_mdctx_create(), 1); elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + GRPC_API_TRACE( + "grpc_lame_client_channel_create(target=%s, error_code=%d, " + "error_message=%s)", + 3, (target, (int)error_code, error_message)); GPR_ASSERT(elem->filter == &lame_filter); chand = (channel_data *)elem->channel_data; chand->error_code = error_code; chand->error_message = error_message; + grpc_exec_ctx_finish(&exec_ctx); return channel; } diff --git a/src/core/surface/metadata_array.c b/src/core/surface/metadata_array.c index 4010977497..4c7bf17835 100644 --- a/src/core/surface/metadata_array.c +++ b/src/core/surface/metadata_array.c @@ -36,10 +36,14 @@ #include <string.h> -void grpc_metadata_array_init(grpc_metadata_array *array) { +#include "src/core/surface/api_trace.h" + +void grpc_metadata_array_init(grpc_metadata_array* array) { + GRPC_API_TRACE("grpc_metadata_array_init(array=%p)", 1, (array)); memset(array, 0, sizeof(*array)); } -void grpc_metadata_array_destroy(grpc_metadata_array *array) { +void grpc_metadata_array_destroy(grpc_metadata_array* array) { + GRPC_API_TRACE("grpc_metadata_array_destroy(array=%p)", 1, (array)); gpr_free(array->metadata); } diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c index 35b60bdbef..1282ee99ed 100644 --- a/src/core/surface/secure_channel_create.c +++ b/src/core/surface/secure_channel_create.c @@ -47,7 +47,7 @@ #include "src/core/iomgr/tcp_client.h" #include "src/core/security/auth_filters.h" #include "src/core/security/credentials.h" -#include "src/core/security/secure_transport_setup.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/channel.h" #include "src/core/transport/chttp2_transport.h" #include "src/core/tsi/transport_security_interface.h" @@ -58,9 +58,17 @@ typedef struct { grpc_channel_security_connector *security_connector; - grpc_iomgr_closure *notify; + grpc_closure *notify; grpc_connect_in_args args; grpc_connect_out_args *result; + + gpr_mu mu; + grpc_endpoint *connecting_endpoint; + grpc_endpoint *newly_connecting_endpoint; + + grpc_closure connected_closure; + + grpc_mdctx *mdctx; } connector; static void connector_ref(grpc_connector *con) { @@ -68,26 +76,38 @@ static void connector_ref(grpc_connector *con) { gpr_ref(&c->refs); } -static void connector_unref(grpc_connector *con) { +static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) { connector *c = (connector *)con; if (gpr_unref(&c->refs)) { + grpc_mdctx_unref(c->mdctx); gpr_free(c); } } -static void on_secure_transport_setup_done(void *arg, - grpc_security_status status, - grpc_endpoint *wrapped_endpoint, - grpc_endpoint *secure_endpoint) { +static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_security_status status, + grpc_endpoint *wrapped_endpoint, + grpc_endpoint *secure_endpoint) { connector *c = arg; - grpc_iomgr_closure *notify; - if (status != GRPC_SECURITY_OK) { - gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status); + grpc_closure *notify; + gpr_mu_lock(&c->mu); + if (c->connecting_endpoint == NULL) { memset(c->result, 0, sizeof(*c->result)); + gpr_mu_unlock(&c->mu); + } else if (status != GRPC_SECURITY_OK) { + GPR_ASSERT(c->connecting_endpoint == wrapped_endpoint); + gpr_log(GPR_ERROR, "Secure handshake failed with error %d.", status); + memset(c->result, 0, sizeof(*c->result)); + c->connecting_endpoint = NULL; + gpr_mu_unlock(&c->mu); } else { + GPR_ASSERT(c->connecting_endpoint == wrapped_endpoint); + c->connecting_endpoint = NULL; + gpr_mu_unlock(&c->mu); c->result->transport = grpc_create_chttp2_transport( - c->args.channel_args, secure_endpoint, c->args.metadata_context, 1); - grpc_chttp2_transport_start_reading(c->result->transport, NULL, 0); + exec_ctx, c->args.channel_args, secure_endpoint, c->mdctx, 1); + grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL, + 0); c->result->filters = gpr_malloc(sizeof(grpc_channel_filter *) * 2); c->result->filters[0] = &grpc_http_client_filter; c->result->filters[1] = &grpc_client_auth_filter; @@ -95,39 +115,61 @@ static void on_secure_transport_setup_done(void *arg, } notify = c->notify; c->notify = NULL; - grpc_iomgr_add_callback(notify); + notify->cb(exec_ctx, notify->cb_arg, 1); } -static void connected(void *arg, grpc_endpoint *tcp) { +static void connected(grpc_exec_ctx *exec_ctx, void *arg, int success) { connector *c = arg; - grpc_iomgr_closure *notify; + grpc_closure *notify; + grpc_endpoint *tcp = c->newly_connecting_endpoint; if (tcp != NULL) { - grpc_setup_secure_transport(&c->security_connector->base, tcp, - on_secure_transport_setup_done, c); + gpr_mu_lock(&c->mu); + GPR_ASSERT(c->connecting_endpoint == NULL); + c->connecting_endpoint = tcp; + gpr_mu_unlock(&c->mu); + grpc_security_connector_do_handshake(exec_ctx, &c->security_connector->base, + tcp, on_secure_handshake_done, c); } else { memset(c->result, 0, sizeof(*c->result)); notify = c->notify; c->notify = NULL; - grpc_iomgr_add_callback(notify); + notify->cb(exec_ctx, notify->cb_arg, 1); + } +} + +static void connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *con) { + connector *c = (connector *)con; + grpc_endpoint *ep; + gpr_mu_lock(&c->mu); + ep = c->connecting_endpoint; + c->connecting_endpoint = NULL; + gpr_mu_unlock(&c->mu); + if (ep) { + grpc_endpoint_shutdown(exec_ctx, ep); } } -static void connector_connect(grpc_connector *con, +static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con, const grpc_connect_in_args *args, grpc_connect_out_args *result, - grpc_iomgr_closure *notify) { + grpc_closure *notify) { connector *c = (connector *)con; GPR_ASSERT(c->notify == NULL); GPR_ASSERT(notify->cb); c->notify = notify; c->args = *args; c->result = result; - grpc_tcp_client_connect(connected, c, args->interested_parties, args->addr, - args->addr_len, args->deadline); + gpr_mu_lock(&c->mu); + GPR_ASSERT(c->connecting_endpoint == NULL); + gpr_mu_unlock(&c->mu); + grpc_closure_init(&c->connected_closure, connected, c); + grpc_tcp_client_connect( + exec_ctx, &c->connected_closure, &c->newly_connecting_endpoint, + args->interested_parties, args->addr, args->addr_len, args->deadline); } static const grpc_connector_vtable connector_vtable = { - connector_ref, connector_unref, connector_connect}; + connector_ref, connector_unref, connector_shutdown, connector_connect}; typedef struct { grpc_subchannel_factory base; @@ -143,12 +185,13 @@ static void subchannel_factory_ref(grpc_subchannel_factory *scf) { gpr_ref(&f->refs); } -static void subchannel_factory_unref(grpc_subchannel_factory *scf) { +static void subchannel_factory_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel_factory *scf) { subchannel_factory *f = (subchannel_factory *)scf; if (gpr_unref(&f->refs)) { GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, "subchannel_factory"); - GRPC_CHANNEL_INTERNAL_UNREF(f->master, "subchannel_factory"); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory"); grpc_channel_args_destroy(f->merge_args); grpc_mdctx_unref(f->mdctx); gpr_free(f); @@ -156,7 +199,8 @@ static void subchannel_factory_unref(grpc_subchannel_factory *scf) { } static grpc_subchannel *subchannel_factory_create_subchannel( - grpc_subchannel_factory *scf, grpc_subchannel_args *args) { + grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *scf, + grpc_subchannel_args *args) { subchannel_factory *f = (subchannel_factory *)scf; connector *c = gpr_malloc(sizeof(*c)); grpc_channel_args *final_args = @@ -165,12 +209,15 @@ static grpc_subchannel *subchannel_factory_create_subchannel( memset(c, 0, sizeof(*c)); c->base.vtable = &connector_vtable; c->security_connector = f->security_connector; + c->mdctx = f->mdctx; + gpr_mu_init(&c->mu); + grpc_mdctx_ref(c->mdctx); gpr_ref_init(&c->refs, 1); - args->mdctx = f->mdctx; args->args = final_args; args->master = f->master; + args->mdctx = f->mdctx; s = grpc_subchannel_create(&c->base, args); - grpc_connector_unref(&c->base); + grpc_connector_unref(exec_ctx, &c->base); grpc_channel_args_destroy(final_args); return s; } @@ -191,32 +238,40 @@ grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, grpc_arg connector_arg; grpc_channel_args *args_copy; grpc_channel_args *new_args_from_connector; - grpc_channel_security_connector *connector; + grpc_channel_security_connector *security_connector; grpc_mdctx *mdctx; grpc_resolver *resolver; subchannel_factory *f; #define MAX_FILTERS 3 const grpc_channel_filter *filters[MAX_FILTERS]; - int n = 0; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + size_t n = 0; + GRPC_API_TRACE( + "grpc_secure_channel_create(creds=%p, target=%s, args=%p, " + "reserved=%p)", + 4, (creds, target, args, reserved)); GPR_ASSERT(reserved == NULL); + if (grpc_find_security_connector_in_args(args) != NULL) { gpr_log(GPR_ERROR, "Cannot set security context in channel args."); + grpc_exec_ctx_finish(&exec_ctx); return grpc_lame_client_channel_create( target, GRPC_STATUS_INVALID_ARGUMENT, "Security connector exists in channel args."); } if (grpc_credentials_create_security_connector( - creds, target, args, NULL, &connector, &new_args_from_connector) != - GRPC_SECURITY_OK) { + creds, target, args, NULL, &security_connector, + &new_args_from_connector) != GRPC_SECURITY_OK) { + grpc_exec_ctx_finish(&exec_ctx); return grpc_lame_client_channel_create( target, GRPC_STATUS_INVALID_ARGUMENT, "Failed to create security connector."); } mdctx = grpc_mdctx_create(); - connector_arg = grpc_security_connector_to_arg(&connector->base); + connector_arg = grpc_security_connector_to_arg(&security_connector->base); args_copy = grpc_channel_args_copy_and_add( new_args_from_connector != NULL ? new_args_from_connector : args, &connector_arg, 1); @@ -227,34 +282,37 @@ grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, filters[n++] = &grpc_client_channel_filter; GPR_ASSERT(n <= MAX_FILTERS); - channel = - grpc_channel_create_from_filters(target, filters, n, args_copy, mdctx, 1); + channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n, + args_copy, mdctx, 1); f = gpr_malloc(sizeof(*f)); f->base.vtable = &subchannel_factory_vtable; gpr_ref_init(&f->refs, 1); grpc_mdctx_ref(mdctx); f->mdctx = mdctx; - GRPC_SECURITY_CONNECTOR_REF(&connector->base, "subchannel_factory"); - f->security_connector = connector; + GRPC_SECURITY_CONNECTOR_REF(&security_connector->base, "subchannel_factory"); + f->security_connector = security_connector; f->merge_args = grpc_channel_args_copy(args_copy); f->master = channel; GRPC_CHANNEL_INTERNAL_REF(channel, "subchannel_factory"); resolver = grpc_resolver_create(target, &f->base); if (!resolver) { + grpc_exec_ctx_finish(&exec_ctx); return NULL; } - grpc_client_channel_set_resolver(grpc_channel_get_channel_stack(channel), - resolver); - GRPC_RESOLVER_UNREF(resolver, "create"); - grpc_subchannel_factory_unref(&f->base); - GRPC_SECURITY_CONNECTOR_UNREF(&connector->base, "channel_create"); + grpc_client_channel_set_resolver( + &exec_ctx, grpc_channel_get_channel_stack(channel), resolver); + GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "create"); + grpc_subchannel_factory_unref(&exec_ctx, &f->base); + GRPC_SECURITY_CONNECTOR_UNREF(&security_connector->base, "channel_create"); grpc_channel_args_destroy(args_copy); if (new_args_from_connector != NULL) { grpc_channel_args_destroy(new_args_from_connector); } + grpc_exec_ctx_finish(&exec_ctx); + return channel; } diff --git a/src/core/surface/server.c b/src/core/surface/server.c index 292bf6fab8..819226278d 100644 --- a/src/core/surface/server.c +++ b/src/core/surface/server.c @@ -33,6 +33,7 @@ #include "src/core/surface/server.h" +#include <limits.h> #include <stdlib.h> #include <string.h> @@ -47,6 +48,7 @@ #include "src/core/iomgr/iomgr.h" #include "src/core/support/stack_lockfree.h" #include "src/core/support/string.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/call.h" #include "src/core/surface/channel.h" #include "src/core/surface/completion_queue.h" @@ -55,10 +57,12 @@ typedef struct listener { void *arg; - void (*start)(grpc_server *server, void *arg, grpc_pollset **pollsets, - size_t pollset_count); - void (*destroy)(grpc_server *server, void *arg); + void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_pollset **pollsets, size_t pollset_count); + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_closure *closure); struct listener *next; + grpc_closure destroy_done; } listener; typedef struct call_data call_data; @@ -112,8 +116,8 @@ struct channel_data { channel_registered_method *registered_methods; gpr_uint32 registered_method_slots; gpr_uint32 registered_method_max_probes; - grpc_iomgr_closure finish_destroy_channel_closure; - grpc_iomgr_closure channel_connectivity_changed; + grpc_closure finish_destroy_channel_closure; + grpc_closure channel_connectivity_changed; }; typedef struct shutdown_tag { @@ -152,10 +156,10 @@ struct call_data { grpc_stream_op_buffer *recv_ops; grpc_stream_state *recv_state; - grpc_iomgr_closure *on_done_recv; + grpc_closure *on_done_recv; - grpc_iomgr_closure server_on_recv; - grpc_iomgr_closure kill_zombie_closure; + grpc_closure server_on_recv; + grpc_closure kill_zombie_closure; call_data *pending_next; }; @@ -180,7 +184,7 @@ typedef struct { struct grpc_server { size_t channel_filter_count; - const grpc_channel_filter **channel_filters; + grpc_channel_filter const **channel_filters; grpc_channel_args *channel_args; grpc_completion_queue **cqs; @@ -203,7 +207,7 @@ struct grpc_server { gpr_stack_lockfree *request_freelist; /** requested call backing data */ requested_call *requested_calls; - int max_requested_calls; + size_t max_requested_calls; gpr_atm shutdown_flag; gpr_uint8 shutdown_published; @@ -223,12 +227,13 @@ struct grpc_server { #define SERVER_FROM_CALL_ELEM(elem) \ (((channel_data *)(elem)->channel_data)->server) -static void begin_call(grpc_server *server, call_data *calld, - requested_call *rc); -static void fail_call(grpc_server *server, requested_call *rc); +static void begin_call(grpc_exec_ctx *exec_ctx, grpc_server *server, + call_data *calld, requested_call *rc); +static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server, + requested_call *rc); /* Before calling maybe_finish_shutdown, we must hold mu_global and not hold mu_call */ -static void maybe_finish_shutdown(grpc_server *server); +static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_server *server); /* * channel broadcaster @@ -251,18 +256,19 @@ static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) { } struct shutdown_cleanup_args { - grpc_iomgr_closure closure; + grpc_closure closure; gpr_slice slice; }; -static void shutdown_cleanup(void *arg, int iomgr_status_ignored) { +static void shutdown_cleanup(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_status_ignored) { struct shutdown_cleanup_args *a = arg; gpr_slice_unref(a->slice); gpr_free(a); } -static void send_shutdown(grpc_channel *channel, int send_goaway, - int send_disconnect) { +static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel, + int send_goaway, int send_disconnect) { grpc_transport_op op; struct shutdown_cleanup_args *sc; grpc_channel_element *elem; @@ -274,21 +280,22 @@ static void send_shutdown(grpc_channel *channel, int send_goaway, op.goaway_message = &sc->slice; op.goaway_status = GRPC_STATUS_OK; op.disconnect = send_disconnect; - grpc_iomgr_closure_init(&sc->closure, shutdown_cleanup, sc); + grpc_closure_init(&sc->closure, shutdown_cleanup, sc); op.on_consumed = &sc->closure; elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); - elem->filter->start_transport_op(elem, &op); + elem->filter->start_transport_op(exec_ctx, elem, &op); } -static void channel_broadcaster_shutdown(channel_broadcaster *cb, +static void channel_broadcaster_shutdown(grpc_exec_ctx *exec_ctx, + channel_broadcaster *cb, int send_goaway, int force_disconnect) { size_t i; for (i = 0; i < cb->num_channels; i++) { - send_shutdown(cb->channels[i], send_goaway, force_disconnect); - GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast"); + send_shutdown(exec_ctx, cb->channels[i], send_goaway, force_disconnect); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, cb->channels[i], "broadcast"); } gpr_free(cb->channels); } @@ -297,41 +304,41 @@ static void channel_broadcaster_shutdown(channel_broadcaster *cb, * request_matcher */ -static void request_matcher_init(request_matcher *request_matcher, - int entries) { - memset(request_matcher, 0, sizeof(*request_matcher)); - request_matcher->requests = gpr_stack_lockfree_create(entries); +static void request_matcher_init(request_matcher *rm, size_t entries) { + memset(rm, 0, sizeof(*rm)); + rm->requests = gpr_stack_lockfree_create(entries); } -static void request_matcher_destroy(request_matcher *request_matcher) { - GPR_ASSERT(gpr_stack_lockfree_pop(request_matcher->requests) == -1); - gpr_stack_lockfree_destroy(request_matcher->requests); +static void request_matcher_destroy(request_matcher *rm) { + GPR_ASSERT(gpr_stack_lockfree_pop(rm->requests) == -1); + gpr_stack_lockfree_destroy(rm->requests); } -static void kill_zombie(void *elem, int success) { +static void kill_zombie(grpc_exec_ctx *exec_ctx, void *elem, int success) { grpc_call_destroy(grpc_call_from_top_element(elem)); } -static void request_matcher_zombify_all_pending_calls( - request_matcher *request_matcher) { - while (request_matcher->pending_head) { - call_data *calld = request_matcher->pending_head; - request_matcher->pending_head = calld->pending_next; +static void request_matcher_zombify_all_pending_calls(grpc_exec_ctx *exec_ctx, + request_matcher *rm) { + while (rm->pending_head) { + call_data *calld = rm->pending_head; + rm->pending_head = calld->pending_next; gpr_mu_lock(&calld->mu_state); calld->state = ZOMBIED; gpr_mu_unlock(&calld->mu_state); - grpc_iomgr_closure_init( + grpc_closure_init( &calld->kill_zombie_closure, kill_zombie, grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0)); - grpc_iomgr_add_callback(&calld->kill_zombie_closure); + grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, 1); } } -static void request_matcher_kill_requests(grpc_server *server, +static void request_matcher_kill_requests(grpc_exec_ctx *exec_ctx, + grpc_server *server, request_matcher *rm) { int request_id; while ((request_id = gpr_stack_lockfree_pop(rm->requests)) != -1) { - fail_call(server, &server->requested_calls[request_id]); + fail_call(exec_ctx, server, &server->requested_calls[request_id]); } } @@ -343,13 +350,13 @@ static void server_ref(grpc_server *server) { gpr_ref(&server->internal_refcount); } -static void server_delete(grpc_server *server) { +static void server_delete(grpc_exec_ctx *exec_ctx, grpc_server *server) { registered_method *rm; size_t i; grpc_channel_args_destroy(server->channel_args); gpr_mu_destroy(&server->mu_global); gpr_mu_destroy(&server->mu_call); - gpr_free(server->channel_filters); + gpr_free((void *)server->channel_filters); while ((rm = server->registered_methods) != NULL) { server->registered_methods = rm->next; request_matcher_destroy(&rm->request_matcher); @@ -369,9 +376,9 @@ static void server_delete(grpc_server *server) { gpr_free(server); } -static void server_unref(grpc_server *server) { +static void server_unref(grpc_exec_ctx *exec_ctx, grpc_server *server) { if (gpr_unref(&server->internal_refcount)) { - server_delete(server); + server_delete(exec_ctx, server); } } @@ -385,26 +392,28 @@ static void orphan_channel(channel_data *chand) { chand->next = chand->prev = chand; } -static void finish_destroy_channel(void *cd, int success) { +static void finish_destroy_channel(grpc_exec_ctx *exec_ctx, void *cd, + int success) { channel_data *chand = cd; grpc_server *server = chand->server; - GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "server"); - server_unref(server); + gpr_log(GPR_DEBUG, "finish_destroy_channel: %p", chand->channel); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "server"); + server_unref(exec_ctx, server); } -static void destroy_channel(channel_data *chand) { +static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand) { if (is_channel_orphaned(chand)) return; GPR_ASSERT(chand->server != NULL); orphan_channel(chand); server_ref(chand->server); - maybe_finish_shutdown(chand->server); + maybe_finish_shutdown(exec_ctx, chand->server); chand->finish_destroy_channel_closure.cb = finish_destroy_channel; chand->finish_destroy_channel_closure.cb_arg = chand; - grpc_iomgr_add_callback(&chand->finish_destroy_channel_closure); + grpc_exec_ctx_enqueue(exec_ctx, &chand->finish_destroy_channel_closure, 1); } -static void finish_start_new_rpc(grpc_server *server, grpc_call_element *elem, - request_matcher *request_matcher) { +static void finish_start_new_rpc(grpc_exec_ctx *exec_ctx, grpc_server *server, + grpc_call_element *elem, request_matcher *rm) { call_data *calld = elem->call_data; int request_id; @@ -412,22 +421,22 @@ static void finish_start_new_rpc(grpc_server *server, grpc_call_element *elem, gpr_mu_lock(&calld->mu_state); calld->state = ZOMBIED; gpr_mu_unlock(&calld->mu_state); - grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); - grpc_iomgr_add_callback(&calld->kill_zombie_closure); + grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); + grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, 1); return; } - request_id = gpr_stack_lockfree_pop(request_matcher->requests); + request_id = gpr_stack_lockfree_pop(rm->requests); if (request_id == -1) { gpr_mu_lock(&server->mu_call); gpr_mu_lock(&calld->mu_state); calld->state = PENDING; gpr_mu_unlock(&calld->mu_state); - if (request_matcher->pending_head == NULL) { - request_matcher->pending_tail = request_matcher->pending_head = calld; + if (rm->pending_head == NULL) { + rm->pending_tail = rm->pending_head = calld; } else { - request_matcher->pending_tail->pending_next = calld; - request_matcher->pending_tail = calld; + rm->pending_tail->pending_next = calld; + rm->pending_tail = calld; } calld->pending_next = NULL; gpr_mu_unlock(&server->mu_call); @@ -435,11 +444,11 @@ static void finish_start_new_rpc(grpc_server *server, grpc_call_element *elem, gpr_mu_lock(&calld->mu_state); calld->state = ACTIVATED; gpr_mu_unlock(&calld->mu_state); - begin_call(server, calld, &server->requested_calls[request_id]); + begin_call(exec_ctx, server, calld, &server->requested_calls[request_id]); } } -static void start_new_rpc(grpc_call_element *elem) { +static void start_new_rpc(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { channel_data *chand = elem->channel_data; call_data *calld = elem->call_data; grpc_server *server = chand->server; @@ -457,7 +466,7 @@ static void start_new_rpc(grpc_call_element *elem) { if (!rm) break; if (rm->host != calld->host) continue; if (rm->method != calld->path) continue; - finish_start_new_rpc(server, elem, + finish_start_new_rpc(exec_ctx, server, elem, &rm->server_registered_method->request_matcher); return; } @@ -469,12 +478,13 @@ static void start_new_rpc(grpc_call_element *elem) { if (!rm) break; if (rm->host != NULL) continue; if (rm->method != calld->path) continue; - finish_start_new_rpc(server, elem, + finish_start_new_rpc(exec_ctx, server, elem, &rm->server_registered_method->request_matcher); return; } } - finish_start_new_rpc(server, elem, &server->unregistered_request_matcher); + finish_start_new_rpc(exec_ctx, server, elem, + &server->unregistered_request_matcher); } static int num_listeners(grpc_server *server) { @@ -486,8 +496,9 @@ static int num_listeners(grpc_server *server) { return n; } -static void done_shutdown_event(void *server, grpc_cq_completion *completion) { - server_unref(server); +static void done_shutdown_event(grpc_exec_ctx *exec_ctx, void *server, + grpc_cq_completion *completion) { + server_unref(exec_ctx, server); } static int num_channels(grpc_server *server) { @@ -500,24 +511,27 @@ static int num_channels(grpc_server *server) { return n; } -static void kill_pending_work_locked(grpc_server *server) { +static void kill_pending_work_locked(grpc_exec_ctx *exec_ctx, + grpc_server *server) { registered_method *rm; - request_matcher_kill_requests(server, &server->unregistered_request_matcher); + request_matcher_kill_requests(exec_ctx, server, + &server->unregistered_request_matcher); request_matcher_zombify_all_pending_calls( - &server->unregistered_request_matcher); + exec_ctx, &server->unregistered_request_matcher); for (rm = server->registered_methods; rm; rm = rm->next) { - request_matcher_kill_requests(server, &rm->request_matcher); - request_matcher_zombify_all_pending_calls(&rm->request_matcher); + request_matcher_kill_requests(exec_ctx, server, &rm->request_matcher); + request_matcher_zombify_all_pending_calls(exec_ctx, &rm->request_matcher); } } -static void maybe_finish_shutdown(grpc_server *server) { +static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, + grpc_server *server) { size_t i; if (!gpr_atm_acq_load(&server->shutdown_flag) || server->shutdown_published) { return; } - kill_pending_work_locked(server); + kill_pending_work_locked(exec_ctx, server); if (server->root_channel_data.next != &server->root_channel_data || server->listeners_destroyed < num_listeners(server)) { @@ -537,8 +551,8 @@ static void maybe_finish_shutdown(grpc_server *server) { server->shutdown_published = 1; for (i = 0; i < server->num_shutdown_tags; i++) { server_ref(server); - grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag, 1, - done_shutdown_event, server, + grpc_cq_end_op(exec_ctx, server->shutdown_tags[i].cq, + server->shutdown_tags[i].tag, 1, done_shutdown_event, server, &server->shutdown_tags[i].completion); } } @@ -557,7 +571,7 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { return md; } -static void server_on_recv(void *ptr, int success) { +static void server_on_recv(grpc_exec_ctx *exec_ctx, void *ptr, int success) { grpc_call_element *elem = ptr; call_data *calld = elem->call_data; gpr_timespec op_deadline; @@ -577,7 +591,7 @@ static void server_on_recv(void *ptr, int success) { } if (calld->host && calld->path) { calld->got_initial_metadata = 1; - start_new_rpc(elem); + start_new_rpc(exec_ctx, elem); } break; } @@ -593,8 +607,8 @@ static void server_on_recv(void *ptr, int success) { if (calld->state == NOT_STARTED) { calld->state = ZOMBIED; gpr_mu_unlock(&calld->mu_state); - grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); - grpc_iomgr_add_callback(&calld->kill_zombie_closure); + grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); + grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, 1); } else { gpr_mu_unlock(&calld->mu_state); } @@ -604,8 +618,8 @@ static void server_on_recv(void *ptr, int success) { if (calld->state == NOT_STARTED) { calld->state = ZOMBIED; gpr_mu_unlock(&calld->mu_state); - grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); - grpc_iomgr_add_callback(&calld->kill_zombie_closure); + grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); + grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, 1); } else if (calld->state == PENDING) { calld->state = ZOMBIED; gpr_mu_unlock(&calld->mu_state); @@ -617,7 +631,7 @@ static void server_on_recv(void *ptr, int success) { break; } - calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); + calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success); } static void server_mutate_op(grpc_call_element *elem, @@ -633,11 +647,12 @@ static void server_mutate_op(grpc_call_element *elem, } } -static void server_start_transport_stream_op(grpc_call_element *elem, +static void server_start_transport_stream_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_transport_stream_op *op) { GRPC_CALL_LOG_OP(GPR_INFO, elem, op); server_mutate_op(elem, op); - grpc_call_next_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); } static void accept_stream(void *cd, grpc_transport *transport, @@ -648,7 +663,8 @@ static void accept_stream(void *cd, grpc_transport *transport, 0, gpr_inf_future(GPR_CLOCK_MONOTONIC)); } -static void channel_connectivity_changed(void *cd, int iomgr_status_ignored) { +static void channel_connectivity_changed(grpc_exec_ctx *exec_ctx, void *cd, + int iomgr_status_ignored) { channel_data *chand = cd; grpc_server *server = chand->server; if (chand->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) { @@ -656,18 +672,19 @@ static void channel_connectivity_changed(void *cd, int iomgr_status_ignored) { memset(&op, 0, sizeof(op)); op.on_connectivity_state_change = &chand->channel_connectivity_changed, op.connectivity_state = &chand->connectivity_state; - grpc_channel_next_op(grpc_channel_stack_element( + grpc_channel_next_op(exec_ctx, + grpc_channel_stack_element( grpc_channel_get_channel_stack(chand->channel), 0), &op); } else { gpr_mu_lock(&server->mu_global); - destroy_channel(chand); + destroy_channel(exec_ctx, chand); gpr_mu_unlock(&server->mu_global); - GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "connectivity"); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "connectivity"); } } -static void init_call_elem(grpc_call_element *elem, +static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const void *server_transport_data, grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; @@ -677,14 +694,15 @@ static void init_call_elem(grpc_call_element *elem, calld->call = grpc_call_from_top_element(elem); gpr_mu_init(&calld->mu_state); - grpc_iomgr_closure_init(&calld->server_on_recv, server_on_recv, elem); + grpc_closure_init(&calld->server_on_recv, server_on_recv, elem); server_ref(chand->server); if (initial_op) server_mutate_op(elem, initial_op); } -static void destroy_call_elem(grpc_call_element *elem) { +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { channel_data *chand = elem->channel_data; call_data *calld = elem->call_data; @@ -699,10 +717,11 @@ static void destroy_call_elem(grpc_call_element *elem) { gpr_mu_destroy(&calld->mu_state); - server_unref(chand->server); + server_unref(exec_ctx, chand->server); } -static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, +static void init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, int is_first, int is_last) { @@ -711,17 +730,17 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, GPR_ASSERT(!is_last); chand->server = NULL; chand->channel = NULL; - chand->path_key = grpc_mdstr_from_string(metadata_context, ":path", 0); - chand->authority_key = - grpc_mdstr_from_string(metadata_context, ":authority", 0); + chand->path_key = grpc_mdstr_from_string(metadata_context, ":path"); + chand->authority_key = grpc_mdstr_from_string(metadata_context, ":authority"); chand->next = chand->prev = chand; chand->registered_methods = NULL; chand->connectivity_state = GRPC_CHANNEL_IDLE; - grpc_iomgr_closure_init(&chand->channel_connectivity_changed, - channel_connectivity_changed, chand); + grpc_closure_init(&chand->channel_connectivity_changed, + channel_connectivity_changed, chand); } -static void destroy_channel_elem(grpc_channel_element *elem) { +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { size_t i; channel_data *chand = elem->channel_data; if (chand->registered_methods) { @@ -740,31 +759,27 @@ static void destroy_channel_elem(grpc_channel_element *elem) { chand->next->prev = chand->prev; chand->prev->next = chand->next; chand->next = chand->prev = chand; - maybe_finish_shutdown(chand->server); + maybe_finish_shutdown(exec_ctx, chand->server); gpr_mu_unlock(&chand->server->mu_global); GRPC_MDSTR_UNREF(chand->path_key); GRPC_MDSTR_UNREF(chand->authority_key); - server_unref(chand->server); + server_unref(exec_ctx, chand->server); } } static const grpc_channel_filter server_surface_filter = { - server_start_transport_stream_op, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_call_next_get_peer, - "server", + server_start_transport_stream_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, + destroy_channel_elem, grpc_call_next_get_peer, "server", }; void grpc_server_register_completion_queue(grpc_server *server, grpc_completion_queue *cq, void *reserved) { size_t i, n; + GRPC_API_TRACE( + "grpc_server_register_completion_queue(server=%p, cq=%p, reserved=%p)", 3, + (server, cq, reserved)); GPR_ASSERT(!reserved); for (i = 0; i < server->cq_count; i++) { if (server->cqs[i] == cq) return; @@ -804,7 +819,7 @@ grpc_server *grpc_server_create_from_filters( server->request_freelist = gpr_stack_lockfree_create(server->max_requested_calls); for (i = 0; i < (size_t)server->max_requested_calls; i++) { - gpr_stack_lockfree_push(server->request_freelist, i); + gpr_stack_lockfree_push(server->request_freelist, (int)i); } request_matcher_init(&server->unregistered_request_matcher, server->max_requested_calls); @@ -817,7 +832,7 @@ grpc_server *grpc_server_create_from_filters( grpc_server_census_filter (optional) - for stats collection and tracing {passed in filter stack} grpc_connected_channel_filter - for interfacing with transports */ - server->channel_filter_count = filter_count + 1 + census_enabled; + server->channel_filter_count = filter_count + 1u + (census_enabled ? 1u : 0u); server->channel_filters = gpr_malloc(server->channel_filter_count * sizeof(grpc_channel_filter *)); server->channel_filters[0] = &server_surface_filter; @@ -825,7 +840,7 @@ grpc_server *grpc_server_create_from_filters( server->channel_filters[1] = &grpc_server_census_filter; } for (i = 0; i < filter_count; i++) { - server->channel_filters[i + 1 + census_enabled] = filters[i]; + server->channel_filters[i + 1u + (census_enabled ? 1u : 0u)] = filters[i]; } server->channel_args = grpc_channel_args_copy(args); @@ -843,6 +858,8 @@ static int streq(const char *a, const char *b) { void *grpc_server_register_method(grpc_server *server, const char *method, const char *host) { registered_method *m; + GRPC_API_TRACE("grpc_server_register_method(server=%p, method=%s, host=%s)", + 3, (server, method, host)); if (!method) { gpr_log(GPR_ERROR, "grpc_server_register_method method string cannot be NULL"); @@ -868,6 +885,9 @@ void *grpc_server_register_method(grpc_server *server, const char *method, void grpc_server_start(grpc_server *server) { listener *l; size_t i; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_start(server=%p)", 1, (server)); server->pollsets = gpr_malloc(sizeof(grpc_pollset *) * server->cq_count); for (i = 0; i < server->cq_count; i++) { @@ -875,11 +895,14 @@ void grpc_server_start(grpc_server *server) { } for (l = server->listeners; l; l = l->next) { - l->start(server, l->arg, server->pollsets, server->cq_count); + l->start(&exec_ctx, server, l->arg, server->pollsets, server->cq_count); } + + grpc_exec_ctx_finish(&exec_ctx); } -void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, +void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s, + grpc_transport *transport, grpc_channel_filter const **extra_filters, size_t num_extra_filters, grpc_mdctx *mdctx, const grpc_channel_args *args) { @@ -896,7 +919,7 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, grpc_mdstr *host; grpc_mdstr *method; gpr_uint32 hash; - gpr_uint32 slots; + size_t slots; gpr_uint32 probes; gpr_uint32 max_probes = 0; grpc_transport_op op; @@ -912,14 +935,13 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, for (i = 0; i < s->cq_count; i++) { memset(&op, 0, sizeof(op)); op.bind_pollset = grpc_cq_pollset(s->cqs[i]); - grpc_transport_perform_op(transport, &op); + grpc_transport_perform_op(exec_ctx, transport, &op); } - channel = grpc_channel_create_from_filters(NULL, filters, num_filters, args, - mdctx, 0); + channel = grpc_channel_create_from_filters(exec_ctx, NULL, filters, + num_filters, args, mdctx, 0); chand = (channel_data *)grpc_channel_stack_element( - grpc_channel_get_channel_stack(channel), 0) - ->channel_data; + grpc_channel_get_channel_stack(channel), 0)->channel_data; chand->server = s; server_ref(s); chand->channel = channel; @@ -936,11 +958,11 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, chand->registered_methods = gpr_malloc(alloc); memset(chand->registered_methods, 0, alloc); for (rm = s->registered_methods; rm; rm = rm->next) { - host = rm->host ? grpc_mdstr_from_string(mdctx, rm->host, 0) : NULL; - method = grpc_mdstr_from_string(mdctx, rm->method, 0); + host = rm->host ? grpc_mdstr_from_string(mdctx, rm->host) : NULL; + method = grpc_mdstr_from_string(mdctx, rm->method); hash = GRPC_MDSTR_KV_HASH(host ? host->hash : 0, method->hash); for (probes = 0; chand->registered_methods[(hash + probes) % slots] - .server_registered_method != NULL; + .server_registered_method != NULL; probes++) ; if (probes > max_probes) max_probes = probes; @@ -949,7 +971,8 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, crm->host = host; crm->method = method; } - chand->registered_method_slots = slots; + GPR_ASSERT(slots <= GPR_UINT32_MAX); + chand->registered_method_slots = (gpr_uint32)slots; chand->registered_method_max_probes = max_probes; } @@ -962,7 +985,7 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, chand->next->prev = chand->prev->next = chand; gpr_mu_unlock(&s->mu_global); - gpr_free(filters); + gpr_free((void *)filters); GRPC_CHANNEL_INTERNAL_REF(channel, "connectivity"); memset(&op, 0, sizeof(op)); @@ -970,20 +993,34 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, op.set_accept_stream_user_data = chand; op.on_connectivity_state_change = &chand->channel_connectivity_changed; op.connectivity_state = &chand->connectivity_state; - op.disconnect = gpr_atm_acq_load(&s->shutdown_flag); - grpc_transport_perform_op(transport, &op); + op.disconnect = gpr_atm_acq_load(&s->shutdown_flag) != 0; + grpc_transport_perform_op(exec_ctx, transport, &op); } -void done_published_shutdown(void *done_arg, grpc_cq_completion *storage) { - (void) done_arg; +void done_published_shutdown(grpc_exec_ctx *exec_ctx, void *done_arg, + grpc_cq_completion *storage) { + (void)done_arg; gpr_free(storage); } +static void listener_destroy_done(grpc_exec_ctx *exec_ctx, void *s, + int success) { + grpc_server *server = s; + gpr_mu_lock(&server->mu_global); + server->listeners_destroyed++; + maybe_finish_shutdown(exec_ctx, server); + gpr_mu_unlock(&server->mu_global); +} + void grpc_server_shutdown_and_notify(grpc_server *server, grpc_completion_queue *cq, void *tag) { listener *l; shutdown_tag *sdt; channel_broadcaster broadcaster; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", 3, + (server, cq, tag)); GRPC_SERVER_LOG_SHUTDOWN(GPR_INFO, server, cq, tag); @@ -991,10 +1028,10 @@ void grpc_server_shutdown_and_notify(grpc_server *server, gpr_mu_lock(&server->mu_global); grpc_cq_begin_op(cq); if (server->shutdown_published) { - grpc_cq_end_op(cq, tag, 1, done_published_shutdown, NULL, + grpc_cq_end_op(&exec_ctx, cq, tag, 1, done_published_shutdown, NULL, gpr_malloc(sizeof(grpc_cq_completion))); gpr_mu_unlock(&server->mu_global); - return; + goto done; } server->shutdown_tags = gpr_realloc(server->shutdown_tags, @@ -1004,50 +1041,54 @@ void grpc_server_shutdown_and_notify(grpc_server *server, sdt->cq = cq; if (gpr_atm_acq_load(&server->shutdown_flag)) { gpr_mu_unlock(&server->mu_global); - return; + goto done; } server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); channel_broadcaster_init(server, &broadcaster); + gpr_atm_rel_store(&server->shutdown_flag, 1); + /* collect all unregistered then registered calls */ gpr_mu_lock(&server->mu_call); - kill_pending_work_locked(server); + kill_pending_work_locked(&exec_ctx, server); gpr_mu_unlock(&server->mu_call); - gpr_atm_rel_store(&server->shutdown_flag, 1); - maybe_finish_shutdown(server); + maybe_finish_shutdown(&exec_ctx, server); gpr_mu_unlock(&server->mu_global); /* Shutdown listeners */ for (l = server->listeners; l; l = l->next) { - l->destroy(server, l->arg); + grpc_closure_init(&l->destroy_done, listener_destroy_done, server); + l->destroy(&exec_ctx, server, l->arg, &l->destroy_done); } - channel_broadcaster_shutdown(&broadcaster, 1, 0); -} + channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 1, 0); -void grpc_server_listener_destroy_done(void *s) { - grpc_server *server = s; - gpr_mu_lock(&server->mu_global); - server->listeners_destroyed++; - maybe_finish_shutdown(server); - gpr_mu_unlock(&server->mu_global); +done: + grpc_exec_ctx_finish(&exec_ctx); } void grpc_server_cancel_all_calls(grpc_server *server) { channel_broadcaster broadcaster; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_cancel_all_calls(server=%p)", 1, (server)); gpr_mu_lock(&server->mu_global); channel_broadcaster_init(server, &broadcaster); gpr_mu_unlock(&server->mu_global); - channel_broadcaster_shutdown(&broadcaster, 0, 1); + channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 0, 1); + grpc_exec_ctx_finish(&exec_ctx); } void grpc_server_destroy(grpc_server *server) { listener *l; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_destroy(server=%p)", 1, (server)); gpr_mu_lock(&server->mu_global); GPR_ASSERT(gpr_atm_acq_load(&server->shutdown_flag) || !server->listeners); @@ -1061,14 +1102,16 @@ void grpc_server_destroy(grpc_server *server) { gpr_mu_unlock(&server->mu_global); - server_unref(server); + server_unref(&exec_ctx, server); + grpc_exec_ctx_finish(&exec_ctx); } -void grpc_server_add_listener(grpc_server *server, void *arg, - void (*start)(grpc_server *server, void *arg, - grpc_pollset **pollsets, - size_t pollset_count), - void (*destroy)(grpc_server *server, void *arg)) { +void grpc_server_add_listener( + grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_pollset **pollsets, size_t pollset_count), + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_closure *on_done)) { listener *l = gpr_malloc(sizeof(listener)); l->arg = arg; l->start = start; @@ -1077,52 +1120,54 @@ void grpc_server_add_listener(grpc_server *server, void *arg, server->listeners = l; } -static grpc_call_error queue_call_request(grpc_server *server, +static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx, + grpc_server *server, requested_call *rc) { call_data *calld = NULL; - request_matcher *request_matcher = NULL; + request_matcher *rm = NULL; int request_id; if (gpr_atm_acq_load(&server->shutdown_flag)) { - fail_call(server, rc); + fail_call(exec_ctx, server, rc); return GRPC_CALL_OK; } request_id = gpr_stack_lockfree_pop(server->request_freelist); if (request_id == -1) { /* out of request ids: just fail this one */ - fail_call(server, rc); + fail_call(exec_ctx, server, rc); return GRPC_CALL_OK; } switch (rc->type) { case BATCH_CALL: - request_matcher = &server->unregistered_request_matcher; + rm = &server->unregistered_request_matcher; break; case REGISTERED_CALL: - request_matcher = &rc->data.registered.registered_method->request_matcher; + rm = &rc->data.registered.registered_method->request_matcher; break; } server->requested_calls[request_id] = *rc; gpr_free(rc); - if (gpr_stack_lockfree_push(request_matcher->requests, request_id)) { + if (gpr_stack_lockfree_push(rm->requests, request_id)) { /* this was the first queued request: we need to lock and start matching calls */ gpr_mu_lock(&server->mu_call); - while ((calld = request_matcher->pending_head) != NULL) { - request_id = gpr_stack_lockfree_pop(request_matcher->requests); + while ((calld = rm->pending_head) != NULL) { + request_id = gpr_stack_lockfree_pop(rm->requests); if (request_id == -1) break; - request_matcher->pending_head = calld->pending_next; + rm->pending_head = calld->pending_next; gpr_mu_unlock(&server->mu_call); gpr_mu_lock(&calld->mu_state); if (calld->state == ZOMBIED) { gpr_mu_unlock(&calld->mu_state); - grpc_iomgr_closure_init( + grpc_closure_init( &calld->kill_zombie_closure, kill_zombie, grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0)); - grpc_iomgr_add_callback(&calld->kill_zombie_closure); + grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, 1); } else { GPR_ASSERT(calld->state == PENDING); calld->state = ACTIVATED; gpr_mu_unlock(&calld->mu_state); - begin_call(server, calld, &server->requested_calls[request_id]); + begin_call(exec_ctx, server, calld, + &server->requested_calls[request_id]); } gpr_mu_lock(&server->mu_call); } @@ -1136,13 +1181,22 @@ grpc_call_error grpc_server_request_call( grpc_metadata_array *initial_metadata, grpc_completion_queue *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void *tag) { + grpc_call_error error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; requested_call *rc = gpr_malloc(sizeof(*rc)); + GRPC_API_TRACE( + "grpc_server_request_call(" + "server=%p, call=%p, details=%p, initial_metadata=%p, " + "cq_bound_to_call=%p, cq_for_notification=%p, tag%p)", + 7, (server, call, details, initial_metadata, cq_bound_to_call, + cq_for_notification, tag)); GRPC_SERVER_LOG_REQUEST_CALL(GPR_INFO, server, call, details, initial_metadata, cq_bound_to_call, cq_for_notification, tag); if (!grpc_cq_is_server_cq(cq_for_notification)) { gpr_free(rc); - return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + goto done; } grpc_cq_begin_op(cq_for_notification); details->reserved = NULL; @@ -1154,19 +1208,32 @@ grpc_call_error grpc_server_request_call( rc->call = call; rc->data.batch.details = details; rc->data.batch.initial_metadata = initial_metadata; - return queue_call_request(server, rc); + error = queue_call_request(&exec_ctx, server, rc); +done: + grpc_exec_ctx_finish(&exec_ctx); + return error; } grpc_call_error grpc_server_request_registered_call( - grpc_server *server, void *rm, grpc_call **call, gpr_timespec *deadline, + grpc_server *server, void *rmp, grpc_call **call, gpr_timespec *deadline, grpc_metadata_array *initial_metadata, grpc_byte_buffer **optional_payload, grpc_completion_queue *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void *tag) { + grpc_call_error error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; requested_call *rc = gpr_malloc(sizeof(*rc)); - registered_method *registered_method = rm; + registered_method *rm = rmp; + GRPC_API_TRACE( + "grpc_server_request_registered_call(" + "server=%p, rmp=%p, call=%p, deadline=%p, initial_metadata=%p, " + "optional_payload=%p, cq_bound_to_call=%p, cq_for_notification=%p, " + "tag=%p)", + 9, (server, rmp, call, deadline, initial_metadata, optional_payload, + cq_bound_to_call, cq_for_notification, tag)); if (!grpc_cq_is_server_cq(cq_for_notification)) { gpr_free(rc); - return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + goto done; } grpc_cq_begin_op(cq_for_notification); rc->type = REGISTERED_CALL; @@ -1175,16 +1242,21 @@ grpc_call_error grpc_server_request_registered_call( rc->cq_bound_to_call = cq_bound_to_call; rc->cq_for_notification = cq_for_notification; rc->call = call; - rc->data.registered.registered_method = registered_method; + rc->data.registered.registered_method = rm; rc->data.registered.deadline = deadline; rc->data.registered.initial_metadata = initial_metadata; rc->data.registered.optional_payload = optional_payload; - return queue_call_request(server, rc); + error = queue_call_request(&exec_ctx, server, rc); +done: + grpc_exec_ctx_finish(&exec_ctx); + return error; } -static void publish_registered_or_batch(grpc_call *call, int success, +static void publish_registered_or_batch(grpc_exec_ctx *exec_ctx, + grpc_call *call, int success, void *tag); -static void publish_was_not_set(grpc_call *call, int success, void *tag) { +static void publish_was_not_set(grpc_exec_ctx *exec_ctx, grpc_call *call, + int success, void *tag) { abort(); } @@ -1199,8 +1271,8 @@ static void cpstr(char **dest, size_t *capacity, grpc_mdstr *value) { memcpy(*dest, grpc_mdstr_as_c_string(value), len + 1); } -static void begin_call(grpc_server *server, call_data *calld, - requested_call *rc) { +static void begin_call(grpc_exec_ctx *exec_ctx, grpc_server *server, + call_data *calld, requested_call *rc) { grpc_ioreq_completion_func publish = publish_was_not_set; grpc_ioreq req[2]; grpc_ioreq *r = req; @@ -1211,7 +1283,7 @@ static void begin_call(grpc_server *server, call_data *calld, fill in the metadata array passed by the client, we need to perform an ioreq op, that should complete immediately. */ - grpc_call_set_completion_queue(calld->call, rc->cq_bound_to_call); + grpc_call_set_completion_queue(exec_ctx, calld->call, rc->cq_bound_to_call); *rc->call = calld->call; calld->cq_new = rc->cq_for_notification; switch (rc->type) { @@ -1246,25 +1318,29 @@ static void begin_call(grpc_server *server, call_data *calld, } GRPC_CALL_INTERNAL_REF(calld->call, "server"); - grpc_call_start_ioreq_and_call_back(calld->call, req, r - req, publish, rc); + grpc_call_start_ioreq_and_call_back(exec_ctx, calld->call, req, + (size_t)(r - req), publish, rc); } -static void done_request_event(void *req, grpc_cq_completion *c) { +static void done_request_event(grpc_exec_ctx *exec_ctx, void *req, + grpc_cq_completion *c) { requested_call *rc = req; grpc_server *server = rc->server; if (rc >= server->requested_calls && rc < server->requested_calls + server->max_requested_calls) { + GPR_ASSERT(rc - server->requested_calls <= INT_MAX); gpr_stack_lockfree_push(server->request_freelist, - rc - server->requested_calls); + (int)(rc - server->requested_calls)); } else { gpr_free(req); } - server_unref(server); + server_unref(exec_ctx, server); } -static void fail_call(grpc_server *server, requested_call *rc) { +static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server, + requested_call *rc) { *rc->call = NULL; switch (rc->type) { case BATCH_CALL: @@ -1275,11 +1351,12 @@ static void fail_call(grpc_server *server, requested_call *rc) { break; } server_ref(server); - grpc_cq_end_op(rc->cq_for_notification, rc->tag, 0, done_request_event, rc, - &rc->completion); + grpc_cq_end_op(exec_ctx, rc->cq_for_notification, rc->tag, 0, + done_request_event, rc, &rc->completion); } -static void publish_registered_or_batch(grpc_call *call, int success, +static void publish_registered_or_batch(grpc_exec_ctx *exec_ctx, + grpc_call *call, int success, void *prc) { grpc_call_element *elem = grpc_call_stack_element(grpc_call_get_call_stack(call), 0); @@ -1287,9 +1364,9 @@ static void publish_registered_or_batch(grpc_call *call, int success, call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; server_ref(chand->server); - grpc_cq_end_op(calld->cq_new, rc->tag, success, done_request_event, rc, - &rc->completion); - GRPC_CALL_INTERNAL_UNREF(call, "server", 0); + grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, success, done_request_event, + rc, &rc->completion); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "server"); } const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) { diff --git a/src/core/surface/server.h b/src/core/surface/server.h index c638d682bb..4c46d07679 100644 --- a/src/core/surface/server.h +++ b/src/core/surface/server.h @@ -45,17 +45,17 @@ grpc_server *grpc_server_create_from_filters( /* Add a listener to the server: when the server starts, it will call start, and when it shuts down, it will call destroy */ -void grpc_server_add_listener(grpc_server *server, void *listener, - void (*start)(grpc_server *server, void *arg, - grpc_pollset **pollsets, - size_t npollsets), - void (*destroy)(grpc_server *server, void *arg)); - -void grpc_server_listener_destroy_done(void *server); +void grpc_server_add_listener( + grpc_exec_ctx *exec_ctx, grpc_server *server, void *listener, + void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_pollset **pollsets, size_t npollsets), + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_closure *on_done)); /* Setup a transport - creates a channel stack, binds the transport to the server */ -void grpc_server_setup_transport(grpc_server *server, grpc_transport *transport, +void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *server, + grpc_transport *transport, grpc_channel_filter const **extra_filters, size_t num_extra_filters, grpc_mdctx *mdctx, const grpc_channel_args *args); diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c index 4ab845bc00..580b91573c 100644 --- a/src/core/surface/server_chttp2.c +++ b/src/core/surface/server_chttp2.c @@ -36,22 +36,24 @@ #include "src/core/channel/http_server_filter.h" #include "src/core/iomgr/resolve_address.h" #include "src/core/iomgr/tcp_server.h" +#include "src/core/surface/api_trace.h" #include "src/core/surface/server.h" #include "src/core/transport/chttp2_transport.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/useful.h> -static void setup_transport(void *server, grpc_transport *transport, - grpc_mdctx *mdctx) { +static void setup_transport(grpc_exec_ctx *exec_ctx, void *server, + grpc_transport *transport, grpc_mdctx *mdctx) { static grpc_channel_filter const *extra_filters[] = { &grpc_http_server_filter}; - grpc_server_setup_transport(server, transport, extra_filters, + grpc_server_setup_transport(exec_ctx, server, transport, extra_filters, GPR_ARRAY_SIZE(extra_filters), mdctx, grpc_server_get_channel_args(server)); } -static void new_transport(void *server, grpc_endpoint *tcp) { +static void new_transport(grpc_exec_ctx *exec_ctx, void *server, + grpc_endpoint *tcp) { /* * Beware that the call to grpc_create_chttp2_transport() has to happen before * grpc_tcp_server_destroy(). This is fine here, but similar code @@ -61,23 +63,25 @@ static void new_transport(void *server, grpc_endpoint *tcp) { */ grpc_mdctx *mdctx = grpc_mdctx_create(); grpc_transport *transport = grpc_create_chttp2_transport( - grpc_server_get_channel_args(server), tcp, mdctx, 0); - setup_transport(server, transport, mdctx); - grpc_chttp2_transport_start_reading(transport, NULL, 0); + exec_ctx, grpc_server_get_channel_args(server), tcp, mdctx, 0); + setup_transport(exec_ctx, server, transport, mdctx); + grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0); } /* Server callback: start listening on our ports */ -static void start(grpc_server *server, void *tcpp, grpc_pollset **pollsets, - size_t pollset_count) { +static void start(grpc_exec_ctx *exec_ctx, grpc_server *server, void *tcpp, + grpc_pollset **pollsets, size_t pollset_count) { grpc_tcp_server *tcp = tcpp; - grpc_tcp_server_start(tcp, pollsets, pollset_count, new_transport, server); + grpc_tcp_server_start(exec_ctx, tcp, pollsets, pollset_count, new_transport, + server); } /* Server callback: destroy the tcp listener (so we don't generate further callbacks) */ -static void destroy(grpc_server *server, void *tcpp) { +static void destroy(grpc_exec_ctx *exec_ctx, grpc_server *server, void *tcpp, + grpc_closure *destroy_done) { grpc_tcp_server *tcp = tcpp; - grpc_tcp_server_destroy(tcp, grpc_server_listener_destroy_done, server); + grpc_tcp_server_destroy(exec_ctx, tcp, destroy_done); } int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { @@ -87,6 +91,10 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { unsigned count = 0; int port_num = -1; int port_temp; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2, + (server, addr)); resolved = grpc_blocking_resolve_address(addr, "http"); if (!resolved) { @@ -123,9 +131,8 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { grpc_resolved_addresses_destroy(resolved); /* Register with the server only upon success */ - grpc_server_add_listener(server, tcp, start, destroy); - - return port_num; + grpc_server_add_listener(&exec_ctx, server, tcp, start, destroy); + goto done; /* Error path: cleanup and return */ error: @@ -133,7 +140,11 @@ error: grpc_resolved_addresses_destroy(resolved); } if (tcp) { - grpc_tcp_server_destroy(tcp, NULL, NULL); + grpc_tcp_server_destroy(&exec_ctx, tcp, NULL); } - return 0; + port_num = 0; + +done: + grpc_exec_ctx_finish(&exec_ctx); + return port_num; } diff --git a/src/core/surface/server_create.c b/src/core/surface/server_create.c index fc7ae820f5..c7811a6d88 100644 --- a/src/core/surface/server_create.c +++ b/src/core/surface/server_create.c @@ -32,13 +32,14 @@ */ #include <grpc/grpc.h> +#include "src/core/surface/api_trace.h" #include "src/core/surface/completion_queue.h" #include "src/core/surface/server.h" #include "src/core/channel/compress_filter.h" grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) { const grpc_channel_filter *filters[] = {&grpc_compress_filter}; - (void)reserved; + GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved)); return grpc_server_create_from_filters(filters, GPR_ARRAY_SIZE(filters), args); } diff --git a/src/core/surface/surface_trace.h b/src/core/surface/surface_trace.h index 2b4728e2b4..93b2859ac5 100644 --- a/src/core/surface/surface_trace.h +++ b/src/core/surface/surface_trace.h @@ -35,12 +35,11 @@ #define GRPC_INTERNAL_CORE_SURFACE_SURFACE_TRACE_H #include "src/core/debug/trace.h" +#include "src/core/surface/api_trace.h" #include <grpc/support/log.h> -extern int grpc_surface_trace; - #define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \ - if (grpc_surface_trace) { \ + if (grpc_api_trace) { \ char *_ev = grpc_event_string(event); \ gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \ gpr_free(_ev); \ diff --git a/src/core/surface/version.c b/src/core/surface/version.c index d7aaba3868..e559d51448 100644 --- a/src/core/surface/version.c +++ b/src/core/surface/version.c @@ -36,6 +36,4 @@ #include <grpc/grpc.h> -const char *grpc_version_string(void) { - return "0.10.1.0"; -} +const char *grpc_version_string(void) { return "0.11.0.0"; } diff --git a/src/core/transport/chttp2/bin_encoder.c b/src/core/transport/chttp2/bin_encoder.c index dee6dbec8b..9c9070ede4 100644 --- a/src/core/transport/chttp2/bin_encoder.c +++ b/src/core/transport/chttp2/bin_encoder.c @@ -46,18 +46,70 @@ typedef struct { gpr_uint8 length; } b64_huff_sym; -static const b64_huff_sym huff_alphabet[64] = { - {0x21, 6}, {0x5d, 7}, {0x5e, 7}, {0x5f, 7}, {0x60, 7}, {0x61, 7}, - {0x62, 7}, {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, {0x67, 7}, - {0x68, 7}, {0x69, 7}, {0x6a, 7}, {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, - {0x6e, 7}, {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, {0xfc, 8}, - {0x73, 7}, {0xfd, 8}, {0x3, 5}, {0x23, 6}, {0x4, 5}, {0x24, 6}, - {0x5, 5}, {0x25, 6}, {0x26, 6}, {0x27, 6}, {0x6, 5}, {0x74, 7}, - {0x75, 7}, {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, {0x2b, 6}, - {0x76, 7}, {0x2c, 6}, {0x8, 5}, {0x9, 5}, {0x2d, 6}, {0x77, 7}, - {0x78, 7}, {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x0, 5}, {0x1, 5}, - {0x2, 5}, {0x19, 6}, {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6}, - {0x1e, 6}, {0x1f, 6}, {0x7fb, 11}, {0x18, 6}}; +static const b64_huff_sym huff_alphabet[64] = {{0x21, 6}, + {0x5d, 7}, + {0x5e, 7}, + {0x5f, 7}, + {0x60, 7}, + {0x61, 7}, + {0x62, 7}, + {0x63, 7}, + {0x64, 7}, + {0x65, 7}, + {0x66, 7}, + {0x67, 7}, + {0x68, 7}, + {0x69, 7}, + {0x6a, 7}, + {0x6b, 7}, + {0x6c, 7}, + {0x6d, 7}, + {0x6e, 7}, + {0x6f, 7}, + {0x70, 7}, + {0x71, 7}, + {0x72, 7}, + {0xfc, 8}, + {0x73, 7}, + {0xfd, 8}, + {0x3, 5}, + {0x23, 6}, + {0x4, 5}, + {0x24, 6}, + {0x5, 5}, + {0x25, 6}, + {0x26, 6}, + {0x27, 6}, + {0x6, 5}, + {0x74, 7}, + {0x75, 7}, + {0x28, 6}, + {0x29, 6}, + {0x2a, 6}, + {0x7, 5}, + {0x2b, 6}, + {0x76, 7}, + {0x2c, 6}, + {0x8, 5}, + {0x9, 5}, + {0x2d, 6}, + {0x77, 7}, + {0x78, 7}, + {0x79, 7}, + {0x7a, 7}, + {0x7b, 7}, + {0x0, 5}, + {0x1, 5}, + {0x2, 5}, + {0x19, 6}, + {0x1a, 6}, + {0x1b, 6}, + {0x1c, 6}, + {0x1d, 6}, + {0x1e, 6}, + {0x1f, 6}, + {0x7fb, 11}, + {0x18, 6}}; static const gpr_uint8 tail_xtra[3] = {0, 2, 3}; @@ -68,7 +120,7 @@ gpr_slice grpc_chttp2_base64_encode(gpr_slice input) { size_t output_length = input_triplets * 4 + tail_xtra[tail_case]; gpr_slice output = gpr_slice_malloc(output_length); gpr_uint8 *in = GPR_SLICE_START_PTR(input); - gpr_uint8 *out = GPR_SLICE_START_PTR(output); + char *out = (char *)GPR_SLICE_START_PTR(output); size_t i; /* encode full triplets */ @@ -100,7 +152,7 @@ gpr_slice grpc_chttp2_base64_encode(gpr_slice input) { break; } - GPR_ASSERT(out == GPR_SLICE_END_PTR(output)); + GPR_ASSERT(out == (char *)GPR_SLICE_END_PTR(output)); GPR_ASSERT(in == GPR_SLICE_END_PTR(input)); return output; } @@ -128,12 +180,17 @@ gpr_slice grpc_chttp2_huffman_compress(gpr_slice input) { while (temp_length > 8) { temp_length -= 8; - *out++ = temp >> temp_length; + *out++ = (gpr_uint8)(temp >> temp_length); } } if (temp_length) { - *out++ = (temp << (8 - temp_length)) | (0xff >> temp_length); + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + *out++ = (gpr_uint8)((gpr_uint8)(temp << (8u - temp_length)) | + (gpr_uint8)(0xffu >> temp_length)); } GPR_ASSERT(out == GPR_SLICE_END_PTR(output)); @@ -150,16 +207,16 @@ typedef struct { static void enc_flush_some(huff_out *out) { while (out->temp_length > 8) { out->temp_length -= 8; - *out->out++ = out->temp >> out->temp_length; + *out->out++ = (gpr_uint8)(out->temp >> out->temp_length); } } static void enc_add2(huff_out *out, gpr_uint8 a, gpr_uint8 b) { b64_huff_sym sa = huff_alphabet[a]; b64_huff_sym sb = huff_alphabet[b]; - out->temp = - (out->temp << (sa.length + sb.length)) | (sa.bits << sb.length) | sb.bits; - out->temp_length += sa.length + sb.length; + out->temp = (out->temp << (sa.length + sb.length)) | + ((gpr_uint32)sa.bits << sb.length) | sb.bits; + out->temp_length += (gpr_uint32)sa.length + (gpr_uint32)sb.length; enc_flush_some(out); } @@ -189,8 +246,9 @@ gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input) { /* encode full triplets */ for (i = 0; i < input_triplets; i++) { - enc_add2(&out, in[0] >> 2, ((in[0] & 0x3) << 4) | (in[1] >> 4)); - enc_add2(&out, ((in[1] & 0xf) << 2) | (in[2] >> 6), in[2] & 0x3f); + enc_add2(&out, in[0] >> 2, (gpr_uint8)((in[0] & 0x3) << 4) | (in[1] >> 4)); + enc_add2(&out, (gpr_uint8)((in[1] & 0xf) << 2) | (in[2] >> 6), + (gpr_uint8)(in[2] & 0x3f)); in += 3; } @@ -199,19 +257,24 @@ gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input) { case 0: break; case 1: - enc_add2(&out, in[0] >> 2, (in[0] & 0x3) << 4); + enc_add2(&out, in[0] >> 2, (gpr_uint8)((in[0] & 0x3) << 4)); in += 1; break; case 2: - enc_add2(&out, in[0] >> 2, ((in[0] & 0x3) << 4) | (in[1] >> 4)); - enc_add1(&out, (in[1] & 0xf) << 2); + enc_add2(&out, in[0] >> 2, + (gpr_uint8)((in[0] & 0x3) << 4) | (gpr_uint8)(in[1] >> 4)); + enc_add1(&out, (gpr_uint8)((in[1] & 0xf) << 2)); in += 2; break; } if (out.temp_length) { - *out.out++ = - (out.temp << (8 - out.temp_length)) | (0xff >> out.temp_length); + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + *out.out++ = (gpr_uint8)((gpr_uint8)(out.temp << (8u - out.temp_length)) | + (gpr_uint8)(0xffu >> out.temp_length)); } GPR_ASSERT(out.out <= GPR_SLICE_END_PTR(output)); diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c index 474c3d5ee6..07179a4571 100644 --- a/src/core/transport/chttp2/frame_data.c +++ b/src/core/transport/chttp2/frame_data.c @@ -70,7 +70,8 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame( } grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); @@ -146,25 +147,26 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, stream_parsing); if ((gpr_uint32)(end - cur) == p->frame_size) { - grpc_sopb_add_slice(&p->incoming_sopb, - gpr_slice_sub(slice, cur - beg, end - beg)); + grpc_sopb_add_slice( + &p->incoming_sopb, + gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); p->state = GRPC_CHTTP2_DATA_FH_0; return GRPC_CHTTP2_PARSE_OK; } else if ((gpr_uint32)(end - cur) > p->frame_size) { - grpc_sopb_add_slice( - &p->incoming_sopb, - gpr_slice_sub(slice, cur - beg, cur + p->frame_size - beg)); + grpc_sopb_add_slice(&p->incoming_sopb, + gpr_slice_sub(slice, (size_t)(cur - beg), + (size_t)(cur + p->frame_size - beg))); cur += p->frame_size; goto fh_0; /* loop */ } else { - grpc_sopb_add_slice(&p->incoming_sopb, - gpr_slice_sub(slice, cur - beg, end - beg)); - p->frame_size -= (end - cur); + grpc_sopb_add_slice( + &p->incoming_sopb, + gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); + GPR_ASSERT((size_t)(end - cur) <= p->frame_size); + p->frame_size -= (gpr_uint32)(end - cur); return GRPC_CHTTP2_PARSE_OK; } } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - return GRPC_CHTTP2_CONNECTION_ERROR; + GPR_UNREACHABLE_CODE(return GRPC_CHTTP2_CONNECTION_ERROR); } diff --git a/src/core/transport/chttp2/frame_data.h b/src/core/transport/chttp2/frame_data.h index 23957b05ad..6762484e5b 100644 --- a/src/core/transport/chttp2/frame_data.h +++ b/src/core/transport/chttp2/frame_data.h @@ -36,6 +36,7 @@ /* Parser for GRPC streams embedded in DATA frames */ +#include "src/core/iomgr/exec_ctx.h" #include <grpc/support/slice.h> #include <grpc/support/slice_buffer.h> #include "src/core/transport/stream_op.h" @@ -73,7 +74,8 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame( /* handle a slice of a data frame - is_last indicates the last slice of a frame */ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); /* create a slice with an empty data frame and is_last set */ diff --git a/src/core/transport/chttp2/frame_goaway.c b/src/core/transport/chttp2/frame_goaway.c index 1ccbba840c..c5758bcb71 100644 --- a/src/core/transport/chttp2/frame_goaway.c +++ b/src/core/transport/chttp2/frame_goaway.c @@ -63,7 +63,8 @@ grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame( } grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); @@ -136,23 +137,22 @@ grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse( ++cur; /* fallthrough */ case GRPC_CHTTP2_GOAWAY_DEBUG: - memcpy(p->debug_data + p->debug_pos, cur, end - cur); - p->debug_pos += end - cur; + memcpy(p->debug_data + p->debug_pos, cur, (size_t)(end - cur)); + GPR_ASSERT((size_t)(end - cur) < GPR_UINT32_MAX - p->debug_pos); + p->debug_pos += (gpr_uint32)(end - cur); p->state = GRPC_CHTTP2_GOAWAY_DEBUG; if (is_last) { transport_parsing->goaway_received = 1; transport_parsing->goaway_last_stream_index = p->last_stream_id; gpr_slice_unref(transport_parsing->goaway_text); - transport_parsing->goaway_error = p->error_code; + transport_parsing->goaway_error = (grpc_status_code)p->error_code; transport_parsing->goaway_text = gpr_slice_new(p->debug_data, p->debug_length, gpr_free); p->debug_data = NULL; } return GRPC_CHTTP2_PARSE_OK; } - gpr_log(GPR_ERROR, "Should never end up here"); - abort(); - return GRPC_CHTTP2_CONNECTION_ERROR; + GPR_UNREACHABLE_CODE(return GRPC_CHTTP2_CONNECTION_ERROR); } void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code, @@ -160,12 +160,14 @@ void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code, gpr_slice_buffer *slice_buffer) { gpr_slice header = gpr_slice_malloc(9 + 4 + 4); gpr_uint8 *p = GPR_SLICE_START_PTR(header); - gpr_uint32 frame_length = 4 + 4 + GPR_SLICE_LENGTH(debug_data); + gpr_uint32 frame_length; + GPR_ASSERT(GPR_SLICE_LENGTH(debug_data) < GPR_UINT32_MAX - 4 - 4); + frame_length = 4 + 4 + (gpr_uint32)GPR_SLICE_LENGTH(debug_data); /* frame header: length */ - *p++ = frame_length >> 16; - *p++ = frame_length >> 8; - *p++ = frame_length; + *p++ = (gpr_uint8)(frame_length >> 16); + *p++ = (gpr_uint8)(frame_length >> 8); + *p++ = (gpr_uint8)(frame_length); /* frame header: type */ *p++ = GRPC_CHTTP2_FRAME_GOAWAY; /* frame header: flags */ @@ -176,15 +178,15 @@ void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code, *p++ = 0; *p++ = 0; /* payload: last stream id */ - *p++ = last_stream_id >> 24; - *p++ = last_stream_id >> 16; - *p++ = last_stream_id >> 8; - *p++ = last_stream_id; + *p++ = (gpr_uint8)(last_stream_id >> 24); + *p++ = (gpr_uint8)(last_stream_id >> 16); + *p++ = (gpr_uint8)(last_stream_id >> 8); + *p++ = (gpr_uint8)(last_stream_id); /* payload: error code */ - *p++ = error_code >> 24; - *p++ = error_code >> 16; - *p++ = error_code >> 8; - *p++ = error_code; + *p++ = (gpr_uint8)(error_code >> 24); + *p++ = (gpr_uint8)(error_code >> 16); + *p++ = (gpr_uint8)(error_code >> 8); + *p++ = (gpr_uint8)(error_code); GPR_ASSERT(p == GPR_SLICE_END_PTR(header)); gpr_slice_buffer_add(slice_buffer, header); gpr_slice_buffer_add(slice_buffer, debug_data); diff --git a/src/core/transport/chttp2/frame_goaway.h b/src/core/transport/chttp2/frame_goaway.h index 9c5edfc821..06aaa92f07 100644 --- a/src/core/transport/chttp2/frame_goaway.h +++ b/src/core/transport/chttp2/frame_goaway.h @@ -34,6 +34,7 @@ #ifndef GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_GOAWAY_H #define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_GOAWAY_H +#include "src/core/iomgr/exec_ctx.h" #include "src/core/transport/chttp2/frame.h" #include <grpc/support/port_platform.h> #include <grpc/support/slice.h> @@ -65,7 +66,8 @@ void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p); grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame( grpc_chttp2_goaway_parser *parser, gpr_uint32 length, gpr_uint8 flags); grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code, diff --git a/src/core/transport/chttp2/frame_ping.c b/src/core/transport/chttp2/frame_ping.c index 05451c7a8a..4d2c54269d 100644 --- a/src/core/transport/chttp2/frame_ping.c +++ b/src/core/transport/chttp2/frame_ping.c @@ -69,7 +69,8 @@ grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame( } grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); @@ -89,7 +90,7 @@ grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( for (ping = transport_parsing->pings.next; ping != &transport_parsing->pings; ping = ping->next) { if (0 == memcmp(p->opaque_8bytes, ping->id, 8)) { - grpc_iomgr_add_delayed_callback(ping->on_recv, 1); + grpc_exec_ctx_enqueue(exec_ctx, ping->on_recv, 1); } ping->next->prev = ping->prev; ping->prev->next = ping->next; diff --git a/src/core/transport/chttp2/frame_ping.h b/src/core/transport/chttp2/frame_ping.h index 99197e8352..2c71d0d491 100644 --- a/src/core/transport/chttp2/frame_ping.h +++ b/src/core/transport/chttp2/frame_ping.h @@ -34,6 +34,7 @@ #ifndef GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_PING_H #define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_PING_H +#include "src/core/iomgr/exec_ctx.h" #include <grpc/support/slice.h> #include "src/core/transport/chttp2/frame.h" @@ -48,7 +49,8 @@ gpr_slice grpc_chttp2_ping_create(gpr_uint8 ack, gpr_uint8 *opaque_8bytes); grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame( grpc_chttp2_ping_parser *parser, gpr_uint32 length, gpr_uint8 flags); grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_PING_H */ diff --git a/src/core/transport/chttp2/frame_rst_stream.c b/src/core/transport/chttp2/frame_rst_stream.c index a878d936c1..3cd5bcfc39 100644 --- a/src/core/transport/chttp2/frame_rst_stream.c +++ b/src/core/transport/chttp2/frame_rst_stream.c @@ -47,14 +47,14 @@ gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 id, gpr_uint32 code) { *p++ = 4; *p++ = GRPC_CHTTP2_FRAME_RST_STREAM; *p++ = 0; - *p++ = id >> 24; - *p++ = id >> 16; - *p++ = id >> 8; - *p++ = id; - *p++ = code >> 24; - *p++ = code >> 16; - *p++ = code >> 8; - *p++ = code; + *p++ = (gpr_uint8)(id >> 24); + *p++ = (gpr_uint8)(id >> 16); + *p++ = (gpr_uint8)(id >> 8); + *p++ = (gpr_uint8)(id); + *p++ = (gpr_uint8)(code >> 24); + *p++ = (gpr_uint8)(code >> 16); + *p++ = (gpr_uint8)(code >> 8); + *p++ = (gpr_uint8)(code); return slice; } @@ -71,7 +71,8 @@ grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame( } grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); diff --git a/src/core/transport/chttp2/frame_rst_stream.h b/src/core/transport/chttp2/frame_rst_stream.h index ed69e588af..92cb77c971 100644 --- a/src/core/transport/chttp2/frame_rst_stream.h +++ b/src/core/transport/chttp2/frame_rst_stream.h @@ -36,6 +36,7 @@ #include <grpc/support/slice.h> #include "src/core/transport/chttp2/frame.h" +#include "src/core/iomgr/exec_ctx.h" typedef struct { gpr_uint8 byte; @@ -47,7 +48,8 @@ gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 stream_id, gpr_uint32 code); grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame( grpc_chttp2_rst_stream_parser *parser, gpr_uint32 length, gpr_uint8 flags); grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H */ diff --git a/src/core/transport/chttp2/frame_settings.c b/src/core/transport/chttp2/frame_settings.c index d42bc000ae..395a2da452 100644 --- a/src/core/transport/chttp2/frame_settings.c +++ b/src/core/transport/chttp2/frame_settings.c @@ -61,9 +61,9 @@ const grpc_chttp2_setting_parameters static gpr_uint8 *fill_header(gpr_uint8 *out, gpr_uint32 length, gpr_uint8 flags) { - *out++ = length >> 16; - *out++ = length >> 8; - *out++ = length; + *out++ = (gpr_uint8)(length >> 16); + *out++ = (gpr_uint8)(length >> 8); + *out++ = (gpr_uint8)(length); *out++ = GRPC_CHTTP2_FRAME_SETTINGS; *out++ = flags; *out++ = 0; @@ -76,26 +76,26 @@ static gpr_uint8 *fill_header(gpr_uint8 *out, gpr_uint32 length, gpr_slice grpc_chttp2_settings_create(gpr_uint32 *old, const gpr_uint32 *new, gpr_uint32 force_mask, size_t count) { size_t i; - size_t n = 0; + gpr_uint32 n = 0; gpr_slice output; gpr_uint8 *p; for (i = 0; i < count; i++) { - n += (new[i] != old[i] || (force_mask & (1 << i)) != 0); + n += (new[i] != old[i] || (force_mask & (1u << i)) != 0); } output = gpr_slice_malloc(9 + 6 * n); p = fill_header(GPR_SLICE_START_PTR(output), 6 * n, 0); for (i = 0; i < count; i++) { - if (new[i] != old[i] || (force_mask & (1 << i)) != 0) { + if (new[i] != old[i] || (force_mask & (1u << i)) != 0) { GPR_ASSERT(i); - *p++ = i >> 8; - *p++ = i; - *p++ = new[i] >> 24; - *p++ = new[i] >> 16; - *p++ = new[i] >> 8; - *p++ = new[i]; + *p++ = (gpr_uint8)(i >> 8); + *p++ = (gpr_uint8)(i); + *p++ = (gpr_uint8)(new[i] >> 24); + *p++ = (gpr_uint8)(new[i] >> 16); + *p++ = (gpr_uint8)(new[i] >> 8); + *p++ = (gpr_uint8)(new[i]); old[i] = new[i]; } } @@ -138,7 +138,8 @@ grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame( } grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( - void *p, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *p, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { grpc_chttp2_settings_parser *parser = p; const gpr_uint8 *cur = GPR_SLICE_START_PTR(slice); @@ -162,7 +163,7 @@ grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( } return GRPC_CHTTP2_PARSE_OK; } - parser->id = ((gpr_uint16)*cur) << 8; + parser->id = (gpr_uint16)(((gpr_uint16)*cur) << 8); cur++; /* fallthrough */ case GRPC_CHTTP2_SPS_ID1: @@ -170,7 +171,7 @@ grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( parser->state = GRPC_CHTTP2_SPS_ID1; return GRPC_CHTTP2_PARSE_OK; } - parser->id |= (*cur); + parser->id = (gpr_uint16)(parser->id | (*cur)); cur++; /* fallthrough */ case GRPC_CHTTP2_SPS_VAL0: diff --git a/src/core/transport/chttp2/frame_settings.h b/src/core/transport/chttp2/frame_settings.h index 0ac68a9fa8..cf857dd602 100644 --- a/src/core/transport/chttp2/frame_settings.h +++ b/src/core/transport/chttp2/frame_settings.h @@ -37,6 +37,7 @@ #include <grpc/support/port_platform.h> #include <grpc/support/slice.h> #include "src/core/transport/chttp2/frame.h" +#include "src/core/iomgr/exec_ctx.h" typedef enum { GRPC_CHTTP2_SPS_ID0, @@ -94,7 +95,8 @@ grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame( grpc_chttp2_settings_parser *parser, gpr_uint32 length, gpr_uint8 flags, gpr_uint32 *settings); grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_SETTINGS_H */ diff --git a/src/core/transport/chttp2/frame_window_update.c b/src/core/transport/chttp2/frame_window_update.c index d624298ad2..91bbcfe2c1 100644 --- a/src/core/transport/chttp2/frame_window_update.c +++ b/src/core/transport/chttp2/frame_window_update.c @@ -48,14 +48,14 @@ gpr_slice grpc_chttp2_window_update_create(gpr_uint32 id, *p++ = 4; *p++ = GRPC_CHTTP2_FRAME_WINDOW_UPDATE; *p++ = 0; - *p++ = id >> 24; - *p++ = id >> 16; - *p++ = id >> 8; - *p++ = id; - *p++ = window_update >> 24; - *p++ = window_update >> 16; - *p++ = window_update >> 8; - *p++ = window_update; + *p++ = (gpr_uint8)(id >> 24); + *p++ = (gpr_uint8)(id >> 16); + *p++ = (gpr_uint8)(id >> 8); + *p++ = (gpr_uint8)(id); + *p++ = (gpr_uint8)(window_update >> 24); + *p++ = (gpr_uint8)(window_update >> 16); + *p++ = (gpr_uint8)(window_update >> 8); + *p++ = (gpr_uint8)(window_update); return slice; } @@ -74,7 +74,8 @@ grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( } grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); diff --git a/src/core/transport/chttp2/frame_window_update.h b/src/core/transport/chttp2/frame_window_update.h index deba801d00..fc074092ff 100644 --- a/src/core/transport/chttp2/frame_window_update.h +++ b/src/core/transport/chttp2/frame_window_update.h @@ -34,6 +34,7 @@ #ifndef GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H #define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H +#include "src/core/iomgr/exec_ctx.h" #include <grpc/support/slice.h> #include "src/core/transport/chttp2/frame.h" @@ -50,7 +51,8 @@ grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( grpc_chttp2_window_update_parser *parser, gpr_uint32 length, gpr_uint8 flags); grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H */ diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c index f8bff42ed6..20d8312d54 100644 --- a/src/core/transport/chttp2/hpack_parser.c +++ b/src/core/transport/chttp2/hpack_parser.c @@ -150,12 +150,10 @@ typedef enum { /* jump table of parse state functions -- order must match first_byte_type above */ static const grpc_chttp2_hpack_parser_state first_byte_action[] = { - parse_indexed_field, parse_indexed_field_x, - parse_lithdr_incidx, parse_lithdr_incidx_x, - parse_lithdr_incidx_v, parse_lithdr_notidx, - parse_lithdr_notidx_x, parse_lithdr_notidx_v, - parse_lithdr_nvridx, parse_lithdr_nvridx_x, - parse_lithdr_nvridx_v, parse_max_tbl_size, + parse_indexed_field, parse_indexed_field_x, parse_lithdr_incidx, + parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx, + parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx, + parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size, parse_max_tbl_size_x, parse_error}; /* indexes the first byte to a parse state function - generated by @@ -247,6 +245,7 @@ static const gpr_uint8 next_tbl[256] = { 41, 1, 1, 1, 42, 43, 1, 1, 44, 1, 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, 3, 3, 3, 45, 46, 1, 1, 2, 2, 2, 35, 3, 3, 18, 47, 2, }; + /* next state, based upon current state and the current nibble: see above. generated by gen_hpack_tables.c */ static const gpr_int16 next_sub_tbl[48 * 16] = { @@ -303,6 +302,7 @@ static const gpr_int16 next_sub_tbl[48 * 16] = { 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, }; + /* emission table: indexed like next_tbl, ultimately gives the byte to be emitted, or -1 for no byte, or 256 for end of stream @@ -327,6 +327,7 @@ static const gpr_uint16 emit_tbl[256] = { 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, }; + /* generated by gen_hpack_tables.c */ static const gpr_int16 emit_sub_tbl[249 * 16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -1085,11 +1086,13 @@ static int parse_string_prefix(grpc_chttp2_hpack_parser *p, static void append_bytes(grpc_chttp2_hpack_parser_string *str, const gpr_uint8 *data, size_t length) { if (length + str->length > str->capacity) { - str->capacity = str->length + length; + GPR_ASSERT(str->length + length <= GPR_UINT32_MAX); + str->capacity = (gpr_uint32)(str->length + length); str->str = gpr_realloc(str->str, str->capacity); } memcpy(str->str + str->length, data, length); - str->length += length; + GPR_ASSERT(length <= GPR_UINT32_MAX - str->length); + str->length += (gpr_uint32)length; } static int append_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, @@ -1099,7 +1102,7 @@ static int append_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, gpr_uint8 decoded[3]; switch ((binary_state)p->binary) { case NOT_BINARY: - append_bytes(str, cur, end - cur); + append_bytes(str, cur, (size_t)(end - cur)); return 1; b64_byte0: case B64_BYTE0: @@ -1157,15 +1160,13 @@ static int append_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, goto b64_byte3; p->base64_buffer |= bits; bits = p->base64_buffer; - decoded[0] = bits >> 16; - decoded[1] = bits >> 8; - decoded[2] = bits; + decoded[0] = (gpr_uint8)(bits >> 16); + decoded[1] = (gpr_uint8)(bits >> 8); + decoded[2] = (gpr_uint8)(bits); append_bytes(str, decoded, 3); goto b64_byte0; } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - return 1; + GPR_UNREACHABLE_CODE(return 1); } /* append a null terminator to a string */ @@ -1189,7 +1190,7 @@ static int finish_str(grpc_chttp2_hpack_parser *p) { bits & 0xffff); return 0; } - decoded[0] = bits >> 16; + decoded[0] = (gpr_uint8)(bits >> 16); append_bytes(str, decoded, 1); break; case B64_BYTE3: @@ -1199,8 +1200,8 @@ static int finish_str(grpc_chttp2_hpack_parser *p) { bits & 0xff); return 0; } - decoded[0] = bits >> 16; - decoded[1] = bits >> 8; + decoded[0] = (gpr_uint8)(bits >> 16); + decoded[1] = (gpr_uint8)(bits >> 8); append_bytes(str, decoded, 2); break; } @@ -1249,13 +1250,14 @@ static int add_str_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, static int parse_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { size_t remaining = p->strlen - p->strgot; - size_t given = end - cur; + size_t given = (size_t)(end - cur); if (remaining <= given) { return add_str_bytes(p, cur, cur + remaining) && finish_str(p) && parse_next(p, cur + remaining, end); } else { if (!add_str_bytes(p, cur, cur + given)) return 0; - p->strgot += given; + GPR_ASSERT(given <= GPR_UINT32_MAX - p->strgot); + p->strgot += (gpr_uint32)given; p->state = parse_string; return 1; } @@ -1309,9 +1311,7 @@ static int parse_value_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, return 0; } /* Add code to prevent return without value error */ - gpr_log(GPR_ERROR, "Should never reach beyond switch in parse_value_string"); - abort(); - return 0; + GPR_UNREACHABLE_CODE(return 0); } static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p, @@ -1368,14 +1368,15 @@ void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p) { int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p, const gpr_uint8 *beg, const gpr_uint8 *end) { /* TODO(ctiller): limit the distance of end from beg, and perform multiple - steps in the event of a large chunk of data to limit - stack space usage when no tail call optimization is - available */ + steps in the event of a large chunk of data to limit + stack space usage when no tail call optimization is + available */ return p->state(p, beg, end); } grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( - void *hpack_parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *hpack_parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { grpc_chttp2_hpack_parser *parser = hpack_parser; if (!grpc_chttp2_hpack_parser_parse(parser, GPR_SLICE_START_PTR(slice), diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h index c1768d9d5d..f56867016c 100644 --- a/src/core/transport/chttp2/hpack_parser.h +++ b/src/core/transport/chttp2/hpack_parser.h @@ -37,6 +37,7 @@ #include <stddef.h> #include <grpc/support/port_platform.h> +#include "src/core/iomgr/exec_ctx.h" #include "src/core/transport/chttp2/frame.h" #include "src/core/transport/chttp2/hpack_table.h" #include "src/core/transport/metadata.h" @@ -79,7 +80,7 @@ struct grpc_chttp2_hpack_parser { /* number of source bytes read for the currently parsing string */ gpr_uint32 strgot; /* huffman decoding state */ - gpr_uint16 huff_state; + gpr_int16 huff_state; /* is the string being decoded binary? */ gpr_uint8 binary; /* is the current string huffman encoded? */ @@ -107,7 +108,8 @@ int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p, /* wraps grpc_chttp2_hpack_parser_parse to provide a frame level parser for the transport */ grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( - void *hpack_parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *hpack_parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_PARSER_H */ diff --git a/src/core/transport/chttp2/hpack_table.c b/src/core/transport/chttp2/hpack_table.c index 4fc154380e..c442c2c341 100644 --- a/src/core/transport/chttp2/hpack_table.c +++ b/src/core/transport/chttp2/hpack_table.c @@ -43,68 +43,130 @@ static struct { const char *key; const char *value; } static_table[] = { - /* 0: */ {NULL, NULL}, - /* 1: */ {":authority", ""}, - /* 2: */ {":method", "GET"}, - /* 3: */ {":method", "POST"}, - /* 4: */ {":path", "/"}, - /* 5: */ {":path", "/index.html"}, - /* 6: */ {":scheme", "http"}, - /* 7: */ {":scheme", "https"}, - /* 8: */ {":status", "200"}, - /* 9: */ {":status", "204"}, - /* 10: */ {":status", "206"}, - /* 11: */ {":status", "304"}, - /* 12: */ {":status", "400"}, - /* 13: */ {":status", "404"}, - /* 14: */ {":status", "500"}, - /* 15: */ {"accept-charset", ""}, - /* 16: */ {"accept-encoding", "gzip, deflate"}, - /* 17: */ {"accept-language", ""}, - /* 18: */ {"accept-ranges", ""}, - /* 19: */ {"accept", ""}, - /* 20: */ {"access-control-allow-origin", ""}, - /* 21: */ {"age", ""}, - /* 22: */ {"allow", ""}, - /* 23: */ {"authorization", ""}, - /* 24: */ {"cache-control", ""}, - /* 25: */ {"content-disposition", ""}, - /* 26: */ {"content-encoding", ""}, - /* 27: */ {"content-language", ""}, - /* 28: */ {"content-length", ""}, - /* 29: */ {"content-location", ""}, - /* 30: */ {"content-range", ""}, - /* 31: */ {"content-type", ""}, - /* 32: */ {"cookie", ""}, - /* 33: */ {"date", ""}, - /* 34: */ {"etag", ""}, - /* 35: */ {"expect", ""}, - /* 36: */ {"expires", ""}, - /* 37: */ {"from", ""}, - /* 38: */ {"host", ""}, - /* 39: */ {"if-match", ""}, - /* 40: */ {"if-modified-since", ""}, - /* 41: */ {"if-none-match", ""}, - /* 42: */ {"if-range", ""}, - /* 43: */ {"if-unmodified-since", ""}, - /* 44: */ {"last-modified", ""}, - /* 45: */ {"link", ""}, - /* 46: */ {"location", ""}, - /* 47: */ {"max-forwards", ""}, - /* 48: */ {"proxy-authenticate", ""}, - /* 49: */ {"proxy-authorization", ""}, - /* 50: */ {"range", ""}, - /* 51: */ {"referer", ""}, - /* 52: */ {"refresh", ""}, - /* 53: */ {"retry-after", ""}, - /* 54: */ {"server", ""}, - /* 55: */ {"set-cookie", ""}, - /* 56: */ {"strict-transport-security", ""}, - /* 57: */ {"transfer-encoding", ""}, - /* 58: */ {"user-agent", ""}, - /* 59: */ {"vary", ""}, - /* 60: */ {"via", ""}, - /* 61: */ {"www-authenticate", ""}, + /* 0: */ + {NULL, NULL}, + /* 1: */ + {":authority", ""}, + /* 2: */ + {":method", "GET"}, + /* 3: */ + {":method", "POST"}, + /* 4: */ + {":path", "/"}, + /* 5: */ + {":path", "/index.html"}, + /* 6: */ + {":scheme", "http"}, + /* 7: */ + {":scheme", "https"}, + /* 8: */ + {":status", "200"}, + /* 9: */ + {":status", "204"}, + /* 10: */ + {":status", "206"}, + /* 11: */ + {":status", "304"}, + /* 12: */ + {":status", "400"}, + /* 13: */ + {":status", "404"}, + /* 14: */ + {":status", "500"}, + /* 15: */ + {"accept-charset", ""}, + /* 16: */ + {"accept-encoding", "gzip, deflate"}, + /* 17: */ + {"accept-language", ""}, + /* 18: */ + {"accept-ranges", ""}, + /* 19: */ + {"accept", ""}, + /* 20: */ + {"access-control-allow-origin", ""}, + /* 21: */ + {"age", ""}, + /* 22: */ + {"allow", ""}, + /* 23: */ + {"authorization", ""}, + /* 24: */ + {"cache-control", ""}, + /* 25: */ + {"content-disposition", ""}, + /* 26: */ + {"content-encoding", ""}, + /* 27: */ + {"content-language", ""}, + /* 28: */ + {"content-length", ""}, + /* 29: */ + {"content-location", ""}, + /* 30: */ + {"content-range", ""}, + /* 31: */ + {"content-type", ""}, + /* 32: */ + {"cookie", ""}, + /* 33: */ + {"date", ""}, + /* 34: */ + {"etag", ""}, + /* 35: */ + {"expect", ""}, + /* 36: */ + {"expires", ""}, + /* 37: */ + {"from", ""}, + /* 38: */ + {"host", ""}, + /* 39: */ + {"if-match", ""}, + /* 40: */ + {"if-modified-since", ""}, + /* 41: */ + {"if-none-match", ""}, + /* 42: */ + {"if-range", ""}, + /* 43: */ + {"if-unmodified-since", ""}, + /* 44: */ + {"last-modified", ""}, + /* 45: */ + {"link", ""}, + /* 46: */ + {"location", ""}, + /* 47: */ + {"max-forwards", ""}, + /* 48: */ + {"proxy-authenticate", ""}, + /* 49: */ + {"proxy-authorization", ""}, + /* 50: */ + {"range", ""}, + /* 51: */ + {"referer", ""}, + /* 52: */ + {"refresh", ""}, + /* 53: */ + {"retry-after", ""}, + /* 54: */ + {"server", ""}, + /* 55: */ + {"set-cookie", ""}, + /* 56: */ + {"strict-transport-security", ""}, + /* 57: */ + {"transfer-encoding", ""}, + /* 58: */ + {"user-agent", ""}, + /* 59: */ + {"vary", ""}, + /* 60: */ + {"via", ""}, + /* 61: */ + {"www-authenticate", ""}, }; void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) { @@ -131,15 +193,15 @@ void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) { } grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, - gpr_uint32 index) { + gpr_uint32 tbl_index) { /* Static table comes first, just return an entry from it */ - if (index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) { - return tbl->static_ents[index - 1]; + if (tbl_index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) { + return tbl->static_ents[tbl_index - 1]; } /* Otherwise, find the value in the list of valid entries */ - index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1); - if (index < tbl->num_ents) { - gpr_uint32 offset = (tbl->num_ents - 1 - index + tbl->first_ent) % + tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1); + if (tbl_index < tbl->num_ents) { + gpr_uint32 offset = (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % GRPC_CHTTP2_MAX_TABLE_COUNT; return tbl->ents[offset]; } @@ -150,19 +212,22 @@ grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, /* Evict one element from the table */ static void evict1(grpc_chttp2_hptbl *tbl) { grpc_mdelem *first_ent = tbl->ents[tbl->first_ent]; - tbl->mem_used -= GPR_SLICE_LENGTH(first_ent->key->slice) + - GPR_SLICE_LENGTH(first_ent->value->slice) + - GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; - tbl->first_ent = (tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT; + size_t elem_bytes = GPR_SLICE_LENGTH(first_ent->key->slice) + + GPR_SLICE_LENGTH(first_ent->value->slice) + + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; + GPR_ASSERT(elem_bytes <= tbl->mem_used); + tbl->mem_used = (gpr_uint16)(tbl->mem_used - elem_bytes); + tbl->first_ent = + (gpr_uint16)((tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT); tbl->num_ents--; GRPC_MDELEM_UNREF(first_ent); } void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { /* determine how many bytes of buffer this entry represents */ - gpr_uint16 elem_bytes = GPR_SLICE_LENGTH(md->key->slice) + - GPR_SLICE_LENGTH(md->value->slice) + - GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; + size_t elem_bytes = GPR_SLICE_LENGTH(md->key->slice) + + GPR_SLICE_LENGTH(md->value->slice) + + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; /* we can't add elements bigger than the max table size */ if (elem_bytes > tbl->max_bytes) { @@ -182,7 +247,7 @@ void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { } /* evict entries to ensure no overflow */ - while (elem_bytes > tbl->max_bytes - tbl->mem_used) { + while (elem_bytes > (size_t)tbl->max_bytes - tbl->mem_used) { evict1(tbl); } @@ -190,28 +255,30 @@ void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { tbl->ents[tbl->last_ent] = md; /* update accounting values */ - tbl->last_ent = (tbl->last_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT; + tbl->last_ent = + (gpr_uint16)((tbl->last_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT); tbl->num_ents++; - tbl->mem_used += elem_bytes; + tbl->mem_used = (gpr_uint16)(tbl->mem_used + elem_bytes); } grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { grpc_chttp2_hptbl_find_result r = {0, 0}; - int i; + gpr_uint16 i; /* See if the string is in the static table */ for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { grpc_mdelem *ent = tbl->static_ents[i]; if (md->key != ent->key) continue; - r.index = i + 1; + r.index = (gpr_uint16)(i + 1); r.has_value = md->value == ent->value; if (r.has_value) return r; } /* Scan the dynamic table */ for (i = 0; i < tbl->num_ents; i++) { - int idx = tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY; + gpr_uint16 idx = + (gpr_uint16)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY); grpc_mdelem *ent = tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]; if (md->key != ent->key) continue; diff --git a/src/core/transport/chttp2/huffsyms.c b/src/core/transport/chttp2/huffsyms.c index 6f5cf6a2a9..7b138e9b5d 100644 --- a/src/core/transport/chttp2/huffsyms.c +++ b/src/core/transport/chttp2/huffsyms.c @@ -37,69 +37,261 @@ command: :%s/.* \([0-9a-f]\+\) \[ *\([0-9]\+\)\]/{0x\1, \2},/g */ const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS] = { - {0x1ff8, 13}, {0x7fffd8, 23}, {0xfffffe2, 28}, {0xfffffe3, 28}, - {0xfffffe4, 28}, {0xfffffe5, 28}, {0xfffffe6, 28}, {0xfffffe7, 28}, - {0xfffffe8, 28}, {0xffffea, 24}, {0x3ffffffc, 30}, {0xfffffe9, 28}, - {0xfffffea, 28}, {0x3ffffffd, 30}, {0xfffffeb, 28}, {0xfffffec, 28}, - {0xfffffed, 28}, {0xfffffee, 28}, {0xfffffef, 28}, {0xffffff0, 28}, - {0xffffff1, 28}, {0xffffff2, 28}, {0x3ffffffe, 30}, {0xffffff3, 28}, - {0xffffff4, 28}, {0xffffff5, 28}, {0xffffff6, 28}, {0xffffff7, 28}, - {0xffffff8, 28}, {0xffffff9, 28}, {0xffffffa, 28}, {0xffffffb, 28}, - {0x14, 6}, {0x3f8, 10}, {0x3f9, 10}, {0xffa, 12}, - {0x1ff9, 13}, {0x15, 6}, {0xf8, 8}, {0x7fa, 11}, - {0x3fa, 10}, {0x3fb, 10}, {0xf9, 8}, {0x7fb, 11}, - {0xfa, 8}, {0x16, 6}, {0x17, 6}, {0x18, 6}, - {0x0, 5}, {0x1, 5}, {0x2, 5}, {0x19, 6}, - {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6}, - {0x1e, 6}, {0x1f, 6}, {0x5c, 7}, {0xfb, 8}, - {0x7ffc, 15}, {0x20, 6}, {0xffb, 12}, {0x3fc, 10}, - {0x1ffa, 13}, {0x21, 6}, {0x5d, 7}, {0x5e, 7}, - {0x5f, 7}, {0x60, 7}, {0x61, 7}, {0x62, 7}, - {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, - {0x67, 7}, {0x68, 7}, {0x69, 7}, {0x6a, 7}, - {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, {0x6e, 7}, - {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, - {0xfc, 8}, {0x73, 7}, {0xfd, 8}, {0x1ffb, 13}, - {0x7fff0, 19}, {0x1ffc, 13}, {0x3ffc, 14}, {0x22, 6}, - {0x7ffd, 15}, {0x3, 5}, {0x23, 6}, {0x4, 5}, - {0x24, 6}, {0x5, 5}, {0x25, 6}, {0x26, 6}, - {0x27, 6}, {0x6, 5}, {0x74, 7}, {0x75, 7}, - {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, - {0x2b, 6}, {0x76, 7}, {0x2c, 6}, {0x8, 5}, - {0x9, 5}, {0x2d, 6}, {0x77, 7}, {0x78, 7}, - {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x7ffe, 15}, - {0x7fc, 11}, {0x3ffd, 14}, {0x1ffd, 13}, {0xffffffc, 28}, - {0xfffe6, 20}, {0x3fffd2, 22}, {0xfffe7, 20}, {0xfffe8, 20}, - {0x3fffd3, 22}, {0x3fffd4, 22}, {0x3fffd5, 22}, {0x7fffd9, 23}, - {0x3fffd6, 22}, {0x7fffda, 23}, {0x7fffdb, 23}, {0x7fffdc, 23}, - {0x7fffdd, 23}, {0x7fffde, 23}, {0xffffeb, 24}, {0x7fffdf, 23}, - {0xffffec, 24}, {0xffffed, 24}, {0x3fffd7, 22}, {0x7fffe0, 23}, - {0xffffee, 24}, {0x7fffe1, 23}, {0x7fffe2, 23}, {0x7fffe3, 23}, - {0x7fffe4, 23}, {0x1fffdc, 21}, {0x3fffd8, 22}, {0x7fffe5, 23}, - {0x3fffd9, 22}, {0x7fffe6, 23}, {0x7fffe7, 23}, {0xffffef, 24}, - {0x3fffda, 22}, {0x1fffdd, 21}, {0xfffe9, 20}, {0x3fffdb, 22}, - {0x3fffdc, 22}, {0x7fffe8, 23}, {0x7fffe9, 23}, {0x1fffde, 21}, - {0x7fffea, 23}, {0x3fffdd, 22}, {0x3fffde, 22}, {0xfffff0, 24}, - {0x1fffdf, 21}, {0x3fffdf, 22}, {0x7fffeb, 23}, {0x7fffec, 23}, - {0x1fffe0, 21}, {0x1fffe1, 21}, {0x3fffe0, 22}, {0x1fffe2, 21}, - {0x7fffed, 23}, {0x3fffe1, 22}, {0x7fffee, 23}, {0x7fffef, 23}, - {0xfffea, 20}, {0x3fffe2, 22}, {0x3fffe3, 22}, {0x3fffe4, 22}, - {0x7ffff0, 23}, {0x3fffe5, 22}, {0x3fffe6, 22}, {0x7ffff1, 23}, - {0x3ffffe0, 26}, {0x3ffffe1, 26}, {0xfffeb, 20}, {0x7fff1, 19}, - {0x3fffe7, 22}, {0x7ffff2, 23}, {0x3fffe8, 22}, {0x1ffffec, 25}, - {0x3ffffe2, 26}, {0x3ffffe3, 26}, {0x3ffffe4, 26}, {0x7ffffde, 27}, - {0x7ffffdf, 27}, {0x3ffffe5, 26}, {0xfffff1, 24}, {0x1ffffed, 25}, - {0x7fff2, 19}, {0x1fffe3, 21}, {0x3ffffe6, 26}, {0x7ffffe0, 27}, - {0x7ffffe1, 27}, {0x3ffffe7, 26}, {0x7ffffe2, 27}, {0xfffff2, 24}, - {0x1fffe4, 21}, {0x1fffe5, 21}, {0x3ffffe8, 26}, {0x3ffffe9, 26}, - {0xffffffd, 28}, {0x7ffffe3, 27}, {0x7ffffe4, 27}, {0x7ffffe5, 27}, - {0xfffec, 20}, {0xfffff3, 24}, {0xfffed, 20}, {0x1fffe6, 21}, - {0x3fffe9, 22}, {0x1fffe7, 21}, {0x1fffe8, 21}, {0x7ffff3, 23}, - {0x3fffea, 22}, {0x3fffeb, 22}, {0x1ffffee, 25}, {0x1ffffef, 25}, - {0xfffff4, 24}, {0xfffff5, 24}, {0x3ffffea, 26}, {0x7ffff4, 23}, - {0x3ffffeb, 26}, {0x7ffffe6, 27}, {0x3ffffec, 26}, {0x3ffffed, 26}, - {0x7ffffe7, 27}, {0x7ffffe8, 27}, {0x7ffffe9, 27}, {0x7ffffea, 27}, - {0x7ffffeb, 27}, {0xffffffe, 28}, {0x7ffffec, 27}, {0x7ffffed, 27}, - {0x7ffffee, 27}, {0x7ffffef, 27}, {0x7fffff0, 27}, {0x3ffffee, 26}, + {0x1ff8, 13}, + {0x7fffd8, 23}, + {0xfffffe2, 28}, + {0xfffffe3, 28}, + {0xfffffe4, 28}, + {0xfffffe5, 28}, + {0xfffffe6, 28}, + {0xfffffe7, 28}, + {0xfffffe8, 28}, + {0xffffea, 24}, + {0x3ffffffc, 30}, + {0xfffffe9, 28}, + {0xfffffea, 28}, + {0x3ffffffd, 30}, + {0xfffffeb, 28}, + {0xfffffec, 28}, + {0xfffffed, 28}, + {0xfffffee, 28}, + {0xfffffef, 28}, + {0xffffff0, 28}, + {0xffffff1, 28}, + {0xffffff2, 28}, + {0x3ffffffe, 30}, + {0xffffff3, 28}, + {0xffffff4, 28}, + {0xffffff5, 28}, + {0xffffff6, 28}, + {0xffffff7, 28}, + {0xffffff8, 28}, + {0xffffff9, 28}, + {0xffffffa, 28}, + {0xffffffb, 28}, + {0x14, 6}, + {0x3f8, 10}, + {0x3f9, 10}, + {0xffa, 12}, + {0x1ff9, 13}, + {0x15, 6}, + {0xf8, 8}, + {0x7fa, 11}, + {0x3fa, 10}, + {0x3fb, 10}, + {0xf9, 8}, + {0x7fb, 11}, + {0xfa, 8}, + {0x16, 6}, + {0x17, 6}, + {0x18, 6}, + {0x0, 5}, + {0x1, 5}, + {0x2, 5}, + {0x19, 6}, + {0x1a, 6}, + {0x1b, 6}, + {0x1c, 6}, + {0x1d, 6}, + {0x1e, 6}, + {0x1f, 6}, + {0x5c, 7}, + {0xfb, 8}, + {0x7ffc, 15}, + {0x20, 6}, + {0xffb, 12}, + {0x3fc, 10}, + {0x1ffa, 13}, + {0x21, 6}, + {0x5d, 7}, + {0x5e, 7}, + {0x5f, 7}, + {0x60, 7}, + {0x61, 7}, + {0x62, 7}, + {0x63, 7}, + {0x64, 7}, + {0x65, 7}, + {0x66, 7}, + {0x67, 7}, + {0x68, 7}, + {0x69, 7}, + {0x6a, 7}, + {0x6b, 7}, + {0x6c, 7}, + {0x6d, 7}, + {0x6e, 7}, + {0x6f, 7}, + {0x70, 7}, + {0x71, 7}, + {0x72, 7}, + {0xfc, 8}, + {0x73, 7}, + {0xfd, 8}, + {0x1ffb, 13}, + {0x7fff0, 19}, + {0x1ffc, 13}, + {0x3ffc, 14}, + {0x22, 6}, + {0x7ffd, 15}, + {0x3, 5}, + {0x23, 6}, + {0x4, 5}, + {0x24, 6}, + {0x5, 5}, + {0x25, 6}, + {0x26, 6}, + {0x27, 6}, + {0x6, 5}, + {0x74, 7}, + {0x75, 7}, + {0x28, 6}, + {0x29, 6}, + {0x2a, 6}, + {0x7, 5}, + {0x2b, 6}, + {0x76, 7}, + {0x2c, 6}, + {0x8, 5}, + {0x9, 5}, + {0x2d, 6}, + {0x77, 7}, + {0x78, 7}, + {0x79, 7}, + {0x7a, 7}, + {0x7b, 7}, + {0x7ffe, 15}, + {0x7fc, 11}, + {0x3ffd, 14}, + {0x1ffd, 13}, + {0xffffffc, 28}, + {0xfffe6, 20}, + {0x3fffd2, 22}, + {0xfffe7, 20}, + {0xfffe8, 20}, + {0x3fffd3, 22}, + {0x3fffd4, 22}, + {0x3fffd5, 22}, + {0x7fffd9, 23}, + {0x3fffd6, 22}, + {0x7fffda, 23}, + {0x7fffdb, 23}, + {0x7fffdc, 23}, + {0x7fffdd, 23}, + {0x7fffde, 23}, + {0xffffeb, 24}, + {0x7fffdf, 23}, + {0xffffec, 24}, + {0xffffed, 24}, + {0x3fffd7, 22}, + {0x7fffe0, 23}, + {0xffffee, 24}, + {0x7fffe1, 23}, + {0x7fffe2, 23}, + {0x7fffe3, 23}, + {0x7fffe4, 23}, + {0x1fffdc, 21}, + {0x3fffd8, 22}, + {0x7fffe5, 23}, + {0x3fffd9, 22}, + {0x7fffe6, 23}, + {0x7fffe7, 23}, + {0xffffef, 24}, + {0x3fffda, 22}, + {0x1fffdd, 21}, + {0xfffe9, 20}, + {0x3fffdb, 22}, + {0x3fffdc, 22}, + {0x7fffe8, 23}, + {0x7fffe9, 23}, + {0x1fffde, 21}, + {0x7fffea, 23}, + {0x3fffdd, 22}, + {0x3fffde, 22}, + {0xfffff0, 24}, + {0x1fffdf, 21}, + {0x3fffdf, 22}, + {0x7fffeb, 23}, + {0x7fffec, 23}, + {0x1fffe0, 21}, + {0x1fffe1, 21}, + {0x3fffe0, 22}, + {0x1fffe2, 21}, + {0x7fffed, 23}, + {0x3fffe1, 22}, + {0x7fffee, 23}, + {0x7fffef, 23}, + {0xfffea, 20}, + {0x3fffe2, 22}, + {0x3fffe3, 22}, + {0x3fffe4, 22}, + {0x7ffff0, 23}, + {0x3fffe5, 22}, + {0x3fffe6, 22}, + {0x7ffff1, 23}, + {0x3ffffe0, 26}, + {0x3ffffe1, 26}, + {0xfffeb, 20}, + {0x7fff1, 19}, + {0x3fffe7, 22}, + {0x7ffff2, 23}, + {0x3fffe8, 22}, + {0x1ffffec, 25}, + {0x3ffffe2, 26}, + {0x3ffffe3, 26}, + {0x3ffffe4, 26}, + {0x7ffffde, 27}, + {0x7ffffdf, 27}, + {0x3ffffe5, 26}, + {0xfffff1, 24}, + {0x1ffffed, 25}, + {0x7fff2, 19}, + {0x1fffe3, 21}, + {0x3ffffe6, 26}, + {0x7ffffe0, 27}, + {0x7ffffe1, 27}, + {0x3ffffe7, 26}, + {0x7ffffe2, 27}, + {0xfffff2, 24}, + {0x1fffe4, 21}, + {0x1fffe5, 21}, + {0x3ffffe8, 26}, + {0x3ffffe9, 26}, + {0xffffffd, 28}, + {0x7ffffe3, 27}, + {0x7ffffe4, 27}, + {0x7ffffe5, 27}, + {0xfffec, 20}, + {0xfffff3, 24}, + {0xfffed, 20}, + {0x1fffe6, 21}, + {0x3fffe9, 22}, + {0x1fffe7, 21}, + {0x1fffe8, 21}, + {0x7ffff3, 23}, + {0x3fffea, 22}, + {0x3fffeb, 22}, + {0x1ffffee, 25}, + {0x1ffffef, 25}, + {0xfffff4, 24}, + {0xfffff5, 24}, + {0x3ffffea, 26}, + {0x7ffff4, 23}, + {0x3ffffeb, 26}, + {0x7ffffe6, 27}, + {0x3ffffec, 26}, + {0x3ffffed, 26}, + {0x7ffffe7, 27}, + {0x7ffffe8, 27}, + {0x7ffffe9, 27}, + {0x7ffffea, 27}, + {0x7ffffeb, 27}, + {0xffffffe, 28}, + {0x7ffffec, 27}, + {0x7ffffed, 27}, + {0x7ffffee, 27}, + {0x7ffffef, 27}, + {0x7fffff0, 27}, + {0x3ffffee, 26}, {0x3fffffff, 30}, }; diff --git a/src/core/transport/chttp2/incoming_metadata.c b/src/core/transport/chttp2/incoming_metadata.c index 974b864ffb..10c64f3356 100644 --- a/src/core/transport/chttp2/incoming_metadata.c +++ b/src/core/transport/chttp2/incoming_metadata.c @@ -122,7 +122,7 @@ void grpc_incoming_metadata_buffer_move_to_referencing_sopb( for (i = 0; i < sopb->nops; i++) { if (sopb->ops[i].type != GRPC_OP_METADATA) continue; sopb->ops[i].data.metadata.list.tail = - (void *)(delta + (gpr_intptr)sopb->ops[i].data.metadata.list.tail); + (void *)(delta + (gpr_uintptr)sopb->ops[i].data.metadata.list.tail); } src->count = 0; } @@ -171,7 +171,7 @@ void grpc_chttp2_incoming_metadata_buffer_postprocess_sopb_and_begin_live_op( size_t copy_bytes = sizeof(*buffer->elems) * new_count; GPR_ASSERT(mdidx < buffer->count); buffer->elems = gpr_malloc(copy_bytes); - memcpy(live_op_buffer->elems + mdidx, buffer->elems, copy_bytes); + memcpy(buffer->elems, live_op_buffer->elems + mdidx, copy_bytes); buffer->count = buffer->capacity = new_count; } else { buffer->elems = NULL; diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h index 42cf0ecd5b..b35f8b5d88 100644 --- a/src/core/transport/chttp2/internal.h +++ b/src/core/transport/chttp2/internal.h @@ -155,7 +155,7 @@ typedef enum { /* Outstanding ping request data */ typedef struct grpc_chttp2_outstanding_ping { gpr_uint8 id[8]; - grpc_iomgr_closure *on_recv; + grpc_closure *on_recv; struct grpc_chttp2_outstanding_ping *next; struct grpc_chttp2_outstanding_ping *prev; } grpc_chttp2_outstanding_ping; @@ -163,12 +163,9 @@ typedef struct grpc_chttp2_outstanding_ping { typedef struct { /** data to write next write */ gpr_slice_buffer qbuf; - /** queued callbacks */ - grpc_iomgr_closure *pending_closures_head; - grpc_iomgr_closure *pending_closures_tail; /** window available for us to send to peer */ - gpr_uint32 outgoing_window; + gpr_int64 outgoing_window; /** window available for peer to send to us - updated after parse */ gpr_uint32 incoming_window; /** how much window would we like to have for incoming_window */ @@ -214,6 +211,8 @@ typedef struct { grpc_chttp2_hpack_compressor hpack_compressor; /** is this a client? */ gpr_uint8 is_client; + /** callback for when writing is done */ + grpc_closure done_cb; } grpc_chttp2_transport_writing; struct grpc_chttp2_transport_parsing { @@ -267,7 +266,8 @@ struct grpc_chttp2_transport_parsing { void *parser_data; grpc_chttp2_stream_parsing *incoming_stream; grpc_chttp2_parse_error (*parser)( - void *parser_user_data, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser_user_data, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); /* received settings */ @@ -278,7 +278,7 @@ struct grpc_chttp2_transport_parsing { gpr_uint32 goaway_last_stream_index; gpr_slice goaway_text; - gpr_uint64 outgoing_window_update; + gpr_int64 outgoing_window_update; /** pings awaiting responses */ grpc_chttp2_outstanding_ping pings; @@ -291,6 +291,9 @@ struct grpc_chttp2_transport { gpr_refcount refs; char *peer_string; + /** when this drops to zero it's safe to shutdown the endpoint */ + gpr_refcount shutdown_ep_refs; + gpr_mu mu; /** is the transport destroying itself? */ @@ -328,9 +331,12 @@ struct grpc_chttp2_transport { grpc_chttp2_stream_map new_stream_map; /** closure to execute writing */ - grpc_iomgr_closure writing_action; - /** closure to start reading from the endpoint */ - grpc_iomgr_closure reading_action; + grpc_closure writing_action; + /** closure to finish reading from the endpoint */ + grpc_closure recv_data; + + /** incoming read bytes */ + gpr_slice_buffer read_buffer; /** address to place a newly accepted stream - set and unset by grpc_chttp2_parsing_accept_stream; used by init_stream to @@ -352,8 +358,8 @@ typedef struct { /** HTTP2 stream id for this stream, or zero if one has not been assigned */ gpr_uint32 id; - grpc_iomgr_closure *send_done_closure; - grpc_iomgr_closure *recv_done_closure; + grpc_closure *send_done_closure; + grpc_closure *recv_done_closure; /** window available for us to send to peer */ gpr_int64 outgoing_window; @@ -462,19 +468,23 @@ struct grpc_chttp2_stream { int grpc_chttp2_unlocking_check_writes(grpc_chttp2_transport_global *global, grpc_chttp2_transport_writing *writing); void grpc_chttp2_perform_writes( - grpc_chttp2_transport_writing *transport_writing, grpc_endpoint *endpoint); -void grpc_chttp2_terminate_writing( - grpc_chttp2_transport_writing *transport_writing, int success); -void grpc_chttp2_cleanup_writing(grpc_chttp2_transport_global *global, + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_writing *transport_writing, + grpc_endpoint *endpoint); +void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx, + void *transport_writing, int success); +void grpc_chttp2_cleanup_writing(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport_global *global, grpc_chttp2_transport_writing *writing); void grpc_chttp2_prepare_to_read(grpc_chttp2_transport_global *global, grpc_chttp2_transport_parsing *parsing); /** Process one slice of incoming data; return 1 if the connection is still viable after reading, or 0 if the connection should be torn down */ -int grpc_chttp2_perform_read(grpc_chttp2_transport_parsing *transport_parsing, +int grpc_chttp2_perform_read(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport_parsing *transport_parsing, gpr_slice slice); -void grpc_chttp2_publish_reads(grpc_chttp2_transport_global *global, +void grpc_chttp2_publish_reads(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport_global *global, grpc_chttp2_transport_parsing *parsing); /** Get a writable stream @@ -561,19 +571,14 @@ int grpc_chttp2_list_pop_read_write_state_changed( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global **stream_global); -/** schedule a closure to run without the transport lock taken */ -void grpc_chttp2_schedule_closure( - grpc_chttp2_transport_global *transport_global, grpc_iomgr_closure *closure, - int success); - grpc_chttp2_stream_parsing *grpc_chttp2_parsing_lookup_stream( grpc_chttp2_transport_parsing *transport_parsing, gpr_uint32 id); grpc_chttp2_stream_parsing *grpc_chttp2_parsing_accept_stream( grpc_chttp2_transport_parsing *transport_parsing, gpr_uint32 id); void grpc_chttp2_add_incoming_goaway( - grpc_chttp2_transport_global *transport_global, gpr_uint32 goaway_error, - gpr_slice goaway_text); + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, + gpr_uint32 goaway_error, gpr_slice goaway_text); void grpc_chttp2_register_stream(grpc_chttp2_transport *t, grpc_chttp2_stream *s); @@ -602,20 +607,21 @@ extern int grpc_flowctl_trace; else \ stmt -#define GRPC_CHTTP2_FLOWCTL_TRACE_STREAM(reason, transport, context, var, \ - delta) \ - if (!(grpc_flowctl_trace)) { \ - } else { \ - grpc_chttp2_flowctl_trace(__FILE__, __LINE__, reason, #context, #var, \ - transport->is_client, context->id, context->var, \ - delta); \ +#define GRPC_CHTTP2_FLOWCTL_TRACE_STREAM(reason, transport, context, var, \ + delta) \ + if (!(grpc_flowctl_trace)) { \ + } else { \ + grpc_chttp2_flowctl_trace(__FILE__, __LINE__, reason, #context, #var, \ + transport->is_client, context->id, \ + (gpr_int64)(context->var), (gpr_int64)(delta)); \ } -#define GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT(reason, context, var, delta) \ - if (!(grpc_flowctl_trace)) { \ - } else { \ - grpc_chttp2_flowctl_trace(__FILE__, __LINE__, reason, #context, #var, \ - context->is_client, 0, context->var, delta); \ +#define GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT(reason, context, var, delta) \ + if (!(grpc_flowctl_trace)) { \ + } else { \ + grpc_chttp2_flowctl_trace(__FILE__, __LINE__, reason, #context, #var, \ + context->is_client, 0, \ + (gpr_int64)(context->var), (gpr_int64)(delta)); \ } void grpc_chttp2_flowctl_trace(const char *file, int line, const char *reason, diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c index dc5eb18e42..5d4d8e70c4 100644 --- a/src/core/transport/chttp2/parsing.c +++ b/src/core/transport/chttp2/parsing.c @@ -35,6 +35,7 @@ #include <string.h> +#include "src/core/profiling/timers.h" #include "src/core/transport/chttp2/http2_errors.h" #include "src/core/transport/chttp2/status_conversion.h" #include "src/core/transport/chttp2/timeout_encoding.h" @@ -58,7 +59,8 @@ static int init_goaway_parser(grpc_chttp2_transport_parsing *transport_parsing); static int init_skip_frame_parser( grpc_chttp2_transport_parsing *transport_parsing, int is_header); -static int parse_frame_slice(grpc_chttp2_transport_parsing *transport_parsing, +static int parse_frame_slice(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport_parsing *transport_parsing, gpr_slice slice, int is_last); void grpc_chttp2_prepare_to_read( @@ -67,6 +69,8 @@ void grpc_chttp2_prepare_to_read( grpc_chttp2_stream_global *stream_global; grpc_chttp2_stream_parsing *stream_parsing; + GPR_TIMER_BEGIN("grpc_chttp2_prepare_to_read", 0); + transport_parsing->next_stream_id = transport_global->next_stream_id; /* update the parsing view of incoming window */ @@ -88,10 +92,12 @@ void grpc_chttp2_prepare_to_read( stream_parsing->incoming_window = stream_global->incoming_window; } } + + GPR_TIMER_END("grpc_chttp2_prepare_to_read", 0); } void grpc_chttp2_publish_reads( - grpc_chttp2_transport_global *transport_global, + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_parsing *transport_parsing) { grpc_chttp2_stream_global *stream_global; grpc_chttp2_stream_parsing *stream_parsing; @@ -130,8 +136,8 @@ void grpc_chttp2_publish_reads( /* move goaway to the global state if we received one (it will be published later */ if (transport_parsing->goaway_received) { - grpc_chttp2_add_incoming_goaway(transport_global, - transport_parsing->goaway_error, + grpc_chttp2_add_incoming_goaway(exec_ctx, transport_global, + (gpr_uint32)transport_parsing->goaway_error, transport_parsing->goaway_text); transport_parsing->goaway_text = gpr_empty_slice(); transport_parsing->goaway_received = 0; @@ -194,7 +200,9 @@ void grpc_chttp2_publish_reads( GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "parsed", transport_parsing, stream_parsing, outgoing_window_update, -(gpr_int64)stream_parsing->outgoing_window_update); - stream_global->outgoing_window += stream_parsing->outgoing_window_update; + GPR_ASSERT(stream_parsing->outgoing_window_update <= GPR_UINT32_MAX); + stream_global->outgoing_window += + (gpr_uint32)stream_parsing->outgoing_window_update; stream_parsing->outgoing_window_update = 0; is_zero = stream_global->outgoing_window <= 0; if (was_zero && !is_zero) { @@ -211,7 +219,7 @@ void grpc_chttp2_publish_reads( if (stream_parsing->saw_rst_stream) { stream_global->cancelled = 1; stream_global->cancelled_status = grpc_chttp2_http2_error_to_grpc_status( - stream_parsing->rst_stream_reason); + (grpc_chttp2_error_code)stream_parsing->rst_stream_reason); if (stream_parsing->rst_stream_reason == GRPC_CHTTP2_NO_ERROR) { stream_global->published_cancelled = 1; } @@ -232,7 +240,8 @@ void grpc_chttp2_publish_reads( } } -int grpc_chttp2_perform_read(grpc_chttp2_transport_parsing *transport_parsing, +int grpc_chttp2_perform_read(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport_parsing *transport_parsing, gpr_slice slice) { gpr_uint8 *beg = GPR_SLICE_START_PTR(slice); gpr_uint8 *end = GPR_SLICE_END_PTR(slice); @@ -362,7 +371,8 @@ int grpc_chttp2_perform_read(grpc_chttp2_transport_parsing *transport_parsing, transport_parsing->incoming_stream_id; } if (transport_parsing->incoming_frame_size == 0) { - if (!parse_frame_slice(transport_parsing, gpr_empty_slice(), 1)) { + if (!parse_frame_slice(exec_ctx, transport_parsing, gpr_empty_slice(), + 1)) { return 0; } transport_parsing->incoming_stream = NULL; @@ -379,9 +389,10 @@ int grpc_chttp2_perform_read(grpc_chttp2_transport_parsing *transport_parsing, case GRPC_DTS_FRAME: GPR_ASSERT(cur < end); if ((gpr_uint32)(end - cur) == transport_parsing->incoming_frame_size) { - if (!parse_frame_slice( - transport_parsing, - gpr_slice_sub_no_ref(slice, cur - beg, end - beg), 1)) { + if (!parse_frame_slice(exec_ctx, transport_parsing, + gpr_slice_sub_no_ref(slice, (size_t)(cur - beg), + (size_t)(end - beg)), + 1)) { return 0; } transport_parsing->deframe_state = GRPC_DTS_FH_0; @@ -389,11 +400,12 @@ int grpc_chttp2_perform_read(grpc_chttp2_transport_parsing *transport_parsing, return 1; } else if ((gpr_uint32)(end - cur) > transport_parsing->incoming_frame_size) { + size_t cur_offset = (size_t)(cur - beg); if (!parse_frame_slice( - transport_parsing, + exec_ctx, transport_parsing, gpr_slice_sub_no_ref( - slice, cur - beg, - cur + transport_parsing->incoming_frame_size - beg), + slice, cur_offset, + cur_offset + transport_parsing->incoming_frame_size), 1)) { return 0; } @@ -401,22 +413,19 @@ int grpc_chttp2_perform_read(grpc_chttp2_transport_parsing *transport_parsing, transport_parsing->incoming_stream = NULL; goto dts_fh_0; /* loop */ } else { - if (!parse_frame_slice( - transport_parsing, - gpr_slice_sub_no_ref(slice, cur - beg, end - beg), 0)) { + if (!parse_frame_slice(exec_ctx, transport_parsing, + gpr_slice_sub_no_ref(slice, (size_t)(cur - beg), + (size_t)(end - beg)), + 0)) { return 0; } - transport_parsing->incoming_frame_size -= (end - cur); + transport_parsing->incoming_frame_size -= (gpr_uint32)(end - cur); return 1; } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); + GPR_UNREACHABLE_CODE(return 0); } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - - return 0; + GPR_UNREACHABLE_CODE(return 0); } static int init_frame_parser(grpc_chttp2_transport_parsing *transport_parsing) { @@ -464,7 +473,8 @@ static int init_frame_parser(grpc_chttp2_transport_parsing *transport_parsing) { } static grpc_chttp2_parse_error skip_parser( - void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { return GRPC_CHTTP2_PARSE_OK; } @@ -474,14 +484,14 @@ static void skip_header(void *tp, grpc_mdelem *md) { GRPC_MDELEM_UNREF(md); } static int init_skip_frame_parser( grpc_chttp2_transport_parsing *transport_parsing, int is_header) { if (is_header) { - int is_eoh = transport_parsing->expect_continuation_stream_id != 0; + gpr_uint8 is_eoh = transport_parsing->expect_continuation_stream_id != 0; transport_parsing->parser = grpc_chttp2_header_parser_parse; transport_parsing->parser_data = &transport_parsing->hpack_parser; transport_parsing->hpack_parser.on_header = skip_header; transport_parsing->hpack_parser.on_header_user_data = NULL; transport_parsing->hpack_parser.is_boundary = is_eoh; transport_parsing->hpack_parser.is_eof = - is_eoh ? transport_parsing->header_eof : 0; + (gpr_uint8)(is_eoh ? transport_parsing->header_eof : 0); } else { transport_parsing->parser = skip_parser; } @@ -571,9 +581,7 @@ static int init_data_frame_parser( case GRPC_CHTTP2_CONNECTION_ERROR: return 0; } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - return 0; + GPR_UNREACHABLE_CODE(return 0); } static void free_timeout(void *p) { gpr_free(p); } @@ -617,8 +625,8 @@ static void on_header(void *tp, grpc_mdelem *md) { static int init_header_frame_parser( grpc_chttp2_transport_parsing *transport_parsing, int is_continuation) { - int is_eoh = (transport_parsing->incoming_frame_flags & - GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0; + gpr_uint8 is_eoh = (transport_parsing->incoming_frame_flags & + GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0; int via_accept = 0; grpc_chttp2_stream_parsing *stream_parsing; @@ -691,7 +699,7 @@ static int init_header_frame_parser( transport_parsing->hpack_parser.on_header_user_data = transport_parsing; transport_parsing->hpack_parser.is_boundary = is_eoh; transport_parsing->hpack_parser.is_eof = - is_eoh ? transport_parsing->header_eof : 0; + (gpr_uint8)(is_eoh ? transport_parsing->header_eof : 0); if (!is_continuation && (transport_parsing->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY)) { grpc_chttp2_hpack_parser_set_has_priority(&transport_parsing->hpack_parser); @@ -783,11 +791,12 @@ static int is_window_update_legal(gpr_int64 window_update, gpr_int64 window) { } */ -static int parse_frame_slice(grpc_chttp2_transport_parsing *transport_parsing, +static int parse_frame_slice(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport_parsing *transport_parsing, gpr_slice slice, int is_last) { grpc_chttp2_stream_parsing *stream_parsing = transport_parsing->incoming_stream; - switch (transport_parsing->parser(transport_parsing->parser_data, + switch (transport_parsing->parser(exec_ctx, transport_parsing->parser_data, transport_parsing, stream_parsing, slice, is_last)) { case GRPC_CHTTP2_PARSE_OK: @@ -810,7 +819,5 @@ static int parse_frame_slice(grpc_chttp2_transport_parsing *transport_parsing, case GRPC_CHTTP2_CONNECTION_ERROR: return 0; } - gpr_log(GPR_ERROR, "should never reach here"); - abort(); - return 0; + GPR_UNREACHABLE_CODE(return 0); } diff --git a/src/core/transport/chttp2/stream_encoder.c b/src/core/transport/chttp2/stream_encoder.c index 1ea697f71e..24a5d958b8 100644 --- a/src/core/transport/chttp2/stream_encoder.c +++ b/src/core/transport/chttp2/stream_encoder.c @@ -74,17 +74,18 @@ typedef struct { } framer_state; /* fills p (which is expected to be 9 bytes long) with a data frame header */ -static void fill_header(gpr_uint8 *p, gpr_uint8 type, gpr_uint32 id, - gpr_uint32 len, gpr_uint8 flags) { - *p++ = len >> 16; - *p++ = len >> 8; - *p++ = len; +static void fill_header(gpr_uint8 *p, gpr_uint8 type, gpr_uint32 id, size_t len, + gpr_uint8 flags) { + GPR_ASSERT(len < 16777316); + *p++ = (gpr_uint8)(len >> 16); + *p++ = (gpr_uint8)(len >> 8); + *p++ = (gpr_uint8)(len); *p++ = type; *p++ = flags; - *p++ = id >> 24; - *p++ = id >> 16; - *p++ = id >> 8; - *p++ = id; + *p++ = (gpr_uint8)(id >> 24); + *p++ = (gpr_uint8)(id >> 16); + *p++ = (gpr_uint8)(id >> 8); + *p++ = (gpr_uint8)(id); } /* finish a frame - fill in the previously reserved header */ @@ -105,11 +106,12 @@ static void finish_frame(framer_state *st, int is_header_boundary, case NONE: return; } - fill_header(GPR_SLICE_START_PTR(st->output->slices[st->header_idx]), type, - st->stream_id, - st->output->length - st->output_length_at_start_of_frame, - (is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) | - (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0)); + fill_header( + GPR_SLICE_START_PTR(st->output->slices[st->header_idx]), type, + st->stream_id, st->output->length - st->output_length_at_start_of_frame, + (gpr_uint8)( + (is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) | + (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0))); st->cur_frame_type = NONE; } @@ -134,7 +136,7 @@ static void begin_new_frame(framer_state *st, frame_type type) { space to add at least about_to_add bytes -- finishes the current frame if needed */ static void ensure_frame_type(framer_state *st, frame_type type, - int need_bytes) { + size_t need_bytes) { if (st->cur_frame_type == type && st->output->length - st->output_length_at_start_of_frame + need_bytes <= GRPC_CHTTP2_MAX_PAYLOAD_LENGTH) { @@ -174,7 +176,7 @@ static void add_header_data(framer_state *st, gpr_slice slice) { } } -static gpr_uint8 *add_tiny_header_data(framer_state *st, int len) { +static gpr_uint8 *add_tiny_header_data(framer_state *st, size_t len) { ensure_frame_type(st, HEADER, len); return gpr_slice_buffer_tiny_add(st->output, len); } @@ -185,10 +187,12 @@ static grpc_mdelem *add_elem(grpc_chttp2_hpack_compressor *c, gpr_uint32 key_hash = elem->key->hash; gpr_uint32 elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash); gpr_uint32 new_index = c->tail_remote_index + c->table_elems + 1; - gpr_uint32 elem_size = 32 + GPR_SLICE_LENGTH(elem->key->slice) + - GPR_SLICE_LENGTH(elem->value->slice); + size_t elem_size = 32 + GPR_SLICE_LENGTH(elem->key->slice) + + GPR_SLICE_LENGTH(elem->value->slice); grpc_mdelem *elem_to_unref; + GPR_ASSERT(elem_size < 65536); + /* Reserve space for this element in the remote table: if this overflows the current table, drop elements until it fits, matching the decompressor algorithm */ @@ -200,14 +204,16 @@ static grpc_mdelem *add_elem(grpc_chttp2_hpack_compressor *c, c->table_elem_size[c->tail_remote_index % GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]); GPR_ASSERT(c->table_elems > 0); - c->table_size -= c->table_elem_size[c->tail_remote_index % - GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]; + c->table_size = + (gpr_uint16)(c->table_size - + c->table_elem_size[c->tail_remote_index % + GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]); c->table_elems--; } GPR_ASSERT(c->table_elems < GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS); c->table_elem_size[new_index % GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS] = - elem_size; - c->table_size += elem_size; + (gpr_uint16)elem_size; + c->table_size = (gpr_uint16)(c->table_size + elem_size); c->table_elems++; /* Store this element into {entries,indices}_elem */ @@ -268,10 +274,11 @@ static grpc_mdelem *add_elem(grpc_chttp2_hpack_compressor *c, return elem_to_unref; } -static void emit_indexed(grpc_chttp2_hpack_compressor *c, gpr_uint32 index, +static void emit_indexed(grpc_chttp2_hpack_compressor *c, gpr_uint32 elem_index, framer_state *st) { - int len = GRPC_CHTTP2_VARINT_LENGTH(index, 1); - GRPC_CHTTP2_WRITE_VARINT(index, 1, 0x80, add_tiny_header_data(st, len), len); + gpr_uint32 len = GRPC_CHTTP2_VARINT_LENGTH(elem_index, 1); + GRPC_CHTTP2_WRITE_VARINT(elem_index, 1, 0x80, add_tiny_header_data(st, len), + len); } static gpr_slice get_wire_value(grpc_mdelem *elem, gpr_uint8 *huffman_prefix) { @@ -288,14 +295,16 @@ static gpr_slice get_wire_value(grpc_mdelem *elem, gpr_uint8 *huffman_prefix) { static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor *c, gpr_uint32 key_index, grpc_mdelem *elem, framer_state *st) { - int len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); + gpr_uint32 len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); gpr_uint8 huffman_prefix; gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); - int len_val = GPR_SLICE_LENGTH(value_slice); - int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + size_t len_val = GPR_SLICE_LENGTH(value_slice); + gpr_uint32 len_val_len; + GPR_ASSERT(len_val <= GPR_UINT32_MAX); + len_val_len = GRPC_CHTTP2_VARINT_LENGTH((gpr_uint32)len_val, 1); GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, add_tiny_header_data(st, len_pfx), len_pfx); - GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + GRPC_CHTTP2_WRITE_VARINT((gpr_uint32)len_val, 1, 0x00, add_tiny_header_data(st, len_val_len), len_val_len); add_header_data(st, gpr_slice_ref(value_slice)); } @@ -303,26 +312,30 @@ static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor *c, static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor *c, gpr_uint32 key_index, grpc_mdelem *elem, framer_state *st) { - int len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); + gpr_uint32 len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); gpr_uint8 huffman_prefix; gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); - int len_val = GPR_SLICE_LENGTH(value_slice); - int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + size_t len_val = GPR_SLICE_LENGTH(value_slice); + gpr_uint32 len_val_len; + GPR_ASSERT(len_val <= GPR_UINT32_MAX); + len_val_len = GRPC_CHTTP2_VARINT_LENGTH((gpr_uint32)len_val, 1); GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, add_tiny_header_data(st, len_pfx), len_pfx); - GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + GRPC_CHTTP2_WRITE_VARINT((gpr_uint32)len_val, 1, 0x00, add_tiny_header_data(st, len_val_len), len_val_len); add_header_data(st, gpr_slice_ref(value_slice)); } static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, framer_state *st) { - int len_key = GPR_SLICE_LENGTH(elem->key->slice); + gpr_uint32 len_key = (gpr_uint32)GPR_SLICE_LENGTH(elem->key->slice); gpr_uint8 huffman_prefix; gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); - int len_val = GPR_SLICE_LENGTH(value_slice); - int len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); - int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + gpr_uint32 len_val = (gpr_uint32)GPR_SLICE_LENGTH(value_slice); + gpr_uint32 len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + gpr_uint32 len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GPR_ASSERT(len_key <= GPR_UINT32_MAX); + GPR_ASSERT(GPR_SLICE_LENGTH(value_slice) <= GPR_UINT32_MAX); *add_tiny_header_data(st, 1) = 0x40; GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, add_tiny_header_data(st, len_key_len), len_key_len); @@ -334,12 +347,14 @@ static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c, static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, framer_state *st) { - int len_key = GPR_SLICE_LENGTH(elem->key->slice); + gpr_uint32 len_key = (gpr_uint32)GPR_SLICE_LENGTH(elem->key->slice); gpr_uint8 huffman_prefix; gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); - int len_val = GPR_SLICE_LENGTH(value_slice); - int len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); - int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + gpr_uint32 len_val = (gpr_uint32)GPR_SLICE_LENGTH(value_slice); + gpr_uint32 len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + gpr_uint32 len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GPR_ASSERT(len_key <= GPR_UINT32_MAX); + GPR_ASSERT(GPR_SLICE_LENGTH(value_slice) <= GPR_UINT32_MAX); *add_tiny_header_data(st, 1) = 0x00; GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, add_tiny_header_data(st, len_key_len), len_key_len); @@ -349,9 +364,10 @@ static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c, add_header_data(st, gpr_slice_ref(value_slice)); } -static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c, gpr_uint32 index) { +static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c, + gpr_uint32 elem_index) { return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index + - c->table_elems - index; + c->table_elems - elem_index; } /* encode an mdelem; returns metadata element to unref */ @@ -363,7 +379,7 @@ static grpc_mdelem *hpack_enc(grpc_chttp2_hpack_compressor *c, gpr_uint32 indices_key; int should_add_elem; - GPR_ASSERT (GPR_SLICE_LENGTH(elem->key->slice) > 0); + GPR_ASSERT(GPR_SLICE_LENGTH(elem->key->slice) > 0); if (GPR_SLICE_START_PTR(elem->key->slice)[0] != ':') { /* regular header */ st->seen_regular_header = 1; } else if (st->seen_regular_header != 0) { /* reserved header */ @@ -412,7 +428,7 @@ static grpc_mdelem *hpack_enc(grpc_chttp2_hpack_compressor *c, emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); return elem; } - abort(); + GPR_UNREACHABLE_CODE(return NULL); } indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)]; @@ -426,7 +442,7 @@ static grpc_mdelem *hpack_enc(grpc_chttp2_hpack_compressor *c, emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); return elem; } - abort(); + GPR_UNREACHABLE_CODE(return NULL); } /* no elem, key in the table... fall back to literal emission */ @@ -438,7 +454,7 @@ static grpc_mdelem *hpack_enc(grpc_chttp2_hpack_compressor *c, emit_lithdr_noidx_v(c, elem, st); return elem; } - abort(); + GPR_UNREACHABLE_CODE(return NULL); } #define STRLEN_LIT(x) (sizeof(x) - 1) @@ -452,7 +468,7 @@ static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline, gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str); mdelem = grpc_mdelem_from_metadata_strings( c->mdctx, GRPC_MDSTR_REF(c->timeout_key_str), - grpc_mdstr_from_string(c->mdctx, timeout_str, 0)); + grpc_mdstr_from_string(c->mdctx, timeout_str)); mdelem = hpack_enc(c, mdelem, st); if (mdelem) GRPC_MDELEM_UNREF(mdelem); } @@ -467,7 +483,7 @@ void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c, grpc_mdctx *ctx) { memset(c, 0, sizeof(*c)); c->mdctx = ctx; - c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout", 0); + c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout"); } void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) { @@ -488,7 +504,7 @@ gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count, gpr_uint32 flow_controlled_bytes_taken = 0; gpr_uint32 curop = 0; gpr_uint8 *p; - int compressed_flag_set = 0; + gpr_uint8 compressed_flag_set = 0; while (curop < *inops_count) { GPR_ASSERT(flow_controlled_bytes_taken <= max_flow_controlled_bytes); @@ -514,10 +530,10 @@ gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count, p = GPR_SLICE_START_PTR(slice); p[0] = compressed_flag_set; - p[1] = op->data.begin_message.length >> 24; - p[2] = op->data.begin_message.length >> 16; - p[3] = op->data.begin_message.length >> 8; - p[4] = op->data.begin_message.length; + p[1] = (gpr_uint8)(op->data.begin_message.length >> 24); + p[2] = (gpr_uint8)(op->data.begin_message.length >> 16); + p[3] = (gpr_uint8)(op->data.begin_message.length >> 8); + p[4] = (gpr_uint8)(op->data.begin_message.length); op->type = GRPC_OP_SLICE; op->data.slice = slice; /* fallthrough */ @@ -541,7 +557,7 @@ gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count, grpc_sopb_append(outops, op, 1); curop++; } - flow_controlled_bytes_taken += GPR_SLICE_LENGTH(slice); + flow_controlled_bytes_taken += (gpr_uint32)GPR_SLICE_LENGTH(slice); break; } } @@ -565,10 +581,9 @@ void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof, framer_state st; gpr_slice slice; grpc_stream_op *op; - gpr_uint32 max_take_size; + size_t max_take_size; gpr_uint32 curop = 0; gpr_uint32 unref_op; - grpc_mdctx *mdctx = compressor->mdctx; grpc_linked_mdelem *l; int need_unref = 0; gpr_timespec deadline; @@ -634,17 +649,15 @@ void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof, finish_frame(&st, 1, eof); if (need_unref) { - grpc_mdctx_lock(mdctx); for (unref_op = 0; unref_op < curop; unref_op++) { op = &ops[unref_op]; if (op->type != GRPC_OP_METADATA) continue; for (l = op->data.metadata.list.head; l; l = l->next) { - if (l->md) GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md); + if (l->md) GRPC_MDELEM_UNREF(l->md); } for (l = op->data.metadata.garbage.head; l; l = l->next) { - GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md); + GRPC_MDELEM_UNREF(l->md); } } - grpc_mdctx_unlock(mdctx); } } diff --git a/src/core/transport/chttp2/stream_map.c b/src/core/transport/chttp2/stream_map.c index bd16153ed1..c983105abb 100644 --- a/src/core/transport/chttp2/stream_map.c +++ b/src/core/transport/chttp2/stream_map.c @@ -149,7 +149,8 @@ static void **find(grpc_chttp2_stream_map *map, gpr_uint32 key) { min_idx = mid_idx + 1; } else if (mid_key > key) { max_idx = mid_idx; - } else /* mid_key == key */ { + } else /* mid_key == key */ + { return &values[mid_idx]; } } diff --git a/src/core/transport/chttp2/timeout_encoding.c b/src/core/transport/chttp2/timeout_encoding.c index 1dd768ada4..8a9b290ecb 100644 --- a/src/core/transport/chttp2/timeout_encoding.c +++ b/src/core/transport/chttp2/timeout_encoding.c @@ -123,7 +123,7 @@ void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer) { enc_nanos(buffer, timeout.tv_nsec); } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) { enc_micros(buffer, - timeout.tv_sec * 1000000 + + (int)(timeout.tv_sec * 1000000) + (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0))); } else { enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0)); @@ -137,14 +137,14 @@ static int is_all_whitespace(const char *p) { int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout) { gpr_uint32 x = 0; - const char *p = buffer; + const gpr_uint8 *p = (const gpr_uint8 *)buffer; int have_digit = 0; /* skip whitespace */ for (; *p == ' '; p++) ; /* decode numeric part */ for (; *p >= '0' && *p <= '9'; p++) { - gpr_uint32 xp = x * 10 + *p - '0'; + gpr_uint32 xp = x * 10u + (gpr_uint32)*p - (gpr_uint32)'0'; have_digit = 1; if (xp < x) { *timeout = gpr_inf_future(GPR_CLOCK_REALTIME); @@ -180,5 +180,5 @@ int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout) { return 0; } p++; - return is_all_whitespace(p); + return is_all_whitespace((const char *)p); } diff --git a/src/core/transport/chttp2/varint.c b/src/core/transport/chttp2/varint.c index 0722c9ada9..056f68047b 100644 --- a/src/core/transport/chttp2/varint.c +++ b/src/core/transport/chttp2/varint.c @@ -33,7 +33,7 @@ #include "src/core/transport/chttp2/varint.h" -int grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value) { +gpr_uint32 grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value) { if (tail_value < (1 << 7)) { return 2; } else if (tail_value < (1 << 14)) { @@ -48,7 +48,8 @@ int grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value) { } void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, - gpr_uint8* target, int tail_length) { + gpr_uint8* target, + gpr_uint32 tail_length) { switch (tail_length) { case 5: target[4] = (gpr_uint8)((tail_value >> 28) | 0x80); diff --git a/src/core/transport/chttp2/varint.h b/src/core/transport/chttp2/varint.h index 0a6fb55248..4dfcc76773 100644 --- a/src/core/transport/chttp2/varint.h +++ b/src/core/transport/chttp2/varint.h @@ -41,10 +41,11 @@ /* length of a value that needs varint tail encoding (it's bigger than can be bitpacked into the opcode byte) - returned value includes the length of the opcode byte */ -int grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value); +gpr_uint32 grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value); void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, - gpr_uint8* target, int tail_length); + gpr_uint8* target, + gpr_uint32 tail_length); /* maximum value that can be bitpacked with the opcode if the opcode has a prefix @@ -54,15 +55,15 @@ void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, /* length required to bitpack a value */ #define GRPC_CHTTP2_VARINT_LENGTH(n, prefix_bits) \ ((n) < GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) \ - ? 1 \ + ? 1u \ : grpc_chttp2_hpack_varint_length( \ (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits))) #define GRPC_CHTTP2_WRITE_VARINT(n, prefix_bits, prefix_or, target, length) \ do { \ gpr_uint8* tgt = target; \ - if ((length) == 1) { \ - (tgt)[0] = (prefix_or) | (n); \ + if ((length) == 1u) { \ + (tgt)[0] = (gpr_uint8)((prefix_or) | (n)); \ } else { \ (tgt)[0] = (prefix_or) | GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits); \ grpc_chttp2_hpack_write_varint_tail( \ diff --git a/src/core/transport/chttp2/writing.c b/src/core/transport/chttp2/writing.c index 123061b3fc..69ad8854ba 100644 --- a/src/core/transport/chttp2/writing.c +++ b/src/core/transport/chttp2/writing.c @@ -32,12 +32,15 @@ */ #include "src/core/transport/chttp2/internal.h" -#include "src/core/transport/chttp2/http2_errors.h" + +#include <limits.h> #include <grpc/support/log.h> +#include "src/core/profiling/timers.h" +#include "src/core/transport/chttp2/http2_errors.h" + static void finalize_outbuf(grpc_chttp2_transport_writing *transport_writing); -static void finish_write_cb(void *tw, grpc_endpoint_cb_status write_status); int grpc_chttp2_unlocking_check_writes( grpc_chttp2_transport_global *transport_global, @@ -79,12 +82,13 @@ int grpc_chttp2_unlocking_check_writes( stream_writing->send_closed = GRPC_DONT_SEND_CLOSED; if (stream_global->outgoing_sopb) { - window_delta = - grpc_chttp2_preencode(stream_global->outgoing_sopb->ops, - &stream_global->outgoing_sopb->nops, - GPR_MIN(transport_global->outgoing_window, - stream_global->outgoing_window), - &stream_writing->sopb); + window_delta = grpc_chttp2_preencode( + stream_global->outgoing_sopb->ops, + &stream_global->outgoing_sopb->nops, + (gpr_uint32)GPR_MIN(GPR_MIN(transport_global->outgoing_window, + stream_global->outgoing_window), + GPR_UINT32_MAX), + &stream_writing->sopb); GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( "write", transport_global, outgoing_window, -(gpr_int64)window_delta); GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("write", transport_global, stream_global, @@ -114,6 +118,10 @@ int grpc_chttp2_unlocking_check_writes( if (!stream_global->read_closed && stream_global->unannounced_incoming_window > 0) { + GPR_ASSERT(stream_writing->announce_window == 0); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "write", transport_writing, stream_writing, announce_window, + stream_global->unannounced_incoming_window); stream_writing->announce_window = stream_global->unannounced_incoming_window; GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( @@ -156,7 +164,8 @@ int grpc_chttp2_unlocking_check_writes( } void grpc_chttp2_perform_writes( - grpc_chttp2_transport_writing *transport_writing, grpc_endpoint *endpoint) { + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_writing *transport_writing, + grpc_endpoint *endpoint) { GPR_ASSERT(transport_writing->outbuf.count > 0 || grpc_chttp2_list_have_writing_streams(transport_writing)); @@ -165,23 +174,15 @@ void grpc_chttp2_perform_writes( GPR_ASSERT(transport_writing->outbuf.count > 0); GPR_ASSERT(endpoint); - switch (grpc_endpoint_write(endpoint, transport_writing->outbuf.slices, - transport_writing->outbuf.count, finish_write_cb, - transport_writing)) { - case GRPC_ENDPOINT_WRITE_DONE: - grpc_chttp2_terminate_writing(transport_writing, 1); - break; - case GRPC_ENDPOINT_WRITE_ERROR: - grpc_chttp2_terminate_writing(transport_writing, 0); - break; - case GRPC_ENDPOINT_WRITE_PENDING: - break; - } + grpc_endpoint_write(exec_ctx, endpoint, &transport_writing->outbuf, + &transport_writing->done_cb); } static void finalize_outbuf(grpc_chttp2_transport_writing *transport_writing) { grpc_chttp2_stream_writing *stream_writing; + GPR_TIMER_BEGIN("finalize_outbuf", 0); + while ( grpc_chttp2_list_pop_writing_stream(transport_writing, &stream_writing)) { if (stream_writing->sopb.nops > 0 || @@ -198,6 +199,9 @@ static void finalize_outbuf(grpc_chttp2_transport_writing *transport_writing) { &transport_writing->outbuf, grpc_chttp2_window_update_create(stream_writing->id, stream_writing->announce_window)); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "write", transport_writing, stream_writing, announce_window, + -(gpr_int64)stream_writing->announce_window); stream_writing->announce_window = 0; } if (stream_writing->send_closed == GRPC_SEND_CLOSED_WITH_RST_STREAM) { @@ -207,16 +211,12 @@ static void finalize_outbuf(grpc_chttp2_transport_writing *transport_writing) { } grpc_chttp2_list_add_written_stream(transport_writing, stream_writing); } -} -static void finish_write_cb(void *tw, grpc_endpoint_cb_status write_status) { - grpc_chttp2_transport_writing *transport_writing = tw; - grpc_chttp2_terminate_writing(transport_writing, - write_status == GRPC_ENDPOINT_CB_OK); + GPR_TIMER_END("finalize_outbuf", 0); } void grpc_chttp2_cleanup_writing( - grpc_chttp2_transport_global *transport_global, + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_writing *transport_writing) { grpc_chttp2_stream_writing *stream_writing; grpc_chttp2_stream_global *stream_global; @@ -235,14 +235,12 @@ void grpc_chttp2_cleanup_writing( stream_global->outgoing_sopb->nops == 0) { GPR_ASSERT(stream_global->write_state != GRPC_WRITE_STATE_QUEUED_CLOSE); stream_global->outgoing_sopb = NULL; - grpc_chttp2_schedule_closure(transport_global, - stream_global->send_done_closure, 1); + grpc_exec_ctx_enqueue(exec_ctx, stream_global->send_done_closure, 1); } } stream_global->writing_now = 0; grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); } - transport_writing->outbuf.count = 0; - transport_writing->outbuf.length = 0; + gpr_slice_buffer_reset_and_unref(&transport_writing->outbuf); } diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 1bbd210e46..effc3c4b3b 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -78,28 +78,28 @@ int grpc_flowctl_trace = 0; static const grpc_transport_vtable vtable; static void lock(grpc_chttp2_transport *t); -static void unlock(grpc_chttp2_transport *t); +static void unlock(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t); -static void unlock_check_read_write_state(grpc_chttp2_transport *t); +static void unlock_check_read_write_state(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); /* forward declarations of various callbacks that we'll build closures around */ -static void writing_action(void *t, int iomgr_success_ignored); -static void reading_action(void *t, int iomgr_success_ignored); +static void writing_action(grpc_exec_ctx *exec_ctx, void *t, + int iomgr_success_ignored); /** Set a transport level setting, and push it to our peer */ static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, gpr_uint32 value); /** Endpoint callback to process incoming data */ -static void recv_data(void *tp, gpr_slice *slices, size_t nslices, - grpc_endpoint_cb_status error); +static void recv_data(grpc_exec_ctx *exec_ctx, void *tp, int success); /** Start disconnection chain */ -static void drop_connection(grpc_chttp2_transport *t); +static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t); /** Perform a transport_op */ static void perform_stream_op_locked( - grpc_chttp2_transport_global *transport_global, + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op); /** Cancel a stream: coming from the transport API */ @@ -113,24 +113,27 @@ static void close_from_api(grpc_chttp2_transport_global *transport_global, gpr_slice *optional_message); /** Add endpoint from this transport to pollset */ -static void add_to_pollset_locked(grpc_chttp2_transport *t, +static void add_to_pollset_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, grpc_pollset *pollset); -static void add_to_pollset_set_locked(grpc_chttp2_transport *t, +static void add_to_pollset_set_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, grpc_pollset_set *pollset_set); /** Start new streams that have been created if we can */ static void maybe_start_some_streams( - grpc_chttp2_transport_global *transport_global); + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global); static void connectivity_state_set( - grpc_chttp2_transport_global *transport_global, + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_connectivity_state state, const char *reason); /* * CONSTRUCTION/DESTRUCTION/REFCOUNTING */ -static void destruct_transport(grpc_chttp2_transport *t) { +static void destruct_transport(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { size_t i; gpr_mu_lock(&t->mu); @@ -143,6 +146,7 @@ static void destruct_transport(grpc_chttp2_transport *t) { grpc_chttp2_hpack_compressor_destroy(&t->writing.hpack_compressor); gpr_slice_buffer_destroy(&t->parsing.qbuf); + gpr_slice_buffer_destroy(&t->read_buffer); grpc_chttp2_hpack_parser_destroy(&t->parsing.hpack_parser); grpc_chttp2_goaway_parser_destroy(&t->parsing.goaway_parser); @@ -158,7 +162,7 @@ static void destruct_transport(grpc_chttp2_transport *t) { grpc_chttp2_stream_map_destroy(&t->parsing_stream_map); grpc_chttp2_stream_map_destroy(&t->new_stream_map); - grpc_connectivity_state_destroy(&t->channel_callback.state_tracker); + grpc_connectivity_state_destroy(exec_ctx, &t->channel_callback.state_tracker); gpr_mu_unlock(&t->mu); gpr_mu_destroy(&t->mu); @@ -167,7 +171,7 @@ static void destruct_transport(grpc_chttp2_transport *t) { and maybe they hold resources that need to be freed */ while (t->global.pings.next != &t->global.pings) { grpc_chttp2_outstanding_ping *ping = t->global.pings.next; - grpc_iomgr_add_delayed_callback(ping->on_recv, 0); + grpc_exec_ctx_enqueue(exec_ctx, ping->on_recv, 0); ping->next->prev = ping->prev; ping->prev->next = ping->next; gpr_free(ping); @@ -181,13 +185,13 @@ static void destruct_transport(grpc_chttp2_transport *t) { #ifdef REFCOUNTING_DEBUG #define REF_TRANSPORT(t, r) ref_transport(t, r, __FILE__, __LINE__) -#define UNREF_TRANSPORT(t, r) unref_transport(t, r, __FILE__, __LINE__) -static void unref_transport(grpc_chttp2_transport *t, const char *reason, - const char *file, int line) { +#define UNREF_TRANSPORT(cl, t, r) unref_transport(cl, t, r, __FILE__, __LINE__) +static void unref_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + const char *reason, const char *file, int line) { gpr_log(GPR_DEBUG, "chttp2:unref:%p %d->%d %s [%s:%d]", t, t->refs.count, t->refs.count - 1, reason, file, line); if (!gpr_unref(&t->refs)) return; - destruct_transport(t); + destruct_transport(exec_ctx, t); } static void ref_transport(grpc_chttp2_transport *t, const char *reason, @@ -198,19 +202,19 @@ static void ref_transport(grpc_chttp2_transport *t, const char *reason, } #else #define REF_TRANSPORT(t, r) ref_transport(t) -#define UNREF_TRANSPORT(t, r) unref_transport(t) -static void unref_transport(grpc_chttp2_transport *t) { +#define UNREF_TRANSPORT(cl, t, r) unref_transport(cl, t) +static void unref_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { if (!gpr_unref(&t->refs)) return; - destruct_transport(t); + destruct_transport(exec_ctx, t); } static void ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); } #endif -static void init_transport(grpc_chttp2_transport *t, +static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, const grpc_channel_args *channel_args, grpc_endpoint *ep, grpc_mdctx *mdctx, - int is_client) { + gpr_uint8 is_client) { size_t i; int j; @@ -223,6 +227,8 @@ static void init_transport(grpc_chttp2_transport *t, t->ep = ep; /* one ref is for destroy, the other for when ep becomes NULL */ gpr_ref_init(&t->refs, 2); + /* ref is dropped at transport close() */ + gpr_ref_init(&t->shutdown_ep_refs, 1); gpr_mu_init(&t->mu); grpc_mdctx_ref(mdctx); t->peer_string = grpc_endpoint_get_peer(ep); @@ -237,24 +243,29 @@ static void init_transport(grpc_chttp2_transport *t, t->global.pings.next = t->global.pings.prev = &t->global.pings; t->parsing.is_client = is_client; t->parsing.str_grpc_timeout = - grpc_mdstr_from_string(t->metadata_context, "grpc-timeout", 0); + grpc_mdstr_from_string(t->metadata_context, "grpc-timeout"); t->parsing.deframe_state = is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0; t->writing.is_client = is_client; - grpc_connectivity_state_init(&t->channel_callback.state_tracker, - GRPC_CHANNEL_READY, "transport"); + grpc_connectivity_state_init( + &t->channel_callback.state_tracker, GRPC_CHANNEL_READY, + is_client ? "client_transport" : "server_transport"); gpr_slice_buffer_init(&t->global.qbuf); gpr_slice_buffer_init(&t->writing.outbuf); grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor, mdctx); - grpc_iomgr_closure_init(&t->writing_action, writing_action, t); - grpc_iomgr_closure_init(&t->reading_action, reading_action, t); + grpc_closure_init(&t->writing_action, writing_action, t); gpr_slice_buffer_init(&t->parsing.qbuf); grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser); grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser, t->metadata_context); + grpc_closure_init(&t->writing.done_cb, grpc_chttp2_terminate_writing, + &t->writing); + grpc_closure_init(&t->recv_data, recv_data, t); + gpr_slice_buffer_init(&t->read_buffer); + if (is_client) { gpr_slice_buffer_add( &t->global.qbuf, @@ -301,7 +312,7 @@ static void init_transport(grpc_chttp2_transport *t, GRPC_ARG_MAX_CONCURRENT_STREAMS); } else { push_setting(t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, - channel_args->args[i].value.integer); + (gpr_uint32)channel_args->args[i].value.integer); } } else if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) { @@ -315,37 +326,74 @@ static void init_transport(grpc_chttp2_transport *t, t->global.next_stream_id & 1, is_client ? "client" : "server"); } else { - t->global.next_stream_id = channel_args->args[i].value.integer; + t->global.next_stream_id = + (gpr_uint32)channel_args->args[i].value.integer; } } } } } -static void destroy_transport(grpc_transport *gt) { +static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; lock(t); t->destroying = 1; - drop_connection(t); - unlock(t); + drop_connection(exec_ctx, t); + unlock(exec_ctx, t); + + UNREF_TRANSPORT(exec_ctx, t, "destroy"); +} - UNREF_TRANSPORT(t, "destroy"); +/** block grpc_endpoint_shutdown being called until a paired + allow_endpoint_shutdown is made */ +static void prevent_endpoint_shutdown(grpc_chttp2_transport *t) { + GPR_ASSERT(t->ep); + gpr_ref(&t->shutdown_ep_refs); +} + +static void allow_endpoint_shutdown_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + if (gpr_unref(&t->shutdown_ep_refs)) { + if (t->ep) { + grpc_endpoint_shutdown(exec_ctx, t->ep); + } + } +} + +static void allow_endpoint_shutdown_unlocked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + if (gpr_unref(&t->shutdown_ep_refs)) { + gpr_mu_lock(&t->mu); + if (t->ep) { + grpc_endpoint_shutdown(exec_ctx, t->ep); + } + gpr_mu_unlock(&t->mu); + } } -static void close_transport_locked(grpc_chttp2_transport *t) { +static void destroy_endpoint(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + grpc_endpoint_destroy(exec_ctx, t->ep); + t->ep = NULL; + /* safe because we'll still have the ref for write */ + UNREF_TRANSPORT(exec_ctx, t, "disconnect"); +} + +static void close_transport_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { if (!t->closed) { t->closed = 1; - connectivity_state_set(&t->global, GRPC_CHANNEL_FATAL_FAILURE, + connectivity_state_set(exec_ctx, &t->global, GRPC_CHANNEL_FATAL_FAILURE, "close_transport"); if (t->ep) { - grpc_endpoint_shutdown(t->ep); + allow_endpoint_shutdown_locked(exec_ctx, t); } } } -static int init_stream(grpc_transport *gt, grpc_stream *gs, - const void *server_data, +static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, const void *server_data, grpc_transport_stream_op *initial_op) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; @@ -377,13 +425,15 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs, s->global.in_stream_map = 1; } - if (initial_op) perform_stream_op_locked(&t->global, &s->global, initial_op); - unlock(t); + if (initial_op) + perform_stream_op_locked(exec_ctx, &t->global, &s->global, initial_op); + unlock(exec_ctx, t); return 0; } -static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { +static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; int i; @@ -394,7 +444,7 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { s->global.id == 0); GPR_ASSERT(!s->global.in_stream_map); if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) { - close_transport_locked(t); + close_transport_locked(exec_ctx, t); } if (!t->parsing_active && s->global.id) { GPR_ASSERT(grpc_chttp2_stream_map_find(&t->parsing_stream_map, @@ -424,7 +474,7 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { grpc_chttp2_incoming_metadata_live_op_buffer_end( &s->global.outstanding_metadata); - UNREF_TRANSPORT(t, "stream"); + UNREF_TRANSPORT(exec_ctx, t, "stream"); } grpc_chttp2_stream_parsing *grpc_chttp2_parsing_lookup_stream( @@ -459,28 +509,19 @@ grpc_chttp2_stream_parsing *grpc_chttp2_parsing_accept_stream( static void lock(grpc_chttp2_transport *t) { gpr_mu_lock(&t->mu); } -static void unlock(grpc_chttp2_transport *t) { - grpc_iomgr_closure *run_closures; - - unlock_check_read_write_state(t); +static void unlock(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { + GPR_TIMER_BEGIN("unlock", 0); + unlock_check_read_write_state(exec_ctx, t); if (!t->writing_active && !t->closed && grpc_chttp2_unlocking_check_writes(&t->global, &t->writing)) { t->writing_active = 1; REF_TRANSPORT(t, "writing"); - grpc_chttp2_schedule_closure(&t->global, &t->writing_action, 1); + grpc_exec_ctx_enqueue(exec_ctx, &t->writing_action, 1); + prevent_endpoint_shutdown(t); } - run_closures = t->global.pending_closures_head; - t->global.pending_closures_head = NULL; - t->global.pending_closures_tail = NULL; - gpr_mu_unlock(&t->mu); - - while (run_closures) { - grpc_iomgr_closure *next = run_closures->next; - run_closures->cb(run_closures->cb_arg, run_closures->success); - run_closures = next; - } + GPR_TIMER_END("unlock", 0); } /* @@ -502,53 +543,60 @@ static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, } } -void grpc_chttp2_terminate_writing( - grpc_chttp2_transport_writing *transport_writing, int success) { +void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx, + void *transport_writing_ptr, int success) { + grpc_chttp2_transport_writing *transport_writing = transport_writing_ptr; grpc_chttp2_transport *t = TRANSPORT_FROM_WRITING(transport_writing); + GPR_TIMER_BEGIN("grpc_chttp2_terminate_writing", 0); + lock(t); + allow_endpoint_shutdown_locked(exec_ctx, t); + if (!success) { - drop_connection(t); + drop_connection(exec_ctx, t); } /* cleanup writing related jazz */ - grpc_chttp2_cleanup_writing(&t->global, &t->writing); + grpc_chttp2_cleanup_writing(exec_ctx, &t->global, &t->writing); /* leave the writing flag up on shutdown to prevent further writes in unlock() from starting */ t->writing_active = 0; if (t->ep && !t->endpoint_reading) { - grpc_endpoint_destroy(t->ep); - t->ep = NULL; - UNREF_TRANSPORT( - t, "disconnect"); /* safe because we'll still have the ref for write */ + destroy_endpoint(exec_ctx, t); } - unlock(t); + unlock(exec_ctx, t); + + UNREF_TRANSPORT(exec_ctx, t, "writing"); - UNREF_TRANSPORT(t, "writing"); + GPR_TIMER_END("grpc_chttp2_terminate_writing", 0); } -static void writing_action(void *gt, int iomgr_success_ignored) { +static void writing_action(grpc_exec_ctx *exec_ctx, void *gt, + int iomgr_success_ignored) { grpc_chttp2_transport *t = gt; - grpc_chttp2_perform_writes(&t->writing, t->ep); + GPR_TIMER_BEGIN("writing_action", 0); + grpc_chttp2_perform_writes(exec_ctx, &t->writing, t->ep); + GPR_TIMER_END("writing_action", 0); } void grpc_chttp2_add_incoming_goaway( - grpc_chttp2_transport_global *transport_global, gpr_uint32 goaway_error, - gpr_slice goaway_text) { + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, + gpr_uint32 goaway_error, gpr_slice goaway_text) { char *msg = gpr_dump_slice(goaway_text, GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "got goaway [%d]: %s", goaway_error, msg); gpr_free(msg); gpr_slice_unref(goaway_text); transport_global->seen_goaway = 1; - connectivity_state_set(transport_global, GRPC_CHANNEL_FATAL_FAILURE, + connectivity_state_set(exec_ctx, transport_global, GRPC_CHANNEL_FATAL_FAILURE, "got_goaway"); } static void maybe_start_some_streams( - grpc_chttp2_transport_global *transport_global) { + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global) { grpc_chttp2_stream_global *stream_global; /* start streams where we have free grpc_chttp2_stream ids and free * concurrency */ @@ -569,7 +617,8 @@ static void maybe_start_some_streams( transport_global->next_stream_id += 2; if (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID) { - connectivity_state_set(transport_global, GRPC_CHANNEL_TRANSIENT_FAILURE, + connectivity_state_set(exec_ctx, transport_global, + GRPC_CHANNEL_TRANSIENT_FAILURE, "no_more_stream_ids"); } @@ -599,8 +648,9 @@ static void maybe_start_some_streams( } static void perform_stream_op_locked( - grpc_chttp2_transport_global *transport_global, + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op) { + GPR_TIMER_BEGIN("perform_stream_op_locked", 0); if (op->cancel_with_status != GRPC_STATUS_OK) { cancel_from_api(transport_global, stream_global, op->cancel_with_status); } @@ -627,14 +677,13 @@ static void perform_stream_op_locked( transport_global->is_client ? "CLI" : "SVR", stream_global)); grpc_chttp2_list_add_waiting_for_concurrency(transport_global, stream_global); - maybe_start_some_streams(transport_global); + maybe_start_some_streams(exec_ctx, transport_global); } else if (stream_global->outgoing_window > 0) { grpc_chttp2_list_add_writable_stream(transport_global, stream_global); } } else { grpc_sopb_reset(op->send_ops); - grpc_chttp2_schedule_closure(transport_global, - stream_global->send_done_closure, 0); + grpc_exec_ctx_enqueue(exec_ctx, stream_global->send_done_closure, 0); } } @@ -645,6 +694,8 @@ static void perform_stream_op_locked( stream_global->publish_sopb = op->recv_ops; stream_global->publish_sopb->nops = 0; stream_global->publish_state = op->recv_state; + /* clamp max recv bytes */ + op->max_recv_bytes = GPR_MIN(op->max_recv_bytes, GPR_UINT32_MAX); if (stream_global->max_recv_bytes < op->max_recv_bytes) { GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( "op", transport_global, stream_global, max_recv_bytes, @@ -653,69 +704,66 @@ static void perform_stream_op_locked( "op", transport_global, stream_global, unannounced_incoming_window, op->max_recv_bytes - stream_global->max_recv_bytes); stream_global->unannounced_incoming_window += - op->max_recv_bytes - stream_global->max_recv_bytes; - stream_global->max_recv_bytes = op->max_recv_bytes; + (gpr_uint32)op->max_recv_bytes - stream_global->max_recv_bytes; + stream_global->max_recv_bytes = (gpr_uint32)op->max_recv_bytes; } grpc_chttp2_incoming_metadata_live_op_buffer_end( &stream_global->outstanding_metadata); + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); if (stream_global->id != 0) { - grpc_chttp2_list_add_read_write_state_changed(transport_global, - stream_global); grpc_chttp2_list_add_writable_stream(transport_global, stream_global); } } if (op->bind_pollset) { - add_to_pollset_locked(TRANSPORT_FROM_GLOBAL(transport_global), + add_to_pollset_locked(exec_ctx, TRANSPORT_FROM_GLOBAL(transport_global), op->bind_pollset); } - if (op->on_consumed) { - grpc_chttp2_schedule_closure(transport_global, op->on_consumed, 1); - } + grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 1); + GPR_TIMER_END("perform_stream_op_locked", 0); } -static void perform_stream_op(grpc_transport *gt, grpc_stream *gs, - grpc_transport_stream_op *op) { +static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_transport_stream_op *op) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; lock(t); - perform_stream_op_locked(&t->global, &s->global, op); - unlock(t); + perform_stream_op_locked(exec_ctx, &t->global, &s->global, op); + unlock(exec_ctx, t); } -static void send_ping_locked(grpc_chttp2_transport *t, - grpc_iomgr_closure *on_recv) { +static void send_ping_locked(grpc_chttp2_transport *t, grpc_closure *on_recv) { grpc_chttp2_outstanding_ping *p = gpr_malloc(sizeof(*p)); p->next = &t->global.pings; p->prev = p->next->prev; p->prev->next = p->next->prev = p; - p->id[0] = (t->global.ping_counter >> 56) & 0xff; - p->id[1] = (t->global.ping_counter >> 48) & 0xff; - p->id[2] = (t->global.ping_counter >> 40) & 0xff; - p->id[3] = (t->global.ping_counter >> 32) & 0xff; - p->id[4] = (t->global.ping_counter >> 24) & 0xff; - p->id[5] = (t->global.ping_counter >> 16) & 0xff; - p->id[6] = (t->global.ping_counter >> 8) & 0xff; - p->id[7] = t->global.ping_counter & 0xff; + p->id[0] = (gpr_uint8)((t->global.ping_counter >> 56) & 0xff); + p->id[1] = (gpr_uint8)((t->global.ping_counter >> 48) & 0xff); + p->id[2] = (gpr_uint8)((t->global.ping_counter >> 40) & 0xff); + p->id[3] = (gpr_uint8)((t->global.ping_counter >> 32) & 0xff); + p->id[4] = (gpr_uint8)((t->global.ping_counter >> 24) & 0xff); + p->id[5] = (gpr_uint8)((t->global.ping_counter >> 16) & 0xff); + p->id[6] = (gpr_uint8)((t->global.ping_counter >> 8) & 0xff); + p->id[7] = (gpr_uint8)(t->global.ping_counter & 0xff); p->on_recv = on_recv; gpr_slice_buffer_add(&t->global.qbuf, grpc_chttp2_ping_create(0, p->id)); } -static void perform_transport_op(grpc_transport *gt, grpc_transport_op *op) { +static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_transport_op *op) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; int close_transport = 0; lock(t); - if (op->on_consumed) { - grpc_chttp2_schedule_closure(&t->global, op->on_consumed, 1); - } + grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 1); if (op->on_connectivity_state_change) { grpc_connectivity_state_notify_on_state_change( - &t->channel_callback.state_tracker, op->connectivity_state, + exec_ctx, &t->channel_callback.state_tracker, op->connectivity_state, op->on_connectivity_state_change); } @@ -723,7 +771,7 @@ static void perform_transport_op(grpc_transport *gt, grpc_transport_op *op) { t->global.sent_goaway = 1; grpc_chttp2_goaway_append( t->global.last_incoming_stream_id, - grpc_chttp2_grpc_status_to_http2_error(op->goaway_status), + (gpr_uint32)grpc_chttp2_grpc_status_to_http2_error(op->goaway_status), gpr_slice_ref(*op->goaway_message), &t->global.qbuf); close_transport = !grpc_chttp2_has_streams(t); } @@ -735,11 +783,11 @@ static void perform_transport_op(grpc_transport *gt, grpc_transport_op *op) { } if (op->bind_pollset) { - add_to_pollset_locked(t, op->bind_pollset); + add_to_pollset_locked(exec_ctx, t, op->bind_pollset); } if (op->bind_pollset_set) { - add_to_pollset_set_locked(t, op->bind_pollset_set); + add_to_pollset_set_locked(exec_ctx, t, op->bind_pollset_set); } if (op->send_ping) { @@ -747,15 +795,15 @@ static void perform_transport_op(grpc_transport *gt, grpc_transport_op *op) { } if (op->disconnect) { - close_transport_locked(t); + close_transport_locked(exec_ctx, t); } - unlock(t); + unlock(exec_ctx, t); if (close_transport) { lock(t); - close_transport_locked(t); - unlock(t); + close_transport_locked(exec_ctx, t); + unlock(exec_ctx, t); } } @@ -771,7 +819,8 @@ static grpc_stream_state compute_state(gpr_uint8 write_closed, return GRPC_STREAM_OPEN; } -static void remove_stream(grpc_chttp2_transport *t, gpr_uint32 id) { +static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + gpr_uint32 id) { size_t new_stream_count; grpc_chttp2_stream *s = grpc_chttp2_stream_map_delete(&t->parsing_stream_map, id); @@ -786,18 +835,20 @@ static void remove_stream(grpc_chttp2_transport *t, gpr_uint32 id) { grpc_chttp2_parsing_become_skip_parser(&t->parsing); } if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) { - close_transport_locked(t); + close_transport_locked(exec_ctx, t); } new_stream_count = grpc_chttp2_stream_map_size(&t->parsing_stream_map) + grpc_chttp2_stream_map_size(&t->new_stream_map); + GPR_ASSERT(new_stream_count <= GPR_UINT32_MAX); if (new_stream_count != t->global.concurrent_stream_count) { - t->global.concurrent_stream_count = new_stream_count; - maybe_start_some_streams(&t->global); + t->global.concurrent_stream_count = (gpr_uint32)new_stream_count; + maybe_start_some_streams(exec_ctx, &t->global); } } -static void unlock_check_read_write_state(grpc_chttp2_transport *t) { +static void unlock_check_read_write_state(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { grpc_chttp2_transport_global *transport_global = &t->global; grpc_chttp2_stream_global *stream_global; grpc_stream_state state; @@ -811,7 +862,7 @@ static void unlock_check_read_write_state(grpc_chttp2_transport *t) { GPR_ASSERT(stream_global->in_stream_map); GPR_ASSERT(stream_global->write_state != GRPC_WRITE_STATE_OPEN); GPR_ASSERT(stream_global->read_closed); - remove_stream(t, stream_global->id); + remove_stream(exec_ctx, t, stream_global->id); grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); } @@ -837,8 +888,7 @@ static void unlock_check_read_write_state(grpc_chttp2_transport *t) { if (stream_global->outgoing_sopb != NULL) { grpc_sopb_reset(stream_global->outgoing_sopb); stream_global->outgoing_sopb = NULL; - grpc_chttp2_schedule_closure(transport_global, - stream_global->send_done_closure, 1); + grpc_exec_ctx_enqueue(exec_ctx, stream_global->send_done_closure, 1); } stream_global->read_closed = 1; if (!stream_global->published_cancelled) { @@ -860,7 +910,7 @@ static void unlock_check_read_write_state(grpc_chttp2_transport *t) { grpc_chttp2_list_add_closed_waiting_for_parsing(transport_global, stream_global); } else { - remove_stream(t, stream_global->id); + remove_stream(exec_ctx, t, stream_global->id); } } if (!stream_global->publish_sopb) { @@ -888,8 +938,7 @@ static void unlock_check_read_write_state(grpc_chttp2_transport *t) { &stream_global->outstanding_metadata); grpc_sopb_swap(stream_global->publish_sopb, &stream_global->incoming_sopb); stream_global->published_state = *stream_global->publish_state = state; - grpc_chttp2_schedule_closure(transport_global, - stream_global->recv_done_closure, 1); + grpc_exec_ctx_enqueue(exec_ctx, stream_global->recv_done_closure, 1); stream_global->recv_done_closure = NULL; stream_global->publish_sopb = NULL; stream_global->publish_state = NULL; @@ -905,7 +954,8 @@ static void cancel_from_api(grpc_chttp2_transport_global *transport_global, gpr_slice_buffer_add( &transport_global->qbuf, grpc_chttp2_rst_stream_create( - stream_global->id, grpc_chttp2_grpc_status_to_http2_error(status))); + stream_global->id, + (gpr_uint32)grpc_chttp2_grpc_status_to_http2_error(status))); } grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); @@ -951,14 +1001,14 @@ static void close_from_api(grpc_chttp2_transport_global *transport_global, *p++ = 's'; if (status < 10) { *p++ = 1; - *p++ = '0' + status; + *p++ = (gpr_uint8)('0' + status); } else { *p++ = 2; - *p++ = '0' + (status / 10); - *p++ = '0' + (status % 10); + *p++ = (gpr_uint8)('0' + (status / 10)); + *p++ = (gpr_uint8)('0' + (status % 10)); } GPR_ASSERT(p == GPR_SLICE_END_PTR(status_hdr)); - len += GPR_SLICE_LENGTH(status_hdr); + len += (gpr_uint32)GPR_SLICE_LENGTH(status_hdr); if (optional_message) { GPR_ASSERT(GPR_SLICE_LENGTH(*optional_message) < 127); @@ -978,23 +1028,23 @@ static void close_from_api(grpc_chttp2_transport_global *transport_global, *p++ = 'a'; *p++ = 'g'; *p++ = 'e'; - *p++ = GPR_SLICE_LENGTH(*optional_message); + *p++ = (gpr_uint8)GPR_SLICE_LENGTH(*optional_message); GPR_ASSERT(p == GPR_SLICE_END_PTR(message_pfx)); - len += GPR_SLICE_LENGTH(message_pfx); - len += GPR_SLICE_LENGTH(*optional_message); + len += (gpr_uint32)GPR_SLICE_LENGTH(message_pfx); + len += (gpr_uint32)GPR_SLICE_LENGTH(*optional_message); } hdr = gpr_slice_malloc(9); p = GPR_SLICE_START_PTR(hdr); - *p++ = len >> 16; - *p++ = len >> 8; - *p++ = len; + *p++ = (gpr_uint8)(len >> 16); + *p++ = (gpr_uint8)(len >> 8); + *p++ = (gpr_uint8)(len); *p++ = GRPC_CHTTP2_FRAME_HEADER; *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; - *p++ = stream_global->id >> 24; - *p++ = stream_global->id >> 16; - *p++ = stream_global->id >> 8; - *p++ = stream_global->id; + *p++ = (gpr_uint8)(stream_global->id >> 24); + *p++ = (gpr_uint8)(stream_global->id >> 16); + *p++ = (gpr_uint8)(stream_global->id >> 8); + *p++ = (gpr_uint8)(stream_global->id); GPR_ASSERT(p == GPR_SLICE_END_PTR(hdr)); gpr_slice_buffer_add(&transport_global->qbuf, hdr); @@ -1023,8 +1073,8 @@ static void end_all_the_calls(grpc_chttp2_transport *t) { grpc_chttp2_for_all_streams(&t->global, NULL, cancel_stream_cb); } -static void drop_connection(grpc_chttp2_transport *t) { - close_transport_locked(t); +static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { + close_transport_locked(exec_ctx, t); end_all_the_calls(t); } @@ -1049,135 +1099,110 @@ static void update_global_window(void *args, gpr_uint32 id, void *stream) { } } -static void read_error_locked(grpc_chttp2_transport *t) { +static void read_error_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { t->endpoint_reading = 0; if (!t->writing_active && t->ep) { - grpc_endpoint_destroy(t->ep); - t->ep = NULL; - /* safe as we still have a ref for read */ - UNREF_TRANSPORT(t, "disconnect"); + destroy_endpoint(exec_ctx, t); } } /* tcp read callback */ -static void recv_data(void *tp, gpr_slice *slices, size_t nslices, - grpc_endpoint_cb_status error) { - grpc_chttp2_transport *t = tp; +static void recv_data(grpc_exec_ctx *exec_ctx, void *tp, int success) { size_t i; - int unref = 0; - - switch (error) { - case GRPC_ENDPOINT_CB_SHUTDOWN: - case GRPC_ENDPOINT_CB_EOF: - case GRPC_ENDPOINT_CB_ERROR: - lock(t); - drop_connection(t); - read_error_locked(t); - unlock(t); - unref = 1; - for (i = 0; i < nslices; i++) gpr_slice_unref(slices[i]); - break; - case GRPC_ENDPOINT_CB_OK: - lock(t); - i = 0; - GPR_ASSERT(!t->parsing_active); - if (!t->closed) { - t->parsing_active = 1; - /* merge stream lists */ - grpc_chttp2_stream_map_move_into(&t->new_stream_map, - &t->parsing_stream_map); - grpc_chttp2_prepare_to_read(&t->global, &t->parsing); - gpr_mu_unlock(&t->mu); - for (; i < nslices && grpc_chttp2_perform_read(&t->parsing, slices[i]); - i++) { - gpr_slice_unref(slices[i]); - } - gpr_mu_lock(&t->mu); - if (i != nslices) { - drop_connection(t); - } - /* merge stream lists */ - grpc_chttp2_stream_map_move_into(&t->new_stream_map, - &t->parsing_stream_map); - t->global.concurrent_stream_count = - grpc_chttp2_stream_map_size(&t->parsing_stream_map); - if (t->parsing.initial_window_update != 0) { - grpc_chttp2_stream_map_for_each(&t->parsing_stream_map, - update_global_window, t); - t->parsing.initial_window_update = 0; - } - /* handle higher level things */ - grpc_chttp2_publish_reads(&t->global, &t->parsing); - t->parsing_active = 0; - } - if (i == nslices) { - grpc_chttp2_schedule_closure(&t->global, &t->reading_action, 1); - } else { - read_error_locked(t); - unref = 1; - } - unlock(t); - for (; i < nslices; i++) gpr_slice_unref(slices[i]); - break; + int keep_reading = 0; + grpc_chttp2_transport *t = tp; + + GPR_TIMER_BEGIN("recv_data", 0); + + lock(t); + i = 0; + GPR_ASSERT(!t->parsing_active); + if (!t->closed) { + t->parsing_active = 1; + /* merge stream lists */ + grpc_chttp2_stream_map_move_into(&t->new_stream_map, + &t->parsing_stream_map); + grpc_chttp2_prepare_to_read(&t->global, &t->parsing); + gpr_mu_unlock(&t->mu); + GPR_TIMER_BEGIN("recv_data.parse", 0); + for (; i < t->read_buffer.count && + grpc_chttp2_perform_read(exec_ctx, &t->parsing, + t->read_buffer.slices[i]); + i++) + ; + GPR_TIMER_END("recv_data.parse", 0); + gpr_mu_lock(&t->mu); + if (i != t->read_buffer.count) { + drop_connection(exec_ctx, t); + } + /* merge stream lists */ + grpc_chttp2_stream_map_move_into(&t->new_stream_map, + &t->parsing_stream_map); + t->global.concurrent_stream_count = + (gpr_uint32)grpc_chttp2_stream_map_size(&t->parsing_stream_map); + if (t->parsing.initial_window_update != 0) { + grpc_chttp2_stream_map_for_each(&t->parsing_stream_map, + update_global_window, t); + t->parsing.initial_window_update = 0; + } + /* handle higher level things */ + grpc_chttp2_publish_reads(exec_ctx, &t->global, &t->parsing); + t->parsing_active = 0; } - if (unref) { - UNREF_TRANSPORT(t, "recv_data"); + if (!success || i != t->read_buffer.count || t->closed) { + drop_connection(exec_ctx, t); + read_error_locked(exec_ctx, t); + } else if (!t->closed) { + keep_reading = 1; + REF_TRANSPORT(t, "keep_reading"); + prevent_endpoint_shutdown(t); + } + gpr_slice_buffer_reset_and_unref(&t->read_buffer); + unlock(exec_ctx, t); + + if (keep_reading) { + grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer, &t->recv_data); + allow_endpoint_shutdown_unlocked(exec_ctx, t); + UNREF_TRANSPORT(exec_ctx, t, "keep_reading"); + } else { + UNREF_TRANSPORT(exec_ctx, t, "recv_data"); } -} -static void reading_action(void *pt, int iomgr_success_ignored) { - grpc_chttp2_transport *t = pt; - grpc_endpoint_notify_on_read(t->ep, recv_data, t); + GPR_TIMER_END("recv_data", 0); } /* * CALLBACK LOOP */ -static void schedule_closure_for_connectivity(void *a, - grpc_iomgr_closure *closure) { - grpc_chttp2_schedule_closure(a, closure, 1); -} - static void connectivity_state_set( - grpc_chttp2_transport_global *transport_global, + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_connectivity_state state, const char *reason) { GRPC_CHTTP2_IF_TRACING( gpr_log(GPR_DEBUG, "set connectivity_state=%d", state)); - grpc_connectivity_state_set_with_scheduler( - &TRANSPORT_FROM_GLOBAL(transport_global)->channel_callback.state_tracker, - state, schedule_closure_for_connectivity, transport_global, reason); -} - -void grpc_chttp2_schedule_closure( - grpc_chttp2_transport_global *transport_global, grpc_iomgr_closure *closure, - int success) { - closure->success = success; - if (transport_global->pending_closures_tail == NULL) { - transport_global->pending_closures_head = - transport_global->pending_closures_tail = closure; - } else { - transport_global->pending_closures_tail->next = closure; - transport_global->pending_closures_tail = closure; - } - closure->next = NULL; + grpc_connectivity_state_set(exec_ctx, &TRANSPORT_FROM_GLOBAL(transport_global) + ->channel_callback.state_tracker, + state, reason); } /* * POLLSET STUFF */ -static void add_to_pollset_locked(grpc_chttp2_transport *t, +static void add_to_pollset_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, grpc_pollset *pollset) { if (t->ep) { - grpc_endpoint_add_to_pollset(t->ep, pollset); + grpc_endpoint_add_to_pollset(exec_ctx, t->ep, pollset); } } -static void add_to_pollset_set_locked(grpc_chttp2_transport *t, +static void add_to_pollset_set_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, grpc_pollset_set *pollset_set) { if (t->ep) { - grpc_endpoint_add_to_pollset_set(t->ep, pollset_set); + grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, pollset_set); } } @@ -1216,29 +1241,27 @@ void grpc_chttp2_flowctl_trace(const char *file, int line, const char *reason, * INTEGRATION GLUE */ -static char *chttp2_get_peer(grpc_transport *t) { +static char *chttp2_get_peer(grpc_exec_ctx *exec_ctx, grpc_transport *t) { return gpr_strdup(((grpc_chttp2_transport *)t)->peer_string); } -static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream), - init_stream, - perform_stream_op, - perform_transport_op, - destroy_stream, - destroy_transport, - chttp2_get_peer}; +static const grpc_transport_vtable vtable = { + sizeof(grpc_chttp2_stream), init_stream, perform_stream_op, + perform_transport_op, destroy_stream, destroy_transport, chttp2_get_peer}; grpc_transport *grpc_create_chttp2_transport( - const grpc_channel_args *channel_args, grpc_endpoint *ep, grpc_mdctx *mdctx, - int is_client) { + grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args, + grpc_endpoint *ep, grpc_mdctx *mdctx, int is_client) { grpc_chttp2_transport *t = gpr_malloc(sizeof(grpc_chttp2_transport)); - init_transport(t, channel_args, ep, mdctx, is_client); + init_transport(exec_ctx, t, channel_args, ep, mdctx, is_client != 0); return &t->base; } -void grpc_chttp2_transport_start_reading(grpc_transport *transport, +void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, gpr_slice *slices, size_t nslices) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport; REF_TRANSPORT(t, "recv_data"); /* matches unref inside recv_data */ - recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK); + gpr_slice_buffer_addn(&t->read_buffer, slices, nslices); + recv_data(exec_ctx, t, 1); } diff --git a/src/core/transport/chttp2_transport.h b/src/core/transport/chttp2_transport.h index fa0d6e4151..fce2b680fd 100644 --- a/src/core/transport/chttp2_transport.h +++ b/src/core/transport/chttp2_transport.h @@ -41,10 +41,11 @@ extern int grpc_http_trace; extern int grpc_flowctl_trace; grpc_transport *grpc_create_chttp2_transport( - const grpc_channel_args *channel_args, grpc_endpoint *ep, - grpc_mdctx *metadata_context, int is_client); + grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args, + grpc_endpoint *ep, grpc_mdctx *metadata_context, int is_client); -void grpc_chttp2_transport_start_reading(grpc_transport *transport, +void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, gpr_slice *slices, size_t nslices); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_TRANSPORT_H */ diff --git a/src/core/transport/connectivity_state.c b/src/core/transport/connectivity_state.c index 61d26f06f0..09b298c131 100644 --- a/src/core/transport/connectivity_state.c +++ b/src/core/transport/connectivity_state.c @@ -32,6 +32,9 @@ */ #include "src/core/transport/connectivity_state.h" + +#include <string.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/string_util.h> @@ -63,17 +66,20 @@ void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, tracker->name = gpr_strdup(name); } -void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker *tracker) { +void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx, + grpc_connectivity_state_tracker *tracker) { + int success; grpc_connectivity_state_watcher *w; while ((w = tracker->watchers)) { tracker->watchers = w->next; if (GRPC_CHANNEL_FATAL_FAILURE != *w->current) { *w->current = GRPC_CHANNEL_FATAL_FAILURE; - grpc_iomgr_add_callback(w->notify); + success = 1; } else { - grpc_iomgr_add_delayed_callback(w->notify, 0); + success = 0; } + grpc_exec_ctx_enqueue(exec_ctx, w->notify, success); gpr_free(w); } gpr_free(tracker->name); @@ -81,20 +87,24 @@ void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker *tracker) { grpc_connectivity_state grpc_connectivity_state_check( grpc_connectivity_state_tracker *tracker) { + if (grpc_connectivity_state_trace) { + gpr_log(GPR_DEBUG, "CONWATCH: %s: get %s", tracker->name, + grpc_connectivity_state_name(tracker->current_state)); + } return tracker->current_state; } int grpc_connectivity_state_notify_on_state_change( - grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, - grpc_iomgr_closure *notify) { + grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state *current, grpc_closure *notify) { if (grpc_connectivity_state_trace) { - gpr_log(GPR_DEBUG, "CONWATCH: %s: from %s [cur=%s]", tracker->name, - grpc_connectivity_state_name(*current), - grpc_connectivity_state_name(tracker->current_state)); + gpr_log(GPR_DEBUG, "CONWATCH: %s: from %s [cur=%s] notify=%p", + tracker->name, grpc_connectivity_state_name(*current), + grpc_connectivity_state_name(tracker->current_state), notify); } if (tracker->current_state != *current) { *current = tracker->current_state; - grpc_iomgr_add_callback(notify); + grpc_exec_ctx_enqueue(exec_ctx, notify, 1); } else { grpc_connectivity_state_watcher *w = gpr_malloc(sizeof(*w)); w->current = current; @@ -105,11 +115,31 @@ int grpc_connectivity_state_notify_on_state_change( return tracker->current_state == GRPC_CHANNEL_IDLE; } -void grpc_connectivity_state_set_with_scheduler( - grpc_connectivity_state_tracker *tracker, grpc_connectivity_state state, - void (*scheduler)(void *arg, grpc_iomgr_closure *closure), void *arg, - const char *reason) { - grpc_connectivity_state_watcher *new = NULL; +int grpc_connectivity_state_change_unsubscribe( + grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, + grpc_closure *subscribed_notify) { + grpc_connectivity_state_watcher *w = tracker->watchers; + if (w != NULL && w->notify == subscribed_notify) { + tracker->watchers = w->next; + gpr_free(w); + return 1; + } + while (w != NULL) { + grpc_connectivity_state_watcher *rm_candidate = w->next; + if (rm_candidate != NULL && rm_candidate->notify == subscribed_notify) { + w->next = w->next->next; + gpr_free(rm_candidate); + return 1; + } + w = w->next; + } + return 0; +} + +void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx, + grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state state, + const char *reason) { grpc_connectivity_state_watcher *w; if (grpc_connectivity_state_trace) { gpr_log(GPR_DEBUG, "SET: %s: %s --> %s [%s]", tracker->name, @@ -121,28 +151,10 @@ void grpc_connectivity_state_set_with_scheduler( } GPR_ASSERT(tracker->current_state != GRPC_CHANNEL_FATAL_FAILURE); tracker->current_state = state; - while ((w = tracker->watchers)) { + while ((w = tracker->watchers) != NULL) { + *w->current = tracker->current_state; tracker->watchers = w->next; - - if (state != *w->current) { - *w->current = state; - scheduler(arg, w->notify); - gpr_free(w); - } else { - w->next = new; - new = w; - } + grpc_exec_ctx_enqueue(exec_ctx, w->notify, 1); + gpr_free(w); } - tracker->watchers = new; -} - -static void default_scheduler(void *ignored, grpc_iomgr_closure *closure) { - grpc_iomgr_add_callback(closure); -} - -void grpc_connectivity_state_set(grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state state, - const char *reason) { - grpc_connectivity_state_set_with_scheduler(tracker, state, default_scheduler, - NULL, reason); } diff --git a/src/core/transport/connectivity_state.h b/src/core/transport/connectivity_state.h index a3b0b80c98..119b1c1554 100644 --- a/src/core/transport/connectivity_state.h +++ b/src/core/transport/connectivity_state.h @@ -35,13 +35,13 @@ #define GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H #include <grpc/grpc.h> -#include "src/core/iomgr/iomgr.h" +#include "src/core/iomgr/exec_ctx.h" typedef struct grpc_connectivity_state_watcher { /** we keep watchers in a linked list */ struct grpc_connectivity_state_watcher *next; /** closure to notify on change */ - grpc_iomgr_closure *notify; + grpc_closure *notify; /** the current state as believed by the watcher */ grpc_connectivity_state *current; } grpc_connectivity_state_watcher; @@ -60,22 +60,29 @@ extern int grpc_connectivity_state_trace; void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, grpc_connectivity_state init_state, const char *name); -void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker *tracker); +void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx, + grpc_connectivity_state_tracker *tracker); -void grpc_connectivity_state_set(grpc_connectivity_state_tracker *tracker, +/** Set connectivity state; not thread safe; access must be serialized with an + * external lock */ +void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx, + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state state, const char *reason); -void grpc_connectivity_state_set_with_scheduler( - grpc_connectivity_state_tracker *tracker, grpc_connectivity_state state, - void (*scheduler)(void *arg, grpc_iomgr_closure *closure), void *arg, - const char *reason); grpc_connectivity_state grpc_connectivity_state_check( grpc_connectivity_state_tracker *tracker); /** Return 1 if the channel should start connecting, 0 otherwise */ int grpc_connectivity_state_notify_on_state_change( - grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, - grpc_iomgr_closure *notify); + grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state *current, grpc_closure *notify); + +/** Remove \a subscribed_notify from the list of closures to be called on a + * state change if present, returning 1. Otherwise, nothing is done and return + * 0. */ +int grpc_connectivity_state_change_unsubscribe( + grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, + grpc_closure *subscribed_notify); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H */ diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c index 61638764a6..68f23177eb 100644 --- a/src/core/transport/metadata.c +++ b/src/core/transport/metadata.c @@ -62,6 +62,8 @@ #define REF_MD_LOCKED(s) ref_md_locked((s)) #endif +typedef void (*destroy_user_data_func)(void *user_data); + typedef struct internal_string { /* must be byte compatible with grpc_mdstr */ gpr_slice slice; @@ -88,8 +90,8 @@ typedef struct internal_metadata { /* private only data */ gpr_mu mu_user_data; - void *user_data; - void (*destroy_user_data)(void *user_data); + gpr_atm destroy_user_data; + gpr_atm user_data; grpc_mdctx *context; struct internal_metadata *bucket_next; @@ -158,8 +160,10 @@ static void ref_md_locked(internal_metadata *md DEBUG_ARGS) { grpc_mdstr_as_c_string((grpc_mdstr *)md->key), grpc_mdstr_as_c_string((grpc_mdstr *)md->value)); #endif - if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) { + if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) { md->context->mdtab_free--; + } else { + GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1)); } } @@ -186,7 +190,8 @@ grpc_mdctx *grpc_mdctx_create(void) { /* This seed is used to prevent remote connections from controlling hash table * collisions. It needs to be somewhat unpredictable to a remote connection. */ - return grpc_mdctx_create_with_seed(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); + return grpc_mdctx_create_with_seed( + (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec); } static void discard_metadata(grpc_mdctx *ctx) { @@ -196,12 +201,14 @@ static void discard_metadata(grpc_mdctx *ctx) { for (i = 0; i < ctx->mdtab_capacity; i++) { cur = ctx->mdtab[i]; while (cur) { + void *user_data = (void *)gpr_atm_no_barrier_load(&cur->user_data); GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0); next = cur->bucket_next; INTERNAL_STRING_UNREF(cur->key); INTERNAL_STRING_UNREF(cur->value); - if (cur->user_data) { - cur->destroy_user_data(cur->user_data); + if (user_data != NULL) { + ((destroy_user_data_func)gpr_atm_no_barrier_load( + &cur->destroy_user_data))(user_data); } gpr_mu_destroy(&cur->mu_user_data); gpr_free(cur); @@ -311,38 +318,7 @@ static void slice_unref(void *p) { unlock(ctx); } -grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str, - int canonicalize_key) { - if (canonicalize_key) { - size_t len; - size_t i; - int canonical = 1; - - for (i = 0; str[i]; i++) { - if (str[i] >= 'A' && str[i] <= 'Z') { - canonical = 0; - /* Keep going in loop just to get string length */ - } - } - len = i; - - if (canonical) { - return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, len); - } else { - char *copy = gpr_malloc(len); - grpc_mdstr *ret; - for (i = 0; i < len; i++) { - if (str[i] >= 'A' && str[i] <= 'Z') { - copy[i] = str[i] - 'A' + 'a'; - } else { - copy[i] = str[i]; - } - } - ret = grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)copy, len); - gpr_free(copy); - return ret; - } - } +grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str) { return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, strlen(str)); } @@ -378,7 +354,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf, s->slice.refcount = NULL; memcpy(s->slice.data.inlined.bytes, buf, length); s->slice.data.inlined.bytes[length] = 0; - s->slice.data.inlined.length = length; + s->slice.data.inlined.length = (gpr_uint8)length; } else { /* string data goes after the internal_string header, and we +1 for null terminator */ @@ -418,12 +394,14 @@ static void gc_mdtab(grpc_mdctx *ctx) { for (i = 0; i < ctx->mdtab_capacity; i++) { prev_next = &ctx->mdtab[i]; for (md = ctx->mdtab[i]; md; md = next) { + void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data); next = md->bucket_next; if (gpr_atm_acq_load(&md->refcnt) == 0) { INTERNAL_STRING_UNREF(md->key); INTERNAL_STRING_UNREF(md->value); if (md->user_data) { - md->destroy_user_data(md->user_data); + ((destroy_user_data_func)gpr_atm_no_barrier_load( + &md->destroy_user_data))(user_data); } gpr_free(md); *prev_next = next; @@ -495,12 +473,12 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx, /* not found: create a new pair */ md = gpr_malloc(sizeof(internal_metadata)); - gpr_atm_rel_store(&md->refcnt, 1); + gpr_atm_rel_store(&md->refcnt, 2); md->context = ctx; md->key = key; md->value = value; - md->user_data = NULL; - md->destroy_user_data = NULL; + md->user_data = 0; + md->destroy_user_data = 0; md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity]; gpr_mu_init(&md->mu_user_data); #ifdef GRPC_METADATA_REFCOUNT_DEBUG @@ -523,9 +501,9 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx, grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key, const char *value) { - return grpc_mdelem_from_metadata_strings( - ctx, grpc_mdstr_from_string(ctx, key, 0), - grpc_mdstr_from_string(ctx, value, 0)); + return grpc_mdelem_from_metadata_strings(ctx, + grpc_mdstr_from_string(ctx, key), + grpc_mdstr_from_string(ctx, value)); } grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key, @@ -537,10 +515,9 @@ grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key, grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx, const char *key, const gpr_uint8 *value, - size_t value_length, - int canonicalize_key) { + size_t value_length) { return grpc_mdelem_from_metadata_strings( - ctx, grpc_mdstr_from_string(ctx, key, canonicalize_key), + ctx, grpc_mdstr_from_string(ctx, key), grpc_mdstr_from_buffer(ctx, value, value_length)); } @@ -558,15 +535,13 @@ grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) { this function - meaning that no adjustment to mdtab_free is necessary, simplifying the logic here to be just an atomic increment */ /* use C assert to have this removed in opt builds */ - assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1); + assert(gpr_atm_no_barrier_load(&md->refcnt) >= 2); gpr_atm_no_barrier_fetch_add(&md->refcnt, 1); return gmd; } void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) { internal_metadata *md = (internal_metadata *)gmd; - grpc_mdctx *ctx = md->context; - lock(ctx); #ifdef GRPC_METADATA_REFCOUNT_DEBUG gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "ELM UNREF:%p:%d->%d: '%s' = '%s'", md, @@ -575,11 +550,15 @@ void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) { grpc_mdstr_as_c_string((grpc_mdstr *)md->key), grpc_mdstr_as_c_string((grpc_mdstr *)md->value)); #endif - assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1); - if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) { - ctx->mdtab_free++; + if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) { + grpc_mdctx *ctx = md->context; + lock(ctx); + if (1 == gpr_atm_no_barrier_load(&md->refcnt)) { + ctx->mdtab_free++; + gpr_atm_no_barrier_store(&md->refcnt, 0); + } + unlock(ctx); } - unlock(ctx); } const char *grpc_mdstr_as_c_string(grpc_mdstr *s) { @@ -615,13 +594,14 @@ size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) { return ctx->mdtab_free; } -void *grpc_mdelem_get_user_data(grpc_mdelem *md, - void (*if_destroy_func)(void *)) { +void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) { internal_metadata *im = (internal_metadata *)md; void *result; - gpr_mu_lock(&im->mu_user_data); - result = im->destroy_user_data == if_destroy_func ? im->user_data : NULL; - gpr_mu_unlock(&im->mu_user_data); + if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) { + return (void *)gpr_atm_no_barrier_load(&im->user_data); + } else { + return NULL; + } return result; } @@ -630,7 +610,7 @@ void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *), internal_metadata *im = (internal_metadata *)md; GPR_ASSERT((user_data == NULL) == (destroy_func == NULL)); gpr_mu_lock(&im->mu_user_data); - if (im->destroy_user_data) { + if (gpr_atm_no_barrier_load(&im->destroy_user_data)) { /* user data can only be set once */ gpr_mu_unlock(&im->mu_user_data); if (destroy_func != NULL) { @@ -638,8 +618,8 @@ void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *), } return; } - im->destroy_user_data = destroy_func; - im->user_data = user_data; + gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data); + gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func); gpr_mu_unlock(&im->mu_user_data); } @@ -658,29 +638,6 @@ gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) { return slice; } -void grpc_mdctx_lock(grpc_mdctx *ctx) { lock(ctx); } - -void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, - grpc_mdelem *gmd DEBUG_ARGS) { - internal_metadata *md = (internal_metadata *)gmd; - grpc_mdctx *elem_ctx = md->context; - GPR_ASSERT(ctx == elem_ctx); -#ifdef GRPC_METADATA_REFCOUNT_DEBUG - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "ELM UNREF:%p:%d->%d: '%s' = '%s'", md, - gpr_atm_no_barrier_load(&md->refcnt), - gpr_atm_no_barrier_load(&md->refcnt) - 1, - grpc_mdstr_as_c_string((grpc_mdstr *)md->key), - grpc_mdstr_as_c_string((grpc_mdstr *)md->value)); -#endif - assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1); - if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) { - ctx->mdtab_free++; - } -} - -void grpc_mdctx_unlock(grpc_mdctx *ctx) { unlock(ctx); } - static int conforms_to(grpc_mdstr *s, const gpr_uint8 *legal_bits) { const gpr_uint8 *p = GPR_SLICE_START_PTR(s->slice); const gpr_uint8 *e = GPR_SLICE_END_PTR(s->slice); diff --git a/src/core/transport/metadata.h b/src/core/transport/metadata.h index eb17747be7..9a8164037c 100644 --- a/src/core/transport/metadata.h +++ b/src/core/transport/metadata.h @@ -95,8 +95,7 @@ size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *mdctx); /* Constructors for grpc_mdstr instances; take a variety of data types that clients may have handy */ -grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str, - int perform_key_canonicalization); +grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str); /* Unrefs the slice. */ grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice); grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *str, @@ -118,8 +117,7 @@ grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key, grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx, const char *key, const gpr_uint8 *value, - size_t value_length, - int canonicalize_key); + size_t value_length); /* Mutator and accessor for grpc_mdelem user data. The destructor function is used as a type tag and is checked during user_data fetch. */ @@ -157,28 +155,6 @@ int grpc_mdstr_is_legal_header(grpc_mdstr *s); int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s); int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s); -/* Batch mode metadata functions. - These API's have equivalents above, but allow taking the mdctx just once, - performing a bunch of work, and then leaving the mdctx. */ - -/* Lock the metadata context: it's only safe to call _locked_ functions against - this context from the calling thread until grpc_mdctx_unlock is called */ -void grpc_mdctx_lock(grpc_mdctx *ctx); -#ifdef GRPC_METADATA_REFCOUNT_DEBUG -#define GRPC_MDCTX_LOCKED_MDELEM_UNREF(ctx, elem) \ - grpc_mdctx_locked_mdelem_unref((ctx), (elem), __FILE__, __LINE__) -/* Unref a metadata element */ -void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem, - const char *file, int line); -#else -#define GRPC_MDCTX_LOCKED_MDELEM_UNREF(ctx, elem) \ - grpc_mdctx_locked_mdelem_unref((ctx), (elem)) -/* Unref a metadata element */ -void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem); -#endif -/* Unlock the metadata context */ -void grpc_mdctx_unlock(grpc_mdctx *ctx); - #define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash)) #endif /* GRPC_INTERNAL_CORE_TRANSPORT_METADATA_H */ diff --git a/src/core/transport/stream_op.c b/src/core/transport/stream_op.c index 038586d48e..6493e77bc5 100644 --- a/src/core/transport/stream_op.c +++ b/src/core/transport/stream_op.c @@ -38,6 +38,8 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include "src/core/profiling/timers.h" + /* Exponential growth function: Given x, return a larger x. Currently we grow by 1.5 times upon reallocation. */ #define GROW(x) (3 * (x) / 2) @@ -274,14 +276,14 @@ void grpc_metadata_batch_link_tail(grpc_metadata_batch *batch, } void grpc_metadata_batch_merge(grpc_metadata_batch *target, - grpc_metadata_batch *add) { + grpc_metadata_batch *to_add) { grpc_linked_mdelem *l; grpc_linked_mdelem *next; - for (l = add->list.head; l; l = next) { + for (l = to_add->list.head; l; l = next) { next = l->next; link_tail(&target->list, l); } - for (l = add->garbage.head; l; l = next) { + for (l = to_add->garbage.head; l; l = next) { next = l->next; link_tail(&target->garbage, l); } @@ -300,6 +302,8 @@ void grpc_metadata_batch_filter(grpc_metadata_batch *batch, grpc_linked_mdelem *l; grpc_linked_mdelem *next; + GPR_TIMER_BEGIN("grpc_metadata_batch_filter", 0); + assert_valid_list(&batch->list); assert_valid_list(&batch->garbage); for (l = batch->list.head; l; l = next) { @@ -328,4 +332,6 @@ void grpc_metadata_batch_filter(grpc_metadata_batch *batch, } assert_valid_list(&batch->list); assert_valid_list(&batch->garbage); + + GPR_TIMER_END("grpc_metadata_batch_filter", 0); } diff --git a/src/core/transport/transport.c b/src/core/transport/transport.c index c0d92cf93f..828d212cfe 100644 --- a/src/core/transport/transport.c +++ b/src/core/transport/transport.c @@ -40,48 +40,48 @@ size_t grpc_transport_stream_size(grpc_transport *transport) { return transport->vtable->sizeof_stream; } -void grpc_transport_destroy(grpc_transport *transport) { - transport->vtable->destroy(transport); +void grpc_transport_destroy(grpc_exec_ctx *exec_ctx, + grpc_transport *transport) { + transport->vtable->destroy(exec_ctx, transport); } -int grpc_transport_init_stream(grpc_transport *transport, grpc_stream *stream, +int grpc_transport_init_stream(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_stream *stream, const void *server_data, grpc_transport_stream_op *initial_op) { - return transport->vtable->init_stream(transport, stream, server_data, - initial_op); + return transport->vtable->init_stream(exec_ctx, transport, stream, + server_data, initial_op); } -void grpc_transport_perform_stream_op(grpc_transport *transport, +void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_stream *stream, grpc_transport_stream_op *op) { - transport->vtable->perform_stream_op(transport, stream, op); + transport->vtable->perform_stream_op(exec_ctx, transport, stream, op); } -void grpc_transport_perform_op(grpc_transport *transport, +void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_transport_op *op) { - transport->vtable->perform_op(transport, op); + transport->vtable->perform_op(exec_ctx, transport, op); } -void grpc_transport_destroy_stream(grpc_transport *transport, +void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_stream *stream) { - transport->vtable->destroy_stream(transport, stream); + transport->vtable->destroy_stream(exec_ctx, transport, stream); } -char *grpc_transport_get_peer(grpc_transport *transport) { - return transport->vtable->get_peer(transport); +char *grpc_transport_get_peer(grpc_exec_ctx *exec_ctx, + grpc_transport *transport) { + return transport->vtable->get_peer(exec_ctx, transport); } void grpc_transport_stream_op_finish_with_failure( - grpc_transport_stream_op *op) { - if (op->send_ops) { - op->on_done_send->cb(op->on_done_send->cb_arg, 0); - } - if (op->recv_ops) { - op->on_done_recv->cb(op->on_done_recv->cb_arg, 0); - } - if (op->on_consumed) { - op->on_consumed->cb(op->on_consumed->cb_arg, 0); - } + grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op) { + grpc_exec_ctx_enqueue(exec_ctx, op->on_done_recv, 0); + grpc_exec_ctx_enqueue(exec_ctx, op->on_done_send, 0); + grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 0); } void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op, @@ -101,15 +101,15 @@ void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op, typedef struct { gpr_slice message; - grpc_iomgr_closure *then_call; - grpc_iomgr_closure closure; + grpc_closure *then_call; + grpc_closure closure; } close_message_data; -static void free_message(void *p, int iomgr_success) { +static void free_message(grpc_exec_ctx *exec_ctx, void *p, int iomgr_success) { close_message_data *cmd = p; gpr_slice_unref(cmd->message); if (cmd->then_call != NULL) { - cmd->then_call->cb(cmd->then_call->cb_arg, iomgr_success); + cmd->then_call->cb(exec_ctx, cmd->then_call->cb_arg, iomgr_success); } gpr_free(cmd); } @@ -130,7 +130,7 @@ void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op, cmd = gpr_malloc(sizeof(*cmd)); cmd->message = *optional_message; cmd->then_call = op->on_consumed; - grpc_iomgr_closure_init(&cmd->closure, free_message, cmd); + grpc_closure_init(&cmd->closure, free_message, cmd); op->on_consumed = &cmd->closure; op->optional_close_message = &cmd->message; } diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h index 92c1f38c5e..d4cee03862 100644 --- a/src/core/transport/transport.h +++ b/src/core/transport/transport.h @@ -64,19 +64,19 @@ typedef enum grpc_stream_state { /* Transport stream op: a set of operations to perform on a transport against a single stream */ typedef struct grpc_transport_stream_op { - grpc_iomgr_closure *on_consumed; + grpc_closure *on_consumed; grpc_stream_op_buffer *send_ops; int is_last_send; - grpc_iomgr_closure *on_done_send; + grpc_closure *on_done_send; grpc_stream_op_buffer *recv_ops; grpc_stream_state *recv_state; /** The number of bytes this peer is currently prepared to receive. These bytes will be eventually used to replenish per-stream flow control windows. */ - gpr_uint32 max_recv_bytes; - grpc_iomgr_closure *on_done_recv; + size_t max_recv_bytes; + grpc_closure *on_done_recv; grpc_pollset *bind_pollset; @@ -95,9 +95,9 @@ typedef struct grpc_transport_stream_op { /** Transport op: a set of operations to perform on a transport as a whole */ typedef struct grpc_transport_op { /** called when processing of this op is done */ - grpc_iomgr_closure *on_consumed; + grpc_closure *on_consumed; /** connectivity monitoring */ - grpc_iomgr_closure *on_connectivity_state_change; + grpc_closure *on_connectivity_state_change; grpc_connectivity_state *connectivity_state; /** should the transport be disconnected */ int disconnect; @@ -118,7 +118,7 @@ typedef struct grpc_transport_op { /** add this transport to a pollset_set */ grpc_pollset_set *bind_pollset_set; /** send a ping, call this back if not NULL */ - grpc_iomgr_closure *send_ping; + grpc_closure *send_ping; } grpc_transport_op; /* Returns the amount of memory required to store a grpc_stream for this @@ -134,7 +134,8 @@ size_t grpc_transport_stream_size(grpc_transport *transport); stream - a pointer to uninitialized memory to initialize server_data - either NULL for a client initiated stream, or a pointer supplied from the accept_stream callback function */ -int grpc_transport_init_stream(grpc_transport *transport, grpc_stream *stream, +int grpc_transport_init_stream(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_stream *stream, const void *server_data, grpc_transport_stream_op *initial_op); @@ -148,10 +149,12 @@ int grpc_transport_init_stream(grpc_transport *transport, grpc_stream *stream, transport - the transport on which to create this stream stream - the grpc_stream to destroy (memory is still owned by the caller, but any child memory must be cleaned up) */ -void grpc_transport_destroy_stream(grpc_transport *transport, +void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_stream *stream); -void grpc_transport_stream_op_finish_with_failure(grpc_transport_stream_op *op); +void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx, + grpc_transport_stream_op *op); void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op, grpc_status_code status); @@ -171,17 +174,19 @@ char *grpc_transport_stream_op_string(grpc_transport_stream_op *op); stream - the stream on which to send the operations. This must be non-NULL and previously initialized by the same transport. op - a grpc_transport_stream_op specifying the op to perform */ -void grpc_transport_perform_stream_op(grpc_transport *transport, +void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_stream *stream, grpc_transport_stream_op *op); -void grpc_transport_perform_op(grpc_transport *transport, +void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_transport_op *op); /* Send a ping on a transport Calls cb with user data when a response is received. */ -void grpc_transport_ping(grpc_transport *transport, grpc_iomgr_closure *cb); +void grpc_transport_ping(grpc_transport *transport, grpc_closure *cb); /* Advise peer of pending connection termination. */ void grpc_transport_goaway(grpc_transport *transport, grpc_status_code status, @@ -191,9 +196,10 @@ void grpc_transport_goaway(grpc_transport *transport, grpc_status_code status, void grpc_transport_close(grpc_transport *transport); /* Destroy the transport */ -void grpc_transport_destroy(grpc_transport *transport); +void grpc_transport_destroy(grpc_exec_ctx *exec_ctx, grpc_transport *transport); /* Get the transports peer */ -char *grpc_transport_get_peer(grpc_transport *transport); +char *grpc_transport_get_peer(grpc_exec_ctx *exec_ctx, + grpc_transport *transport); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_TRANSPORT_H */ diff --git a/src/core/transport/transport_impl.h b/src/core/transport/transport_impl.h index d3bbdf6c27..900c6340ff 100644 --- a/src/core/transport/transport_impl.h +++ b/src/core/transport/transport_impl.h @@ -42,25 +42,27 @@ typedef struct grpc_transport_vtable { size_t sizeof_stream; /* = sizeof(transport stream) */ /* implementation of grpc_transport_init_stream */ - int (*init_stream)(grpc_transport *self, grpc_stream *stream, - const void *server_data, + int (*init_stream)(grpc_exec_ctx *exec_ctx, grpc_transport *self, + grpc_stream *stream, const void *server_data, grpc_transport_stream_op *initial_op); /* implementation of grpc_transport_perform_stream_op */ - void (*perform_stream_op)(grpc_transport *self, grpc_stream *stream, - grpc_transport_stream_op *op); + void (*perform_stream_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self, + grpc_stream *stream, grpc_transport_stream_op *op); /* implementation of grpc_transport_perform_op */ - void (*perform_op)(grpc_transport *self, grpc_transport_op *op); + void (*perform_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self, + grpc_transport_op *op); /* implementation of grpc_transport_destroy_stream */ - void (*destroy_stream)(grpc_transport *self, grpc_stream *stream); + void (*destroy_stream)(grpc_exec_ctx *exec_ctx, grpc_transport *self, + grpc_stream *stream); /* implementation of grpc_transport_destroy */ - void (*destroy)(grpc_transport *self); + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_transport *self); /* implementation of grpc_transport_get_peer */ - char *(*get_peer)(grpc_transport *self); + char *(*get_peer)(grpc_exec_ctx *exec_ctx, grpc_transport *self); } grpc_transport_vtable; /* an instance of a grpc transport */ diff --git a/src/core/tsi/fake_transport_security.c b/src/core/tsi/fake_transport_security.c index 29127c4269..99e28ab63b 100644 --- a/src/core/tsi/fake_transport_security.c +++ b/src/core/tsi/fake_transport_security.c @@ -53,7 +53,7 @@ where the size field value is the size of the size field plus the size of the data encoded in little endian on 4 bytes. */ typedef struct { - unsigned char* data; + unsigned char *data; size_t size; size_t allocated_size; size_t offset; @@ -87,10 +87,10 @@ typedef struct { /* --- Utils. ---*/ -static const char* tsi_fake_handshake_message_strings[] = { +static const char *tsi_fake_handshake_message_strings[] = { "CLIENT_INIT", "SERVER_INIT", "CLIENT_FINISHED", "SERVER_FINISHED"}; -static const char* tsi_fake_handshake_message_to_string(int msg) { +static const char *tsi_fake_handshake_message_to_string(int msg) { if (msg < 0 || msg >= TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { gpr_log(GPR_ERROR, "Invalid message %d", msg); return "UNKNOWN"; @@ -99,8 +99,8 @@ static const char* tsi_fake_handshake_message_to_string(int msg) { } static tsi_result tsi_fake_handshake_message_from_string( - const char* msg_string, tsi_fake_handshake_message* msg) { - int i; + const char *msg_string, tsi_fake_handshake_message *msg) { + tsi_fake_handshake_message i; for (i = 0; i < TSI_FAKE_HANDSHAKE_MESSAGE_MAX; i++) { if (strncmp(msg_string, tsi_fake_handshake_message_strings[i], strlen(tsi_fake_handshake_message_strings[i])) == 0) { @@ -112,32 +112,32 @@ static tsi_result tsi_fake_handshake_message_from_string( return TSI_DATA_CORRUPTED; } -static gpr_uint32 load32_little_endian(const unsigned char* buf) { +static gpr_uint32 load32_little_endian(const unsigned char *buf) { return ((gpr_uint32)(buf[0]) | (gpr_uint32)(buf[1] << 8) | (gpr_uint32)(buf[2] << 16) | (gpr_uint32)(buf[3] << 24)); } -static void store32_little_endian(gpr_uint32 value, unsigned char* buf) { - buf[3] = (unsigned char)(value >> 24) & 0xFF; - buf[2] = (unsigned char)(value >> 16) & 0xFF; - buf[1] = (unsigned char)(value >> 8) & 0xFF; - buf[0] = (unsigned char)(value)&0xFF; +static void store32_little_endian(gpr_uint32 value, unsigned char *buf) { + buf[3] = (unsigned char)((value >> 24) & 0xFF); + buf[2] = (unsigned char)((value >> 16) & 0xFF); + buf[1] = (unsigned char)((value >> 8) & 0xFF); + buf[0] = (unsigned char)((value)&0xFF); } -static void tsi_fake_frame_reset(tsi_fake_frame* frame, int needs_draining) { +static void tsi_fake_frame_reset(tsi_fake_frame *frame, int needs_draining) { frame->offset = 0; frame->needs_draining = needs_draining; if (!needs_draining) frame->size = 0; } /* Returns 1 if successful, 0 otherwise. */ -static int tsi_fake_frame_ensure_size(tsi_fake_frame* frame) { +static int tsi_fake_frame_ensure_size(tsi_fake_frame *frame) { if (frame->data == NULL) { frame->allocated_size = frame->size; frame->data = malloc(frame->allocated_size); if (frame->data == NULL) return 0; } else if (frame->size > frame->allocated_size) { - unsigned char* new_data = realloc(frame->data, frame->size); + unsigned char *new_data = realloc(frame->data, frame->size); if (new_data == NULL) { free(frame->data); frame->data = NULL; @@ -150,12 +150,12 @@ static int tsi_fake_frame_ensure_size(tsi_fake_frame* frame) { } /* This method should not be called if frame->needs_framing is not 0. */ -static tsi_result fill_frame_from_bytes(const unsigned char* incoming_bytes, - size_t* incoming_bytes_size, - tsi_fake_frame* frame) { +static tsi_result fill_frame_from_bytes(const unsigned char *incoming_bytes, + size_t *incoming_bytes_size, + tsi_fake_frame *frame) { size_t available_size = *incoming_bytes_size; size_t to_read_size = 0; - const unsigned char* bytes_cursor = incoming_bytes; + const unsigned char *bytes_cursor = incoming_bytes; if (frame->needs_draining) return TSI_INTERNAL_ERROR; if (frame->data == NULL) { @@ -171,7 +171,7 @@ static tsi_result fill_frame_from_bytes(const unsigned char* incoming_bytes, memcpy(frame->data + frame->offset, bytes_cursor, available_size); bytes_cursor += available_size; frame->offset += available_size; - *incoming_bytes_size = bytes_cursor - incoming_bytes; + *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); return TSI_INCOMPLETE_DATA; } memcpy(frame->data + frame->offset, bytes_cursor, to_read_size); @@ -187,20 +187,20 @@ static tsi_result fill_frame_from_bytes(const unsigned char* incoming_bytes, memcpy(frame->data + frame->offset, bytes_cursor, available_size); frame->offset += available_size; bytes_cursor += available_size; - *incoming_bytes_size = bytes_cursor - incoming_bytes; + *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); return TSI_INCOMPLETE_DATA; } memcpy(frame->data + frame->offset, bytes_cursor, to_read_size); bytes_cursor += to_read_size; - *incoming_bytes_size = bytes_cursor - incoming_bytes; + *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); tsi_fake_frame_reset(frame, 1 /* needs_draining */); return TSI_OK; } /* This method should not be called if frame->needs_framing is 0. */ -static tsi_result drain_frame_to_bytes(unsigned char* outgoing_bytes, - size_t* outgoing_bytes_size, - tsi_fake_frame* frame) { +static tsi_result drain_frame_to_bytes(unsigned char *outgoing_bytes, + size_t *outgoing_bytes_size, + tsi_fake_frame *frame) { size_t to_write_size = frame->size - frame->offset; if (!frame->needs_draining) return TSI_INTERNAL_ERROR; if (*outgoing_bytes_size < to_write_size) { @@ -214,35 +214,35 @@ static tsi_result drain_frame_to_bytes(unsigned char* outgoing_bytes, return TSI_OK; } -static tsi_result bytes_to_frame(unsigned char* bytes, size_t bytes_size, - tsi_fake_frame* frame) { +static tsi_result bytes_to_frame(unsigned char *bytes, size_t bytes_size, + tsi_fake_frame *frame) { frame->offset = 0; frame->size = bytes_size + TSI_FAKE_FRAME_HEADER_SIZE; if (!tsi_fake_frame_ensure_size(frame)) return TSI_OUT_OF_RESOURCES; - store32_little_endian(frame->size, frame->data); + store32_little_endian((gpr_uint32)frame->size, frame->data); memcpy(frame->data + TSI_FAKE_FRAME_HEADER_SIZE, bytes, bytes_size); tsi_fake_frame_reset(frame, 1 /* needs draining */); return TSI_OK; } -static void tsi_fake_frame_destruct(tsi_fake_frame* frame) { +static void tsi_fake_frame_destruct(tsi_fake_frame *frame) { if (frame->data != NULL) free(frame->data); } /* --- tsi_frame_protector methods implementation. ---*/ -static tsi_result fake_protector_protect(tsi_frame_protector* self, - const unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size, - unsigned char* protected_output_frames, - size_t* protected_output_frames_size) { +static tsi_result fake_protector_protect(tsi_frame_protector *self, + const unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size) { tsi_result result = TSI_OK; - tsi_fake_frame_protector* impl = (tsi_fake_frame_protector*)self; + tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; unsigned char frame_header[TSI_FAKE_FRAME_HEADER_SIZE]; - tsi_fake_frame* frame = &impl->protect_frame; + tsi_fake_frame *frame = &impl->protect_frame; size_t saved_output_size = *protected_output_frames_size; size_t drained_size = 0; - size_t* num_bytes_written = protected_output_frames_size; + size_t *num_bytes_written = protected_output_frames_size; *num_bytes_written = 0; /* Try to drain first. */ @@ -266,7 +266,7 @@ static tsi_result fake_protector_protect(tsi_frame_protector* self, if (frame->size == 0) { /* New frame, create a header. */ size_t written_in_frame_size = 0; - store32_little_endian(impl->max_frame_size, frame_header); + store32_little_endian((gpr_uint32)impl->max_frame_size, frame_header); written_in_frame_size = TSI_FAKE_FRAME_HEADER_SIZE; result = fill_frame_from_bytes(frame_header, &written_in_frame_size, frame); if (result != TSI_INCOMPLETE_DATA) { @@ -293,17 +293,18 @@ static tsi_result fake_protector_protect(tsi_frame_protector* self, } static tsi_result fake_protector_protect_flush( - tsi_frame_protector* self, unsigned char* protected_output_frames, - size_t* protected_output_frames_size, size_t* still_pending_size) { + tsi_frame_protector *self, unsigned char *protected_output_frames, + size_t *protected_output_frames_size, size_t *still_pending_size) { tsi_result result = TSI_OK; - tsi_fake_frame_protector* impl = (tsi_fake_frame_protector*)self; - tsi_fake_frame* frame = &impl->protect_frame; + tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; + tsi_fake_frame *frame = &impl->protect_frame; if (!frame->needs_draining) { /* Create a short frame. */ frame->size = frame->offset; frame->offset = 0; frame->needs_draining = 1; - store32_little_endian(frame->size, frame->data); /* Overwrite header. */ + store32_little_endian((gpr_uint32)frame->size, + frame->data); /* Overwrite header. */ } result = drain_frame_to_bytes(protected_output_frames, protected_output_frames_size, frame); @@ -313,15 +314,15 @@ static tsi_result fake_protector_protect_flush( } static tsi_result fake_protector_unprotect( - tsi_frame_protector* self, const unsigned char* protected_frames_bytes, - size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size) { + tsi_frame_protector *self, const unsigned char *protected_frames_bytes, + size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size) { tsi_result result = TSI_OK; - tsi_fake_frame_protector* impl = (tsi_fake_frame_protector*)self; - tsi_fake_frame* frame = &impl->unprotect_frame; + tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; + tsi_fake_frame *frame = &impl->unprotect_frame; size_t saved_output_size = *unprotected_bytes_size; size_t drained_size = 0; - size_t* num_bytes_written = unprotected_bytes_size; + size_t *num_bytes_written = unprotected_bytes_size; *num_bytes_written = 0; /* Try to drain first. */ @@ -361,8 +362,8 @@ static tsi_result fake_protector_unprotect( return result; } -static void fake_protector_destroy(tsi_frame_protector* self) { - tsi_fake_frame_protector* impl = (tsi_fake_frame_protector*)self; +static void fake_protector_destroy(tsi_frame_protector *self) { + tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; tsi_fake_frame_destruct(&impl->protect_frame); tsi_fake_frame_destruct(&impl->unprotect_frame); free(self); @@ -376,18 +377,19 @@ static const tsi_frame_protector_vtable frame_protector_vtable = { /* --- tsi_handshaker methods implementation. ---*/ static tsi_result fake_handshaker_get_bytes_to_send_to_peer( - tsi_handshaker* self, unsigned char* bytes, size_t* bytes_size) { - tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; + tsi_handshaker *self, unsigned char *bytes, size_t *bytes_size) { + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; tsi_result result = TSI_OK; if (impl->needs_incoming_message || impl->result == TSI_OK) { *bytes_size = 0; return TSI_OK; } if (!impl->outgoing.needs_draining) { - int next_message_to_send = impl->next_message_to_send + 2; - const char* msg_string = + tsi_fake_handshake_message next_message_to_send = + impl->next_message_to_send + 2; + const char *msg_string = tsi_fake_handshake_message_to_string(impl->next_message_to_send); - result = bytes_to_frame((unsigned char*)msg_string, strlen(msg_string), + result = bytes_to_frame((unsigned char *)msg_string, strlen(msg_string), &impl->outgoing); if (result != TSI_OK) return result; if (next_message_to_send > TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { @@ -416,9 +418,9 @@ static tsi_result fake_handshaker_get_bytes_to_send_to_peer( } static tsi_result fake_handshaker_process_bytes_from_peer( - tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) { + tsi_handshaker *self, const unsigned char *bytes, size_t *bytes_size) { tsi_result result = TSI_OK; - tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; tsi_fake_handshake_message expected_msg = impl->next_message_to_send - 1; tsi_fake_handshake_message received_msg; @@ -431,7 +433,7 @@ static tsi_result fake_handshaker_process_bytes_from_peer( /* We now have a complete frame. */ result = tsi_fake_handshake_message_from_string( - (const char*)impl->incoming.data + TSI_FAKE_FRAME_HEADER_SIZE, + (const char *)impl->incoming.data + TSI_FAKE_FRAME_HEADER_SIZE, &received_msg); if (result != TSI_OK) { impl->result = result; @@ -458,13 +460,13 @@ static tsi_result fake_handshaker_process_bytes_from_peer( return TSI_OK; } -static tsi_result fake_handshaker_get_result(tsi_handshaker* self) { - tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; +static tsi_result fake_handshaker_get_result(tsi_handshaker *self) { + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; return impl->result; } -static tsi_result fake_handshaker_extract_peer(tsi_handshaker* self, - tsi_peer* peer) { +static tsi_result fake_handshaker_extract_peer(tsi_handshaker *self, + tsi_peer *peer) { tsi_result result = tsi_construct_peer(1, peer); if (result != TSI_OK) return result; result = tsi_construct_string_peer_property_from_cstring( @@ -475,15 +477,15 @@ static tsi_result fake_handshaker_extract_peer(tsi_handshaker* self, } static tsi_result fake_handshaker_create_frame_protector( - tsi_handshaker* self, size_t* max_protected_frame_size, - tsi_frame_protector** protector) { + tsi_handshaker *self, size_t *max_protected_frame_size, + tsi_frame_protector **protector) { *protector = tsi_create_fake_protector(max_protected_frame_size); if (*protector == NULL) return TSI_OUT_OF_RESOURCES; return TSI_OK; } -static void fake_handshaker_destroy(tsi_handshaker* self) { - tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; +static void fake_handshaker_destroy(tsi_handshaker *self) { + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; tsi_fake_frame_destruct(&impl->incoming); tsi_fake_frame_destruct(&impl->outgoing); free(self); @@ -491,15 +493,13 @@ static void fake_handshaker_destroy(tsi_handshaker* self) { static const tsi_handshaker_vtable handshaker_vtable = { fake_handshaker_get_bytes_to_send_to_peer, - fake_handshaker_process_bytes_from_peer, - fake_handshaker_get_result, - fake_handshaker_extract_peer, - fake_handshaker_create_frame_protector, + fake_handshaker_process_bytes_from_peer, fake_handshaker_get_result, + fake_handshaker_extract_peer, fake_handshaker_create_frame_protector, fake_handshaker_destroy, }; -tsi_handshaker* tsi_create_fake_handshaker(int is_client) { - tsi_fake_handshaker* impl = calloc(1, sizeof(tsi_fake_handshaker)); +tsi_handshaker *tsi_create_fake_handshaker(int is_client) { + tsi_fake_handshaker *impl = calloc(1, sizeof(tsi_fake_handshaker)); impl->base.vtable = &handshaker_vtable; impl->is_client = is_client; impl->result = TSI_HANDSHAKE_IN_PROGRESS; @@ -513,9 +513,9 @@ tsi_handshaker* tsi_create_fake_handshaker(int is_client) { return &impl->base; } -tsi_frame_protector* tsi_create_fake_protector( - size_t* max_protected_frame_size) { - tsi_fake_frame_protector* impl = calloc(1, sizeof(tsi_fake_frame_protector)); +tsi_frame_protector *tsi_create_fake_protector( + size_t *max_protected_frame_size) { + tsi_fake_frame_protector *impl = calloc(1, sizeof(tsi_fake_frame_protector)); if (impl == NULL) return NULL; impl->max_frame_size = (max_protected_frame_size == NULL) ? TSI_FAKE_DEFAULT_FRAME_SIZE diff --git a/src/core/tsi/fake_transport_security.h b/src/core/tsi/fake_transport_security.h index 1fa11349fb..fe295aa536 100644 --- a/src/core/tsi/fake_transport_security.h +++ b/src/core/tsi/fake_transport_security.h @@ -48,11 +48,11 @@ extern "C" { No cryptography is performed in these objects. They just simulate handshake messages going back and forth for the handshaker and do some framing on cleartext data for the protector. */ -tsi_handshaker* tsi_create_fake_handshaker(int is_client); +tsi_handshaker *tsi_create_fake_handshaker(int is_client); /* Creates a protector directly without going through the handshake phase. */ -tsi_frame_protector* tsi_create_fake_protector( - size_t* max_protected_frame_size); +tsi_frame_protector *tsi_create_fake_protector( + size_t *max_protected_frame_size); #ifdef __cplusplus } diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c index 0b416f6c9d..22b57964cc 100644 --- a/src/core/tsi/ssl_transport_security.c +++ b/src/core/tsi/ssl_transport_security.c @@ -68,16 +68,16 @@ /* --- Structure definitions. ---*/ struct tsi_ssl_handshaker_factory { - tsi_result (*create_handshaker)(tsi_ssl_handshaker_factory* self, - const char* server_name_indication, - tsi_handshaker** handshaker); - void (*destroy)(tsi_ssl_handshaker_factory* self); + tsi_result (*create_handshaker)(tsi_ssl_handshaker_factory *self, + const char *server_name_indication, + tsi_handshaker **handshaker); + void (*destroy)(tsi_ssl_handshaker_factory *self); }; typedef struct { tsi_ssl_handshaker_factory base; - SSL_CTX* ssl_context; - unsigned char* alpn_protocol_list; + SSL_CTX *ssl_context; + unsigned char *alpn_protocol_list; size_t alpn_protocol_list_length; } tsi_ssl_client_handshaker_factory; @@ -87,27 +87,27 @@ typedef struct { /* Several contexts to support SNI. The tsi_peer array contains the subject names of the server certificates associated with the contexts at the same index. */ - SSL_CTX** ssl_contexts; - tsi_peer* ssl_context_x509_subject_names; + SSL_CTX **ssl_contexts; + tsi_peer *ssl_context_x509_subject_names; size_t ssl_context_count; - unsigned char* alpn_protocol_list; + unsigned char *alpn_protocol_list; size_t alpn_protocol_list_length; } tsi_ssl_server_handshaker_factory; typedef struct { tsi_handshaker base; - SSL* ssl; - BIO* into_ssl; - BIO* from_ssl; + SSL *ssl; + BIO *into_ssl; + BIO *from_ssl; tsi_result result; } tsi_ssl_handshaker; typedef struct { tsi_frame_protector base; - SSL* ssl; - BIO* into_ssl; - BIO* from_ssl; - unsigned char* buffer; + SSL *ssl; + BIO *into_ssl; + BIO *from_ssl; + unsigned char *buffer; size_t buffer_size; size_t buffer_offset; } tsi_ssl_frame_protector; @@ -115,9 +115,9 @@ typedef struct { /* --- Library Initialization. ---*/ static gpr_once init_openssl_once = GPR_ONCE_INIT; -static gpr_mu* openssl_mutexes = NULL; +static gpr_mu *openssl_mutexes = NULL; -static void openssl_locking_cb(int mode, int type, const char* file, int line) { +static void openssl_locking_cb(int mode, int type, const char *file, int line) { if (mode & CRYPTO_LOCK) { gpr_mu_lock(&openssl_mutexes[type]); } else { @@ -131,10 +131,13 @@ static unsigned long openssl_thread_id_cb(void) { static void init_openssl(void) { int i; + int num_locks; SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); - openssl_mutexes = malloc(CRYPTO_num_locks() * sizeof(gpr_mu)); + num_locks = CRYPTO_num_locks(); + GPR_ASSERT(num_locks > 0); + openssl_mutexes = malloc((size_t)num_locks * sizeof(gpr_mu)); GPR_ASSERT(openssl_mutexes != NULL); for (i = 0; i < CRYPTO_num_locks(); i++) { gpr_mu_init(&openssl_mutexes[i]); @@ -145,7 +148,7 @@ static void init_openssl(void) { /* --- Ssl utils. ---*/ -static const char* ssl_error_string(int error) { +static const char *ssl_error_string(int error) { switch (error) { case SSL_ERROR_NONE: return "SSL_ERROR_NONE"; @@ -171,8 +174,8 @@ static const char* ssl_error_string(int error) { } /* TODO(jboeuf): Remove when we are past the debugging phase with this code. */ -static void ssl_log_where_info(const SSL* ssl, int where, int flag, - const char* msg) { +static void ssl_log_where_info(const SSL *ssl, int where, int flag, + const char *msg) { if ((where & flag) && tsi_tracing_enabled) { gpr_log(GPR_INFO, "%20.20s - %30.30s - %5.10s", msg, SSL_state_string_long(ssl), SSL_state_string(ssl)); @@ -180,7 +183,7 @@ static void ssl_log_where_info(const SSL* ssl, int where, int flag, } /* Used for debugging. TODO(jboeuf): Remove when code is mature enough. */ -static void ssl_info_callback(const SSL* ssl, int where, int ret) { +static void ssl_info_callback(const SSL *ssl, int where, int ret) { if (ret == 0) { gpr_log(GPR_ERROR, "ssl_info_callback: error occured.\n"); return; @@ -194,7 +197,7 @@ static void ssl_info_callback(const SSL* ssl, int where, int ret) { /* Returns 1 if name looks like an IP address, 0 otherwise. This is a very rough heuristic as it does not handle IPV6 or things like: 0300.0250.00.01, 0xC0.0Xa8.0x0.0x1, 000030052000001, 0xc0.052000001 */ -static int looks_like_ip_address(const char* name) { +static int looks_like_ip_address(const char *name) { size_t i; size_t dot_count = 0; size_t num_size = 0; @@ -215,12 +218,12 @@ static int looks_like_ip_address(const char* name) { } /* Gets the subject CN from an X509 cert. */ -static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8, - size_t* utf8_size) { +static tsi_result ssl_get_x509_common_name(X509 *cert, unsigned char **utf8, + size_t *utf8_size) { int common_name_index = -1; - X509_NAME_ENTRY* common_name_entry = NULL; - ASN1_STRING* common_name_asn1 = NULL; - X509_NAME* subject_name = X509_get_subject_name(cert); + X509_NAME_ENTRY *common_name_entry = NULL; + ASN1_STRING *common_name_asn1 = NULL; + X509_NAME *subject_name = X509_get_subject_name(cert); int utf8_returned_size = 0; if (subject_name == NULL) { gpr_log(GPR_ERROR, "Could not get subject name from certificate."); @@ -249,14 +252,14 @@ static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8, gpr_log(GPR_ERROR, "Could not extract utf8 from asn1 string."); return TSI_OUT_OF_RESOURCES; } - *utf8_size = utf8_returned_size; + *utf8_size = (size_t)utf8_returned_size; return TSI_OK; } /* Gets the subject CN of an X509 cert as a tsi_peer_property. */ static tsi_result peer_property_from_x509_common_name( - X509* cert, tsi_peer_property* property) { - unsigned char* common_name; + X509 *cert, tsi_peer_property *property) { + unsigned char *common_name; size_t common_name_size; tsi_result result = ssl_get_x509_common_name(cert, &common_name, &common_name_size); @@ -270,7 +273,7 @@ static tsi_result peer_property_from_x509_common_name( } result = tsi_construct_string_peer_property( TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, - common_name == NULL ? "" : (const char*)common_name, common_name_size, + common_name == NULL ? "" : (const char *)common_name, common_name_size, property); OPENSSL_free(common_name); return result; @@ -278,20 +281,20 @@ static tsi_result peer_property_from_x509_common_name( /* Gets the subject SANs from an X509 cert as a tsi_peer_property. */ static tsi_result add_subject_alt_names_properties_to_peer( - tsi_peer* peer, GENERAL_NAMES* subject_alt_names, - int subject_alt_name_count) { - int i; + tsi_peer *peer, GENERAL_NAMES *subject_alt_names, + size_t subject_alt_name_count) { + size_t i; tsi_result result = TSI_OK; /* Reset for DNS entries filtering. */ peer->property_count -= subject_alt_name_count; for (i = 0; i < subject_alt_name_count; i++) { - GENERAL_NAME* subject_alt_name = - sk_GENERAL_NAME_value(subject_alt_names, i); + GENERAL_NAME *subject_alt_name = + sk_GENERAL_NAME_value(subject_alt_names, (int)i); /* Filter out the non-dns entries names. */ if (subject_alt_name->type == GEN_DNS) { - unsigned char* dns_name = NULL; + unsigned char *dns_name = NULL; int dns_name_size = ASN1_STRING_to_UTF8(&dns_name, subject_alt_name->d.dNSName); if (dns_name_size < 0) { @@ -301,7 +304,7 @@ static tsi_result add_subject_alt_names_properties_to_peer( } result = tsi_construct_string_peer_property( TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, - (const char*)dns_name, dns_name_size, + (const char *)dns_name, (size_t)dns_name_size, &peer->properties[peer->property_count++]); OPENSSL_free(dns_name); if (result != TSI_OK) break; @@ -311,16 +314,20 @@ static tsi_result add_subject_alt_names_properties_to_peer( } /* Gets information about the peer's X509 cert as a tsi_peer object. */ -static tsi_result peer_from_x509(X509* cert, int include_certificate_type, - tsi_peer* peer) { +static tsi_result peer_from_x509(X509 *cert, int include_certificate_type, + tsi_peer *peer) { /* TODO(jboeuf): Maybe add more properties. */ - GENERAL_NAMES* subject_alt_names = + GENERAL_NAMES *subject_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); - int subject_alt_name_count = - (subject_alt_names != NULL) ? sk_GENERAL_NAME_num(subject_alt_names) : 0; - size_t property_count = (include_certificate_type ? 1 : 0) + - 1 /* common name */ + subject_alt_name_count; - tsi_result result = tsi_construct_peer(property_count, peer); + int subject_alt_name_count = (subject_alt_names != NULL) + ? (int)sk_GENERAL_NAME_num(subject_alt_names) + : 0; + size_t property_count; + tsi_result result; + GPR_ASSERT(subject_alt_name_count >= 0); + property_count = (include_certificate_type ? (size_t)1 : 0) + + 1 /* common name */ + (size_t)subject_alt_name_count; + result = tsi_construct_peer(property_count, peer); if (result != TSI_OK) return result; do { if (include_certificate_type) { @@ -334,8 +341,8 @@ static tsi_result peer_from_x509(X509* cert, int include_certificate_type, if (result != TSI_OK) break; if (subject_alt_name_count != 0) { - result = add_subject_alt_names_properties_to_peer(peer, subject_alt_names, - subject_alt_name_count); + result = add_subject_alt_names_properties_to_peer( + peer, subject_alt_names, (size_t)subject_alt_name_count); if (result != TSI_OK) break; } } while (0); @@ -352,15 +359,18 @@ static void log_ssl_error_stack(void) { unsigned long err; while ((err = ERR_get_error()) != 0) { char details[256]; - ERR_error_string_n(err, details, sizeof(details)); + ERR_error_string_n((uint32_t)err, details, sizeof(details)); gpr_log(GPR_ERROR, "%s", details); } } /* Performs an SSL_read and handle errors. */ -static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size) { - int read_from_ssl = SSL_read(ssl, unprotected_bytes, *unprotected_bytes_size); +static tsi_result do_ssl_read(SSL *ssl, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size) { + int read_from_ssl; + GPR_ASSERT(*unprotected_bytes_size <= INT_MAX); + read_from_ssl = + SSL_read(ssl, unprotected_bytes, (int)*unprotected_bytes_size); if (read_from_ssl == 0) { gpr_log(GPR_ERROR, "SSL_read returned 0 unexpectedly."); return TSI_INTERNAL_ERROR; @@ -387,15 +397,17 @@ static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes, return TSI_PROTOCOL_FAILURE; } } - *unprotected_bytes_size = read_from_ssl; + *unprotected_bytes_size = (size_t)read_from_ssl; return TSI_OK; } /* Performs an SSL_write and handle errors. */ -static tsi_result do_ssl_write(SSL* ssl, unsigned char* unprotected_bytes, +static tsi_result do_ssl_write(SSL *ssl, unsigned char *unprotected_bytes, size_t unprotected_bytes_size) { - int ssl_write_result = - SSL_write(ssl, unprotected_bytes, unprotected_bytes_size); + int ssl_write_result; + GPR_ASSERT(unprotected_bytes_size <= INT_MAX); + ssl_write_result = + SSL_write(ssl, unprotected_bytes, (int)unprotected_bytes_size); if (ssl_write_result < 0) { ssl_write_result = SSL_get_error(ssl, ssl_write_result); if (ssl_write_result == SSL_ERROR_WANT_READ) { @@ -413,11 +425,13 @@ static tsi_result do_ssl_write(SSL* ssl, unsigned char* unprotected_bytes, /* Loads an in-memory PEM certificate chain into the SSL context. */ static tsi_result ssl_ctx_use_certificate_chain( - SSL_CTX* context, const unsigned char* pem_cert_chain, + SSL_CTX *context, const unsigned char *pem_cert_chain, size_t pem_cert_chain_size) { tsi_result result = TSI_OK; - X509* certificate = NULL; - BIO* pem = BIO_new_mem_buf((void*)pem_cert_chain, pem_cert_chain_size); + X509 *certificate = NULL; + BIO *pem; + GPR_ASSERT(pem_cert_chain_size <= INT_MAX); + pem = BIO_new_mem_buf((void *)pem_cert_chain, (int)pem_cert_chain_size); if (pem == NULL) return TSI_OUT_OF_RESOURCES; do { @@ -431,7 +445,7 @@ static tsi_result ssl_ctx_use_certificate_chain( break; } while (1) { - X509* certificate_authority = PEM_read_bio_X509(pem, NULL, NULL, ""); + X509 *certificate_authority = PEM_read_bio_X509(pem, NULL, NULL, ""); if (certificate_authority == NULL) { ERR_clear_error(); break; /* Done reading. */ @@ -443,7 +457,7 @@ static tsi_result ssl_ctx_use_certificate_chain( } /* We don't need to free certificate_authority as its ownership has been transfered to the context. That is not the case for certificate though. - */ + */ } } while (0); @@ -453,12 +467,14 @@ static tsi_result ssl_ctx_use_certificate_chain( } /* Loads an in-memory PEM private key into the SSL context. */ -static tsi_result ssl_ctx_use_private_key(SSL_CTX* context, - const unsigned char* pem_key, +static tsi_result ssl_ctx_use_private_key(SSL_CTX *context, + const unsigned char *pem_key, size_t pem_key_size) { tsi_result result = TSI_OK; - EVP_PKEY* private_key = NULL; - BIO* pem = BIO_new_mem_buf((void*)pem_key, pem_key_size); + EVP_PKEY *private_key = NULL; + BIO *pem; + GPR_ASSERT(pem_key_size <= INT_MAX); + pem = BIO_new_mem_buf((void *)pem_key, (int)pem_key_size); if (pem == NULL) return TSI_OUT_OF_RESOURCES; do { private_key = PEM_read_bio_PrivateKey(pem, NULL, NULL, ""); @@ -479,14 +495,17 @@ static tsi_result ssl_ctx_use_private_key(SSL_CTX* context, /* Loads in-memory PEM verification certs into the SSL context and optionally returns the verification cert names (root_names can be NULL). */ static tsi_result ssl_ctx_load_verification_certs( - SSL_CTX* context, const unsigned char* pem_roots, size_t pem_roots_size, + SSL_CTX *context, const unsigned char *pem_roots, size_t pem_roots_size, STACK_OF(X509_NAME) * *root_names) { tsi_result result = TSI_OK; size_t num_roots = 0; - X509* root = NULL; - X509_NAME* root_name = NULL; - BIO* pem = BIO_new_mem_buf((void*)pem_roots, pem_roots_size); - X509_STORE* root_store = SSL_CTX_get_cert_store(context); + X509 *root = NULL; + X509_NAME *root_name = NULL; + BIO *pem; + X509_STORE *root_store; + GPR_ASSERT(pem_roots_size <= INT_MAX); + pem = BIO_new_mem_buf((void *)pem_roots, (int)pem_roots_size); + root_store = SSL_CTX_get_cert_store(context); if (root_store == NULL) return TSI_INVALID_ARGUMENT; if (pem == NULL) return TSI_OUT_OF_RESOURCES; if (root_names != NULL) { @@ -544,9 +563,9 @@ static tsi_result ssl_ctx_load_verification_certs( /* Populates the SSL context with a private key and a cert chain, and sets the cipher list and the ephemeral ECDH key. */ static tsi_result populate_ssl_context( - SSL_CTX* context, const unsigned char* pem_private_key, - size_t pem_private_key_size, const unsigned char* pem_certificate_chain, - size_t pem_certificate_chain_size, const char* cipher_list) { + SSL_CTX *context, const unsigned char *pem_private_key, + size_t pem_private_key_size, const unsigned char *pem_certificate_chain, + size_t pem_certificate_chain_size, const char *cipher_list) { tsi_result result = TSI_OK; if (pem_certificate_chain != NULL) { result = ssl_ctx_use_certificate_chain(context, pem_certificate_chain, @@ -569,7 +588,7 @@ static tsi_result populate_ssl_context( return TSI_INVALID_ARGUMENT; } { - EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (!SSL_CTX_set_tmp_ecdh(context, ecdh)) { gpr_log(GPR_ERROR, "Could not set ephemeral ECDH key."); EC_KEY_free(ecdh); @@ -583,10 +602,12 @@ static tsi_result populate_ssl_context( /* Extracts the CN and the SANs from an X509 cert as a peer object. */ static tsi_result extract_x509_subject_names_from_pem_cert( - const unsigned char* pem_cert, size_t pem_cert_size, tsi_peer* peer) { + const unsigned char *pem_cert, size_t pem_cert_size, tsi_peer *peer) { tsi_result result = TSI_OK; - X509* cert = NULL; - BIO* pem = BIO_new_mem_buf((void*)pem_cert, pem_cert_size); + X509 *cert = NULL; + BIO *pem; + GPR_ASSERT(pem_cert_size <= INT_MAX); + pem = BIO_new_mem_buf((void *)pem_cert, (int)pem_cert_size); if (pem == NULL) return TSI_OUT_OF_RESOURCES; cert = PEM_read_bio_X509(pem, NULL, NULL, ""); @@ -603,11 +624,11 @@ static tsi_result extract_x509_subject_names_from_pem_cert( /* Builds the alpn protocol name list according to rfc 7301. */ static tsi_result build_alpn_protocol_name_list( - const unsigned char** alpn_protocols, - const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, - unsigned char** protocol_name_list, size_t* protocol_name_list_length) { + const unsigned char **alpn_protocols, + const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, + unsigned char **protocol_name_list, size_t *protocol_name_list_length) { uint16_t i; - unsigned char* current; + unsigned char *current; *protocol_name_list = NULL; *protocol_name_list_length = 0; if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT; @@ -616,7 +637,7 @@ static tsi_result build_alpn_protocol_name_list( gpr_log(GPR_ERROR, "Invalid 0-length protocol name."); return TSI_INVALID_ARGUMENT; } - *protocol_name_list_length += alpn_protocols_lengths[i] + 1; + *protocol_name_list_length += (size_t)alpn_protocols_lengths[i] + 1; } *protocol_name_list = malloc(*protocol_name_list_length); if (*protocol_name_list == NULL) return TSI_OUT_OF_RESOURCES; @@ -637,28 +658,29 @@ static tsi_result build_alpn_protocol_name_list( /* --- tsi_frame_protector methods implementation. ---*/ -static tsi_result ssl_protector_protect(tsi_frame_protector* self, - const unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size, - unsigned char* protected_output_frames, - size_t* protected_output_frames_size) { - tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; +static tsi_result ssl_protector_protect(tsi_frame_protector *self, + const unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size) { + tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; int read_from_ssl; size_t available; tsi_result result = TSI_OK; /* First see if we have some pending data in the SSL BIO. */ - size_t pending_in_ssl = BIO_pending(impl->from_ssl); + int pending_in_ssl = (int)BIO_pending(impl->from_ssl); if (pending_in_ssl > 0) { *unprotected_bytes_size = 0; + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - *protected_output_frames_size); + (int)*protected_output_frames_size); if (read_from_ssl < 0) { gpr_log(GPR_ERROR, "Could not read from BIO even though some data is pending"); return TSI_INTERNAL_ERROR; } - *protected_output_frames_size = read_from_ssl; + *protected_output_frames_size = (size_t)read_from_ssl; return TSI_OK; } @@ -678,24 +700,26 @@ static tsi_result ssl_protector_protect(tsi_frame_protector* self, result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_size); if (result != TSI_OK) return result; + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - *protected_output_frames_size); + (int)*protected_output_frames_size); if (read_from_ssl < 0) { gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); return TSI_INTERNAL_ERROR; } - *protected_output_frames_size = read_from_ssl; + *protected_output_frames_size = (size_t)read_from_ssl; *unprotected_bytes_size = available; impl->buffer_offset = 0; return TSI_OK; } static tsi_result ssl_protector_protect_flush( - tsi_frame_protector* self, unsigned char* protected_output_frames, - size_t* protected_output_frames_size, size_t* still_pending_size) { + tsi_frame_protector *self, unsigned char *protected_output_frames, + size_t *protected_output_frames_size, size_t *still_pending_size) { tsi_result result = TSI_OK; - tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; + tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; int read_from_ssl = 0; + int pending; if (impl->buffer_offset != 0) { result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_offset); @@ -703,29 +727,34 @@ static tsi_result ssl_protector_protect_flush( impl->buffer_offset = 0; } - *still_pending_size = BIO_pending(impl->from_ssl); + pending = (int)BIO_pending(impl->from_ssl); + GPR_ASSERT(pending >= 0); + *still_pending_size = (size_t)pending; if (*still_pending_size == 0) return TSI_OK; + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - *protected_output_frames_size); + (int)*protected_output_frames_size); if (read_from_ssl <= 0) { gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); return TSI_INTERNAL_ERROR; } - *protected_output_frames_size = read_from_ssl; - *still_pending_size = BIO_pending(impl->from_ssl); + *protected_output_frames_size = (size_t)read_from_ssl; + pending = (int)BIO_pending(impl->from_ssl); + GPR_ASSERT(pending >= 0); + *still_pending_size = (size_t)pending; return TSI_OK; } static tsi_result ssl_protector_unprotect( - tsi_frame_protector* self, const unsigned char* protected_frames_bytes, - size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size) { + tsi_frame_protector *self, const unsigned char *protected_frames_bytes, + size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size) { tsi_result result = TSI_OK; int written_into_ssl = 0; size_t output_bytes_size = *unprotected_bytes_size; size_t output_bytes_offset = 0; - tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; + tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; /* First, try to read remaining data from ssl. */ result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); @@ -740,14 +769,15 @@ static tsi_result ssl_protector_unprotect( *unprotected_bytes_size = output_bytes_size - output_bytes_offset; /* Then, try to write some data to ssl. */ + GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX); written_into_ssl = BIO_write(impl->into_ssl, protected_frames_bytes, - *protected_frames_bytes_size); + (int)*protected_frames_bytes_size); if (written_into_ssl < 0) { gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d", written_into_ssl); return TSI_INTERNAL_ERROR; } - *protected_frames_bytes_size = written_into_ssl; + *protected_frames_bytes_size = (size_t)written_into_ssl; /* Now try to read some data again. */ result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); @@ -758,8 +788,8 @@ static tsi_result ssl_protector_unprotect( return result; } -static void ssl_protector_destroy(tsi_frame_protector* self) { - tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; +static void ssl_protector_destroy(tsi_frame_protector *self) { + tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; if (impl->buffer != NULL) free(impl->buffer); if (impl->ssl != NULL) SSL_free(impl->ssl); free(self); @@ -772,16 +802,17 @@ static const tsi_frame_protector_vtable frame_protector_vtable = { /* --- tsi_handshaker methods implementation. ---*/ -static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, - unsigned char* bytes, - size_t* bytes_size) { - tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; +static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, + unsigned char *bytes, + size_t *bytes_size) { + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; int bytes_read_from_ssl = 0; if (bytes == NULL || bytes_size == NULL || *bytes_size == 0 || *bytes_size > INT_MAX) { return TSI_INVALID_ARGUMENT; } - bytes_read_from_ssl = BIO_read(impl->from_ssl, bytes, *bytes_size); + GPR_ASSERT(*bytes_size <= INT_MAX); + bytes_read_from_ssl = BIO_read(impl->from_ssl, bytes, (int)*bytes_size); if (bytes_read_from_ssl < 0) { *bytes_size = 0; if (!BIO_should_retry(impl->from_ssl)) { @@ -795,8 +826,8 @@ static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, return BIO_pending(impl->from_ssl) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA; } -static tsi_result ssl_handshaker_get_result(tsi_handshaker* self) { - tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; +static tsi_result ssl_handshaker_get_result(tsi_handshaker *self) { + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; if ((impl->result == TSI_HANDSHAKE_IN_PROGRESS) && SSL_is_init_finished(impl->ssl)) { impl->result = TSI_OK; @@ -805,19 +836,21 @@ static tsi_result ssl_handshaker_get_result(tsi_handshaker* self) { } static tsi_result ssl_handshaker_process_bytes_from_peer( - tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) { - tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; + tsi_handshaker *self, const unsigned char *bytes, size_t *bytes_size) { + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; int bytes_written_into_ssl_size = 0; if (bytes == NULL || bytes_size == 0 || *bytes_size > INT_MAX) { return TSI_INVALID_ARGUMENT; } - bytes_written_into_ssl_size = BIO_write(impl->into_ssl, bytes, *bytes_size); + GPR_ASSERT(*bytes_size <= INT_MAX); + bytes_written_into_ssl_size = + BIO_write(impl->into_ssl, bytes, (int)*bytes_size); if (bytes_written_into_ssl_size < 0) { gpr_log(GPR_ERROR, "Could not write to memory BIO."); impl->result = TSI_INTERNAL_ERROR; return impl->result; } - *bytes_size = bytes_written_into_ssl_size; + *bytes_size = (size_t)bytes_written_into_ssl_size; if (!tsi_handshaker_is_in_progress(self)) { impl->result = TSI_OK; @@ -848,13 +881,13 @@ static tsi_result ssl_handshaker_process_bytes_from_peer( } } -static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self, - tsi_peer* peer) { +static tsi_result ssl_handshaker_extract_peer(tsi_handshaker *self, + tsi_peer *peer) { tsi_result result = TSI_OK; - const unsigned char* alpn_selected = NULL; + const unsigned char *alpn_selected = NULL; unsigned int alpn_selected_len; - tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; - X509* peer_cert = SSL_get_peer_certificate(impl->ssl); + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; + X509 *peer_cert = SSL_get_peer_certificate(impl->ssl); if (peer_cert != NULL) { result = peer_from_x509(peer_cert, 1, peer); X509_free(peer_cert); @@ -870,14 +903,14 @@ static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self, } if (alpn_selected != NULL) { size_t i; - tsi_peer_property* new_properties = + tsi_peer_property *new_properties = calloc(1, sizeof(tsi_peer_property) * (peer->property_count + 1)); if (new_properties == NULL) return TSI_OUT_OF_RESOURCES; for (i = 0; i < peer->property_count; i++) { new_properties[i] = peer->properties[i]; } result = tsi_construct_string_peer_property( - TSI_SSL_ALPN_SELECTED_PROTOCOL, (const char*)alpn_selected, + TSI_SSL_ALPN_SELECTED_PROTOCOL, (const char *)alpn_selected, alpn_selected_len, &new_properties[peer->property_count]); if (result != TSI_OK) { free(new_properties); @@ -891,12 +924,12 @@ static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self, } static tsi_result ssl_handshaker_create_frame_protector( - tsi_handshaker* self, size_t* max_output_protected_frame_size, - tsi_frame_protector** protector) { + tsi_handshaker *self, size_t *max_output_protected_frame_size, + tsi_frame_protector **protector) { size_t actual_max_output_protected_frame_size = TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; - tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; - tsi_ssl_frame_protector* protector_impl = + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; + tsi_ssl_frame_protector *protector_impl = calloc(1, sizeof(tsi_ssl_frame_protector)); if (protector_impl == NULL) { return TSI_OUT_OF_RESOURCES; @@ -936,42 +969,40 @@ static tsi_result ssl_handshaker_create_frame_protector( return TSI_OK; } -static void ssl_handshaker_destroy(tsi_handshaker* self) { - tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; +static void ssl_handshaker_destroy(tsi_handshaker *self) { + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; SSL_free(impl->ssl); /* The BIO objects are owned by ssl */ free(impl); } static const tsi_handshaker_vtable handshaker_vtable = { ssl_handshaker_get_bytes_to_send_to_peer, - ssl_handshaker_process_bytes_from_peer, - ssl_handshaker_get_result, - ssl_handshaker_extract_peer, - ssl_handshaker_create_frame_protector, + ssl_handshaker_process_bytes_from_peer, ssl_handshaker_get_result, + ssl_handshaker_extract_peer, ssl_handshaker_create_frame_protector, ssl_handshaker_destroy, }; /* --- tsi_ssl_handshaker_factory common methods. --- */ tsi_result tsi_ssl_handshaker_factory_create_handshaker( - tsi_ssl_handshaker_factory* self, const char* server_name_indication, - tsi_handshaker** handshaker) { + tsi_ssl_handshaker_factory *self, const char *server_name_indication, + tsi_handshaker **handshaker) { if (self == NULL || handshaker == NULL) return TSI_INVALID_ARGUMENT; return self->create_handshaker(self, server_name_indication, handshaker); } -void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory* self) { +void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory *self) { if (self == NULL) return; self->destroy(self); } -static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client, - const char* server_name_indication, - tsi_handshaker** handshaker) { - SSL* ssl = SSL_new(ctx); - BIO* into_ssl = NULL; - BIO* from_ssl = NULL; - tsi_ssl_handshaker* impl = NULL; +static tsi_result create_tsi_ssl_handshaker(SSL_CTX *ctx, int is_client, + const char *server_name_indication, + tsi_handshaker **handshaker) { + SSL *ssl = SSL_new(ctx); + BIO *into_ssl = NULL; + BIO *from_ssl = NULL; + tsi_ssl_handshaker *impl = NULL; *handshaker = NULL; if (ctx == NULL) { gpr_log(GPR_ERROR, "SSL Context is null. Should never happen."); @@ -1030,16 +1061,16 @@ static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client, return TSI_OK; } -static int select_protocol_list(const unsigned char** out, - unsigned char* outlen, - const unsigned char* client_list, - unsigned int client_list_len, - const unsigned char* server_list, - unsigned int server_list_len) { - const unsigned char* client_current = client_list; +static int select_protocol_list(const unsigned char **out, + unsigned char *outlen, + const unsigned char *client_list, + size_t client_list_len, + const unsigned char *server_list, + size_t server_list_len) { + const unsigned char *client_current = client_list; while ((unsigned int)(client_current - client_list) < client_list_len) { unsigned char client_current_len = *(client_current++); - const unsigned char* server_current = server_list; + const unsigned char *server_current = server_list; while ((server_current >= server_list) && (gpr_uintptr)(server_current - server_list) < server_list_len) { unsigned char server_current_len = *(server_current++); @@ -1059,31 +1090,31 @@ static int select_protocol_list(const unsigned char** out, /* --- tsi_ssl__client_handshaker_factory methods implementation. --- */ static tsi_result ssl_client_handshaker_factory_create_handshaker( - tsi_ssl_handshaker_factory* self, const char* server_name_indication, - tsi_handshaker** handshaker) { - tsi_ssl_client_handshaker_factory* impl = - (tsi_ssl_client_handshaker_factory*)self; + tsi_ssl_handshaker_factory *self, const char *server_name_indication, + tsi_handshaker **handshaker) { + tsi_ssl_client_handshaker_factory *impl = + (tsi_ssl_client_handshaker_factory *)self; return create_tsi_ssl_handshaker(impl->ssl_context, 1, server_name_indication, handshaker); } static void ssl_client_handshaker_factory_destroy( - tsi_ssl_handshaker_factory* self) { - tsi_ssl_client_handshaker_factory* impl = - (tsi_ssl_client_handshaker_factory*)self; + tsi_ssl_handshaker_factory *self) { + tsi_ssl_client_handshaker_factory *impl = + (tsi_ssl_client_handshaker_factory *)self; if (impl->ssl_context != NULL) SSL_CTX_free(impl->ssl_context); if (impl->alpn_protocol_list != NULL) free(impl->alpn_protocol_list); free(impl); } -static int client_handshaker_factory_npn_callback(SSL* ssl, unsigned char** out, - unsigned char* outlen, - const unsigned char* in, +static int client_handshaker_factory_npn_callback(SSL *ssl, unsigned char **out, + unsigned char *outlen, + const unsigned char *in, unsigned int inlen, - void* arg) { - tsi_ssl_client_handshaker_factory* factory = - (tsi_ssl_client_handshaker_factory*)arg; - return select_protocol_list((const unsigned char**)out, outlen, + void *arg) { + tsi_ssl_client_handshaker_factory *factory = + (tsi_ssl_client_handshaker_factory *)arg; + return select_protocol_list((const unsigned char **)out, outlen, factory->alpn_protocol_list, factory->alpn_protocol_list_length, in, inlen); } @@ -1091,10 +1122,10 @@ static int client_handshaker_factory_npn_callback(SSL* ssl, unsigned char** out, /* --- tsi_ssl_server_handshaker_factory methods implementation. --- */ static tsi_result ssl_server_handshaker_factory_create_handshaker( - tsi_ssl_handshaker_factory* self, const char* server_name_indication, - tsi_handshaker** handshaker) { - tsi_ssl_server_handshaker_factory* impl = - (tsi_ssl_server_handshaker_factory*)self; + tsi_ssl_handshaker_factory *self, const char *server_name_indication, + tsi_handshaker **handshaker) { + tsi_ssl_server_handshaker_factory *impl = + (tsi_ssl_server_handshaker_factory *)self; if (impl->ssl_context_count == 0 || server_name_indication != NULL) { return TSI_INVALID_ARGUMENT; } @@ -1104,9 +1135,9 @@ static tsi_result ssl_server_handshaker_factory_create_handshaker( } static void ssl_server_handshaker_factory_destroy( - tsi_ssl_handshaker_factory* self) { - tsi_ssl_server_handshaker_factory* impl = - (tsi_ssl_server_handshaker_factory*)self; + tsi_ssl_handshaker_factory *self) { + tsi_ssl_server_handshaker_factory *impl = + (tsi_ssl_server_handshaker_factory *)self; size_t i; for (i = 0; i < impl->ssl_context_count; i++) { if (impl->ssl_contexts[i] != NULL) { @@ -1122,10 +1153,10 @@ static void ssl_server_handshaker_factory_destroy( free(impl); } -static int does_entry_match_name(const char* entry, size_t entry_length, - const char* name) { - const char* dot; - const char* name_subdomain = NULL; +static int does_entry_match_name(const char *entry, size_t entry_length, + const char *name) { + const char *dot; + const char *name_subdomain = NULL; size_t name_length = strlen(name); size_t name_subdomain_length; if (entry_length == 0) return 0; @@ -1170,12 +1201,12 @@ static int does_entry_match_name(const char* entry, size_t entry_length, strncmp(entry, name_subdomain, entry_length) == 0); } -static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap, - void* arg) { - tsi_ssl_server_handshaker_factory* impl = - (tsi_ssl_server_handshaker_factory*)arg; +static int ssl_server_handshaker_factory_servername_callback(SSL *ssl, int *ap, + void *arg) { + tsi_ssl_server_handshaker_factory *impl = + (tsi_ssl_server_handshaker_factory *)arg; size_t i = 0; - const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (servername == NULL || strlen(servername) == 0) { return SSL_TLSEXT_ERR_NOACK; } @@ -1193,10 +1224,10 @@ static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap, #if TSI_OPENSSL_ALPN_SUPPORT static int server_handshaker_factory_alpn_callback( - SSL* ssl, const unsigned char** out, unsigned char* outlen, - const unsigned char* in, unsigned int inlen, void* arg) { - tsi_ssl_server_handshaker_factory* factory = - (tsi_ssl_server_handshaker_factory*)arg; + SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) { + tsi_ssl_server_handshaker_factory *factory = + (tsi_ssl_server_handshaker_factory *)arg; return select_protocol_list(out, outlen, in, inlen, factory->alpn_protocol_list, factory->alpn_protocol_list_length); @@ -1204,25 +1235,26 @@ static int server_handshaker_factory_alpn_callback( #endif /* TSI_OPENSSL_ALPN_SUPPORT */ static int server_handshaker_factory_npn_advertised_callback( - SSL* ssl, const unsigned char** out, unsigned int* outlen, void* arg) { - tsi_ssl_server_handshaker_factory* factory = - (tsi_ssl_server_handshaker_factory*)arg; + SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) { + tsi_ssl_server_handshaker_factory *factory = + (tsi_ssl_server_handshaker_factory *)arg; *out = factory->alpn_protocol_list; - *outlen = factory->alpn_protocol_list_length; + GPR_ASSERT(factory->alpn_protocol_list_length <= UINT_MAX); + *outlen = (unsigned int)factory->alpn_protocol_list_length; return SSL_TLSEXT_ERR_OK; } /* --- tsi_ssl_handshaker_factory constructors. --- */ tsi_result tsi_create_ssl_client_handshaker_factory( - const unsigned char* pem_private_key, size_t pem_private_key_size, - const unsigned char* pem_cert_chain, size_t pem_cert_chain_size, - const unsigned char* pem_root_certs, size_t pem_root_certs_size, - const char* cipher_list, const unsigned char** alpn_protocols, - const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, - tsi_ssl_handshaker_factory** factory) { - SSL_CTX* ssl_context = NULL; - tsi_ssl_client_handshaker_factory* impl = NULL; + const unsigned char *pem_private_key, size_t pem_private_key_size, + const unsigned char *pem_cert_chain, size_t pem_cert_chain_size, + const unsigned char *pem_root_certs, size_t pem_root_certs_size, + const char *cipher_list, const unsigned char **alpn_protocols, + const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, + tsi_ssl_handshaker_factory **factory) { + SSL_CTX *ssl_context = NULL; + tsi_ssl_client_handshaker_factory *impl = NULL; tsi_result result = TSI_OK; gpr_once_init(&init_openssl_once, init_openssl); @@ -1266,8 +1298,10 @@ tsi_result tsi_create_ssl_client_handshaker_factory( break; } #if TSI_OPENSSL_ALPN_SUPPORT - if (SSL_CTX_set_alpn_protos(ssl_context, impl->alpn_protocol_list, - impl->alpn_protocol_list_length)) { + GPR_ASSERT(impl->alpn_protocol_list_length < UINT_MAX); + if (SSL_CTX_set_alpn_protos( + ssl_context, impl->alpn_protocol_list, + (unsigned int)impl->alpn_protocol_list_length)) { gpr_log(GPR_ERROR, "Could not set alpn protocol list to context."); result = TSI_INVALID_ARGUMENT; break; @@ -1292,15 +1326,15 @@ tsi_result tsi_create_ssl_client_handshaker_factory( } tsi_result tsi_create_ssl_server_handshaker_factory( - const unsigned char** pem_private_keys, - const size_t* pem_private_keys_sizes, const unsigned char** pem_cert_chains, - const size_t* pem_cert_chains_sizes, size_t key_cert_pair_count, - const unsigned char* pem_client_root_certs, + const unsigned char **pem_private_keys, + const size_t *pem_private_keys_sizes, const unsigned char **pem_cert_chains, + const size_t *pem_cert_chains_sizes, size_t key_cert_pair_count, + const unsigned char *pem_client_root_certs, size_t pem_client_root_certs_size, int force_client_auth, - const char* cipher_list, const unsigned char** alpn_protocols, - const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, - tsi_ssl_handshaker_factory** factory) { - tsi_ssl_server_handshaker_factory* impl = NULL; + const char *cipher_list, const unsigned char **alpn_protocols, + const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, + tsi_ssl_handshaker_factory **factory) { + tsi_ssl_server_handshaker_factory *impl = NULL; tsi_result result = TSI_OK; size_t i = 0; @@ -1318,7 +1352,7 @@ tsi_result tsi_create_ssl_server_handshaker_factory( impl->base.create_handshaker = ssl_server_handshaker_factory_create_handshaker; impl->base.destroy = ssl_server_handshaker_factory_destroy; - impl->ssl_contexts = calloc(key_cert_pair_count, sizeof(SSL_CTX*)); + impl->ssl_contexts = calloc(key_cert_pair_count, sizeof(SSL_CTX *)); impl->ssl_context_x509_subject_names = calloc(key_cert_pair_count, sizeof(tsi_peer)); if (impl->ssl_contexts == NULL || @@ -1353,7 +1387,7 @@ tsi_result tsi_create_ssl_server_handshaker_factory( if (pem_client_root_certs != NULL) { int flags = SSL_VERIFY_PEER; - STACK_OF(X509_NAME)* root_names = NULL; + STACK_OF(X509_NAME) *root_names = NULL; result = ssl_ctx_load_verification_certs( impl->ssl_contexts[i], pem_client_root_certs, pem_client_root_certs_size, &root_names); @@ -1396,17 +1430,17 @@ tsi_result tsi_create_ssl_server_handshaker_factory( /* --- tsi_ssl utils. --- */ -int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { +int tsi_ssl_peer_matches_name(const tsi_peer *peer, const char *name) { size_t i = 0; size_t san_count = 0; - const tsi_peer_property* cn_property = NULL; + const tsi_peer_property *cn_property = NULL; /* For now reject what looks like an IP address. */ if (looks_like_ip_address(name)) return 0; /* Check the SAN first. */ for (i = 0; i < peer->property_count; i++) { - const tsi_peer_property* property = &peer->properties[i]; + const tsi_peer_property *property = &peer->properties[i]; if (property->name == NULL) continue; if (strcmp(property->name, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h index cdf4f294be..51c0003a85 100644 --- a/src/core/tsi/ssl_transport_security.h +++ b/src/core/tsi/ssl_transport_security.h @@ -85,12 +85,12 @@ typedef struct tsi_ssl_handshaker_factory tsi_ssl_handshaker_factory; - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case where a parameter is invalid. */ tsi_result tsi_create_ssl_client_handshaker_factory( - const unsigned char* pem_private_key, size_t pem_private_key_size, - const unsigned char* pem_cert_chain, size_t pem_cert_chain_size, - const unsigned char* pem_root_certs, size_t pem_root_certs_size, - const char* cipher_suites, const unsigned char** alpn_protocols, - const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, - tsi_ssl_handshaker_factory** factory); + const unsigned char *pem_private_key, size_t pem_private_key_size, + const unsigned char *pem_cert_chain, size_t pem_cert_chain_size, + const unsigned char *pem_root_certs, size_t pem_root_certs_size, + const char *cipher_suites, const unsigned char **alpn_protocols, + const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, + tsi_ssl_handshaker_factory **factory); /* Creates a server handshaker factory. - version indicates which version of the specification to use. @@ -131,14 +131,14 @@ tsi_result tsi_create_ssl_client_handshaker_factory( - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case where a parameter is invalid. */ tsi_result tsi_create_ssl_server_handshaker_factory( - const unsigned char** pem_private_keys, - const size_t* pem_private_keys_sizes, const unsigned char** pem_cert_chains, - const size_t* pem_cert_chains_sizes, size_t key_cert_pair_count, - const unsigned char* pem_client_root_certs, + const unsigned char **pem_private_keys, + const size_t *pem_private_keys_sizes, const unsigned char **pem_cert_chains, + const size_t *pem_cert_chains_sizes, size_t key_cert_pair_count, + const unsigned char *pem_client_root_certs, size_t pem_client_root_certs_size, int force_client_auth, - const char* cipher_suites, const unsigned char** alpn_protocols, - const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, - tsi_ssl_handshaker_factory** factory); + const char *cipher_suites, const unsigned char **alpn_protocols, + const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, + tsi_ssl_handshaker_factory **factory); /* Creates a handshaker. - self is the factory from which the handshaker will be created. @@ -151,12 +151,12 @@ tsi_result tsi_create_ssl_server_handshaker_factory( - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case where a parameter is invalid. */ tsi_result tsi_ssl_handshaker_factory_create_handshaker( - tsi_ssl_handshaker_factory* self, const char* server_name_indication, - tsi_handshaker** handshaker); + tsi_ssl_handshaker_factory *self, const char *server_name_indication, + tsi_handshaker **handshaker); /* Destroys the handshaker factory. WARNING: it is unsafe to destroy a factory while handshakers created with this factory are still in use. */ -void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory* self); +void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory *self); /* Util that checks that an ssl peer matches a specific name. Still TODO(jboeuf): @@ -164,7 +164,7 @@ void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory* self); - handle %encoded chars. - handle public suffix wildchar more strictly (e.g. *.co.uk) - handle IP addresses in SAN. */ -int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name); +int tsi_ssl_peer_matches_name(const tsi_peer *peer, const char *name); #ifdef __cplusplus } diff --git a/src/core/tsi/transport_security.c b/src/core/tsi/transport_security.c index ec02a478ba..c39e584496 100644 --- a/src/core/tsi/transport_security.c +++ b/src/core/tsi/transport_security.c @@ -42,8 +42,8 @@ int tsi_tracing_enabled = 0; /* --- Utils. --- */ -char* tsi_strdup(const char* src) { - char* dst; +char *tsi_strdup(const char *src) { + char *dst; size_t len; if (!src) return NULL; len = strlen(src) + 1; @@ -55,7 +55,7 @@ char* tsi_strdup(const char* src) { /* --- tsi_result common implementation. --- */ -const char* tsi_result_to_string(tsi_result result) { +const char *tsi_result_to_string(tsi_result result) { switch (result) { case TSI_OK: return "TSI_OK"; @@ -92,11 +92,11 @@ const char* tsi_result_to_string(tsi_result result) { Calls specific implementation after state/input validation. */ -tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, - const unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size, - unsigned char* protected_output_frames, - size_t* protected_output_frames_size) { +tsi_result tsi_frame_protector_protect(tsi_frame_protector *self, + const unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size) { if (self == NULL || unprotected_bytes == NULL || unprotected_bytes_size == NULL || protected_output_frames == NULL || protected_output_frames_size == NULL) { @@ -108,8 +108,8 @@ tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, } tsi_result tsi_frame_protector_protect_flush( - tsi_frame_protector* self, unsigned char* protected_output_frames, - size_t* protected_output_frames_size, size_t* still_pending_size) { + tsi_frame_protector *self, unsigned char *protected_output_frames, + size_t *protected_output_frames_size, size_t *still_pending_size) { if (self == NULL || protected_output_frames == NULL || protected_output_frames == NULL || still_pending_size == NULL) { return TSI_INVALID_ARGUMENT; @@ -120,9 +120,9 @@ tsi_result tsi_frame_protector_protect_flush( } tsi_result tsi_frame_protector_unprotect( - tsi_frame_protector* self, const unsigned char* protected_frames_bytes, - size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size) { + tsi_frame_protector *self, const unsigned char *protected_frames_bytes, + size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size) { if (self == NULL || protected_frames_bytes == NULL || protected_frames_bytes_size == NULL || unprotected_bytes == NULL || unprotected_bytes_size == NULL) { @@ -133,7 +133,7 @@ tsi_result tsi_frame_protector_unprotect( unprotected_bytes_size); } -void tsi_frame_protector_destroy(tsi_frame_protector* self) { +void tsi_frame_protector_destroy(tsi_frame_protector *self) { if (self == NULL) return; self->vtable->destroy(self); } @@ -142,29 +142,29 @@ void tsi_frame_protector_destroy(tsi_frame_protector* self) { Calls specific implementation after state/input validation. */ -tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, - unsigned char* bytes, - size_t* bytes_size) { +tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, + unsigned char *bytes, + size_t *bytes_size) { if (self == NULL) return TSI_INVALID_ARGUMENT; if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size); } -tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self, - const unsigned char* bytes, - size_t* bytes_size) { +tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self, + const unsigned char *bytes, + size_t *bytes_size) { if (self == NULL) return TSI_INVALID_ARGUMENT; if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; return self->vtable->process_bytes_from_peer(self, bytes, bytes_size); } -tsi_result tsi_handshaker_get_result(tsi_handshaker* self) { +tsi_result tsi_handshaker_get_result(tsi_handshaker *self) { if (self == NULL) return TSI_INVALID_ARGUMENT; if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; return self->vtable->get_result(self); } -tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer) { +tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer) { if (self == NULL || peer == NULL) return TSI_INVALID_ARGUMENT; memset(peer, 0, sizeof(tsi_peer)); if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; @@ -175,8 +175,8 @@ tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer) { } tsi_result tsi_handshaker_create_frame_protector( - tsi_handshaker* self, size_t* max_protected_frame_size, - tsi_frame_protector** protector) { + tsi_handshaker *self, size_t *max_protected_frame_size, + tsi_frame_protector **protector) { tsi_result result; if (self == NULL || protector == NULL) return TSI_INVALID_ARGUMENT; if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; @@ -191,7 +191,7 @@ tsi_result tsi_handshaker_create_frame_protector( return result; } -void tsi_handshaker_destroy(tsi_handshaker* self) { +void tsi_handshaker_destroy(tsi_handshaker *self) { if (self == NULL) return; self->vtable->destroy(self); } @@ -204,7 +204,7 @@ tsi_peer_property tsi_init_peer_property(void) { return property; } -static void tsi_peer_destroy_list_property(tsi_peer_property* children, +static void tsi_peer_destroy_list_property(tsi_peer_property *children, size_t child_count) { size_t i; for (i = 0; i < child_count; i++) { @@ -213,7 +213,7 @@ static void tsi_peer_destroy_list_property(tsi_peer_property* children, free(children); } -void tsi_peer_property_destruct(tsi_peer_property* property) { +void tsi_peer_property_destruct(tsi_peer_property *property) { if (property->name != NULL) { free(property->name); } @@ -223,7 +223,7 @@ void tsi_peer_property_destruct(tsi_peer_property* property) { *property = tsi_init_peer_property(); /* Reset everything to 0. */ } -void tsi_peer_destruct(tsi_peer* self) { +void tsi_peer_destruct(tsi_peer *self) { if (self == NULL) return; if (self->properties != NULL) { tsi_peer_destroy_list_property(self->properties, self->property_count); @@ -233,7 +233,7 @@ void tsi_peer_destruct(tsi_peer* self) { } tsi_result tsi_construct_allocated_string_peer_property( - const char* name, size_t value_length, tsi_peer_property* property) { + const char *name, size_t value_length, tsi_peer_property *property) { *property = tsi_init_peer_property(); if (name != NULL) { property->name = tsi_strdup(name); @@ -251,15 +251,15 @@ tsi_result tsi_construct_allocated_string_peer_property( } tsi_result tsi_construct_string_peer_property_from_cstring( - const char* name, const char* value, tsi_peer_property* property) { + const char *name, const char *value, tsi_peer_property *property) { return tsi_construct_string_peer_property(name, value, strlen(value), property); } -tsi_result tsi_construct_string_peer_property(const char* name, - const char* value, +tsi_result tsi_construct_string_peer_property(const char *name, + const char *value, size_t value_length, - tsi_peer_property* property) { + tsi_peer_property *property) { tsi_result result = tsi_construct_allocated_string_peer_property( name, value_length, property); if (result != TSI_OK) return result; @@ -269,7 +269,7 @@ tsi_result tsi_construct_string_peer_property(const char* name, return TSI_OK; } -tsi_result tsi_construct_peer(size_t property_count, tsi_peer* peer) { +tsi_result tsi_construct_peer(size_t property_count, tsi_peer *peer) { memset(peer, 0, sizeof(tsi_peer)); if (property_count > 0) { peer->properties = calloc(property_count, sizeof(tsi_peer_property)); diff --git a/src/core/tsi/transport_security.h b/src/core/tsi/transport_security.h index 34283f2f9c..4077737473 100644 --- a/src/core/tsi/transport_security.h +++ b/src/core/tsi/transport_security.h @@ -45,64 +45,64 @@ extern int tsi_tracing_enabled; /* Base for tsi_frame_protector implementations. See transport_security_interface.h for documentation. */ typedef struct { - tsi_result (*protect)(tsi_frame_protector* self, - const unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size, - unsigned char* protected_output_frames, - size_t* protected_output_frames_size); - tsi_result (*protect_flush)(tsi_frame_protector* self, - unsigned char* protected_output_frames, - size_t* protected_output_frames_size, - size_t* still_pending_size); - tsi_result (*unprotect)(tsi_frame_protector* self, - const unsigned char* protected_frames_bytes, - size_t* protected_frames_bytes_size, - unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size); - void (*destroy)(tsi_frame_protector* self); + tsi_result (*protect)(tsi_frame_protector *self, + const unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size); + tsi_result (*protect_flush)(tsi_frame_protector *self, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size, + size_t *still_pending_size); + tsi_result (*unprotect)(tsi_frame_protector *self, + const unsigned char *protected_frames_bytes, + size_t *protected_frames_bytes_size, + unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size); + void (*destroy)(tsi_frame_protector *self); } tsi_frame_protector_vtable; struct tsi_frame_protector { - const tsi_frame_protector_vtable* vtable; + const tsi_frame_protector_vtable *vtable; }; /* Base for tsi_handshaker implementations. See transport_security_interface.h for documentation. */ typedef struct { - tsi_result (*get_bytes_to_send_to_peer)(tsi_handshaker* self, - unsigned char* bytes, - size_t* bytes_size); - tsi_result (*process_bytes_from_peer)(tsi_handshaker* self, - const unsigned char* bytes, - size_t* bytes_size); - tsi_result (*get_result)(tsi_handshaker* self); - tsi_result (*extract_peer)(tsi_handshaker* self, tsi_peer* peer); - tsi_result (*create_frame_protector)(tsi_handshaker* self, - size_t* max_protected_frame_size, - tsi_frame_protector** protector); - void (*destroy)(tsi_handshaker* self); + tsi_result (*get_bytes_to_send_to_peer)(tsi_handshaker *self, + unsigned char *bytes, + size_t *bytes_size); + tsi_result (*process_bytes_from_peer)(tsi_handshaker *self, + const unsigned char *bytes, + size_t *bytes_size); + tsi_result (*get_result)(tsi_handshaker *self); + tsi_result (*extract_peer)(tsi_handshaker *self, tsi_peer *peer); + tsi_result (*create_frame_protector)(tsi_handshaker *self, + size_t *max_protected_frame_size, + tsi_frame_protector **protector); + void (*destroy)(tsi_handshaker *self); } tsi_handshaker_vtable; struct tsi_handshaker { - const tsi_handshaker_vtable* vtable; + const tsi_handshaker_vtable *vtable; int frame_protector_created; }; /* Peer and property construction/destruction functions. */ -tsi_result tsi_construct_peer(size_t property_count, tsi_peer* peer); +tsi_result tsi_construct_peer(size_t property_count, tsi_peer *peer); tsi_peer_property tsi_init_peer_property(void); -void tsi_peer_property_destruct(tsi_peer_property* property); -tsi_result tsi_construct_string_peer_property(const char* name, - const char* value, +void tsi_peer_property_destruct(tsi_peer_property *property); +tsi_result tsi_construct_string_peer_property(const char *name, + const char *value, size_t value_length, - tsi_peer_property* property); + tsi_peer_property *property); tsi_result tsi_construct_allocated_string_peer_property( - const char* name, size_t value_length, tsi_peer_property* property); + const char *name, size_t value_length, tsi_peer_property *property); tsi_result tsi_construct_string_peer_property_from_cstring( - const char* name, const char* value, tsi_peer_property* property); + const char *name, const char *value, tsi_peer_property *property); /* Utils. */ -char* tsi_strdup(const char* src); /* Sadly, no strdup in C89. */ +char *tsi_strdup(const char *src); /* Sadly, no strdup in C89. */ #ifdef __cplusplus } diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h index 03a51683a2..69ee17ae91 100644 --- a/src/core/tsi/transport_security_interface.h +++ b/src/core/tsi/transport_security_interface.h @@ -59,7 +59,7 @@ typedef enum { TSI_OUT_OF_RESOURCES = 12 } tsi_result; -const char* tsi_result_to_string(tsi_result result); +const char *tsi_result_to_string(tsi_result result); /* --- tsi tracing --- */ @@ -126,11 +126,11 @@ typedef struct tsi_frame_protector tsi_frame_protector; if (result != TSI_OK) HandleError(result); ------------------------------------------------------------------------ */ -tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, - const unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size, - unsigned char* protected_output_frames, - size_t* protected_output_frames_size); +tsi_result tsi_frame_protector_protect(tsi_frame_protector *self, + const unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size); /* Indicates that we need to flush the bytes buffered in the protector and get the resulting frame. @@ -141,8 +141,8 @@ tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, - still_pending_bytes is an output parameter indicating the number of bytes that still need to be flushed from the protector.*/ tsi_result tsi_frame_protector_protect_flush( - tsi_frame_protector* self, unsigned char* protected_output_frames, - size_t* protected_output_frames_size, size_t* still_pending_size); + tsi_frame_protector *self, unsigned char *protected_output_frames, + size_t *protected_output_frames_size, size_t *still_pending_size); /* Outputs unprotected bytes. - protected_frames_bytes is an input only parameter and points to the @@ -167,12 +167,12 @@ tsi_result tsi_frame_protector_protect_flush( needs to be read before new protected data can be processed in which case protected_frames_size will be set to 0. */ tsi_result tsi_frame_protector_unprotect( - tsi_frame_protector* self, const unsigned char* protected_frames_bytes, - size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, - size_t* unprotected_bytes_size); + tsi_frame_protector *self, const unsigned char *protected_frames_bytes, + size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size); /* Destroys the tsi_frame_protector object. */ -void tsi_frame_protector_destroy(tsi_frame_protector* self); +void tsi_frame_protector_destroy(tsi_frame_protector *self); /* --- tsi_peer objects --- @@ -184,20 +184,20 @@ void tsi_frame_protector_destroy(tsi_frame_protector* self); /* Property values may contain NULL characters just like C++ strings. The length field gives the length of the string. */ typedef struct tsi_peer_property { - char* name; + char *name; struct { - char* data; + char *data; size_t length; } value; } tsi_peer_property; typedef struct { - tsi_peer_property* properties; + tsi_peer_property *properties; size_t property_count; } tsi_peer; /* Destructs the tsi_peer object. */ -void tsi_peer_destruct(tsi_peer* self); +void tsi_peer_destruct(tsi_peer *self); /* --- tsi_handshaker objects ---- @@ -279,9 +279,9 @@ typedef struct tsi_handshaker tsi_handshaker; needs to be called again to get all the bytes to send to the peer (there was more data to write than the specified bytes_size). In case of a fatal error in the handshake, another specific error code is returned. */ -tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, - unsigned char* bytes, - size_t* bytes_size); +tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, + unsigned char *bytes, + size_t *bytes_size); /* Processes bytes received from the peer. - bytes is the buffer containing the data. @@ -292,16 +292,16 @@ tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, needs to be called again to complete the data needed for processing. In case of a fatal error in the handshake, another specific error code is returned. */ -tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self, - const unsigned char* bytes, - size_t* bytes_size); +tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self, + const unsigned char *bytes, + size_t *bytes_size); /* Gets the result of the handshaker. Returns TSI_OK if the hanshake completed successfully and there has been no errors. Returns TSI_HANDSHAKE_IN_PROGRESS if the handshaker is not done yet but no error has been encountered so far. Otherwise the handshaker failed with the returned error. */ -tsi_result tsi_handshaker_get_result(tsi_handshaker* self); +tsi_result tsi_handshaker_get_result(tsi_handshaker *self); /* Returns 1 if the handshake is in progress, 0 otherwise. */ #define tsi_handshaker_is_in_progress(h) \ @@ -311,7 +311,7 @@ tsi_result tsi_handshaker_get_result(tsi_handshaker* self); tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise assuming the handshaker is not in a fatal error state. The caller is responsible for destructing the peer. */ -tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer); +tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer); /* This method creates a tsi_frame_protector object after the handshake phase is done. After this method has been called successfully, the only method @@ -330,12 +330,12 @@ tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer); the handshaker is not in a fatal error state. The caller is responsible for destroying the protector. */ tsi_result tsi_handshaker_create_frame_protector( - tsi_handshaker* self, size_t* max_output_protected_frame_size, - tsi_frame_protector** protector); + tsi_handshaker *self, size_t *max_output_protected_frame_size, + tsi_frame_protector **protector); /* This method releases the tsi_handshaker object. After this method is called, no other method can be called on the object. */ -void tsi_handshaker_destroy(tsi_handshaker* self); +void tsi_handshaker_destroy(tsi_handshaker *self); #ifdef __cplusplus } diff --git a/src/cpp/README.md b/src/cpp/README.md index a2eb9a08c8..baeba08315 100644 --- a/src/cpp/README.md +++ b/src/cpp/README.md @@ -5,5 +5,4 @@ This directory contains source code for C++ implementation of gRPC. #Status -Alpha : Ready for early adopters - +Beta diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc index dc8e304664..c7974d655b 100644 --- a/src/cpp/client/channel.cc +++ b/src/cpp/client/channel.cc @@ -78,7 +78,6 @@ Call Channel::CreateCall(const RpcMethod& method, ClientContext* context, context->raw_deadline(), nullptr); } grpc_census_call_set_context(c_call, context->census_context()); - GRPC_TIMER_MARK(GRPC_PTAG_CPP_CALL_CREATED, c_call); context->set_call(c_call, shared_from_this()); return Call(c_call, this, cq); } @@ -87,11 +86,9 @@ void Channel::PerformOpsOnCall(CallOpSetInterface* ops, Call* call) { static const size_t MAX_OPS = 8; size_t nops = 0; grpc_op cops[MAX_OPS]; - GRPC_TIMER_BEGIN(GRPC_PTAG_CPP_PERFORM_OPS, call->call()); ops->FillOps(cops, &nops); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call->call(), cops, nops, ops, nullptr)); - GRPC_TIMER_END(GRPC_PTAG_CPP_PERFORM_OPS, call->call()); } void* Channel::RegisterMethod(const char* method) { diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index 2260f6d33e..8299ebeb8a 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -144,4 +144,65 @@ std::shared_ptr<Credentials> CompositeCredentials( return nullptr; } +void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) { + if (wrapper == nullptr) return; + MetadataCredentialsPluginWrapper* w = + reinterpret_cast<MetadataCredentialsPluginWrapper*>(wrapper); + delete w; +} + +void MetadataCredentialsPluginWrapper::GetMetadata( + void* wrapper, const char* service_url, + grpc_credentials_plugin_metadata_cb cb, void* user_data) { + GPR_ASSERT(wrapper); + MetadataCredentialsPluginWrapper* w = + reinterpret_cast<MetadataCredentialsPluginWrapper*>(wrapper); + if (!w->plugin_) { + cb(user_data, NULL, 0, GRPC_STATUS_OK, NULL); + return; + } + if (w->plugin_->IsBlocking()) { + w->thread_pool_->Add( + std::bind(&MetadataCredentialsPluginWrapper::InvokePlugin, w, + service_url, cb, user_data)); + } else { + w->InvokePlugin(service_url, cb, user_data); + } +} + +void MetadataCredentialsPluginWrapper::InvokePlugin( + const char* service_url, grpc_credentials_plugin_metadata_cb cb, + void* user_data) { + std::multimap<grpc::string, grpc::string> metadata; + Status status = plugin_->GetMetadata(service_url, &metadata); + std::vector<grpc_metadata> md; + for (auto it = metadata.begin(); it != metadata.end(); ++it) { + grpc_metadata md_entry; + md_entry.key = it->first.c_str(); + md_entry.value = it->second.data(); + md_entry.value_length = it->second.size(); + md_entry.flags = 0; + md.push_back(md_entry); + } + cb(user_data, md.empty() ? nullptr : &md[0], md.size(), + static_cast<grpc_status_code>(status.error_code()), + status.error_message().c_str()); +} + +MetadataCredentialsPluginWrapper::MetadataCredentialsPluginWrapper( + std::unique_ptr<MetadataCredentialsPlugin> plugin) + : thread_pool_(CreateDefaultThreadPool()), plugin_(std::move(plugin)) {} + +std::shared_ptr<Credentials> MetadataCredentialsFromPlugin( + std::unique_ptr<MetadataCredentialsPlugin> plugin) { + GrpcLibrary init; // To call grpc_init(). + MetadataCredentialsPluginWrapper* wrapper = + new MetadataCredentialsPluginWrapper(std::move(plugin)); + grpc_metadata_credentials_plugin c_plugin = { + MetadataCredentialsPluginWrapper::GetMetadata, + MetadataCredentialsPluginWrapper::Destroy, wrapper}; + return WrapCredentials( + grpc_metadata_credentials_create_from_plugin(c_plugin, nullptr)); +} + } // namespace grpc diff --git a/src/cpp/client/secure_credentials.h b/src/cpp/client/secure_credentials.h index 8deff856c4..d354827725 100644 --- a/src/cpp/client/secure_credentials.h +++ b/src/cpp/client/secure_credentials.h @@ -39,6 +39,8 @@ #include <grpc++/support/config.h> #include <grpc++/security/credentials.h> +#include "src/cpp/server/thread_pool_interface.h" + namespace grpc { class SecureCredentials GRPC_FINAL : public Credentials { @@ -56,6 +58,23 @@ class SecureCredentials GRPC_FINAL : public Credentials { grpc_credentials* const c_creds_; }; +class MetadataCredentialsPluginWrapper GRPC_FINAL { + public: + static void Destroy(void* wrapper); + static void GetMetadata(void* wrapper, const char* service_url, + grpc_credentials_plugin_metadata_cb cb, + void* user_data); + + explicit MetadataCredentialsPluginWrapper( + std::unique_ptr<MetadataCredentialsPlugin> plugin); + + private: + void InvokePlugin(const char* service_url, + grpc_credentials_plugin_metadata_cb cb, void* user_data); + std::unique_ptr<ThreadPoolInterface> thread_pool_; + std::unique_ptr<MetadataCredentialsPlugin> plugin_; +}; + } // namespace grpc #endif // GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H diff --git a/src/cpp/common/alarm.cc b/src/cpp/common/alarm.cc new file mode 100644 index 0000000000..bce0b174f8 --- /dev/null +++ b/src/cpp/common/alarm.cc @@ -0,0 +1,49 @@ +/* + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <grpc/grpc.h> +#include <grpc++/alarm.h> + +namespace grpc { + +Alarm::Alarm(CompletionQueue* cq, gpr_timespec deadline, void* tag) + : alarm_(grpc_alarm_create(cq->cq(), deadline, tag)) {} + +Alarm::~Alarm() { + grpc_alarm_destroy(alarm_); +} + +void Alarm::Cancel() { + grpc_alarm_cancel(alarm_); +} + +} // namespace grpc diff --git a/src/cpp/proto/proto_utils.cc b/src/cpp/proto/proto_utils.cc index be84c222a0..b1330fde7f 100644 --- a/src/cpp/proto/proto_utils.cc +++ b/src/cpp/proto/proto_utils.cc @@ -36,11 +36,14 @@ #include <grpc/grpc.h> #include <grpc/byte_buffer.h> #include <grpc/byte_buffer_reader.h> +#include <grpc/support/log.h> #include <grpc/support/slice.h> #include <grpc/support/slice_buffer.h> #include <grpc/support/port_platform.h> #include <grpc++/support/config.h> +#include "src/core/profiling/timers.h" + const int kMaxBufferLength = 8192; class GrpcBufferWriter GRPC_FINAL @@ -111,7 +114,8 @@ class GrpcBufferReader GRPC_FINAL if (backup_count_ > 0) { *data = GPR_SLICE_START_PTR(slice_) + GPR_SLICE_LENGTH(slice_) - backup_count_; - *size = backup_count_; + GPR_ASSERT(backup_count_ <= INT_MAX); + *size = (int)backup_count_; backup_count_ = 0; return true; } @@ -156,14 +160,26 @@ namespace grpc { Status SerializeProto(const grpc::protobuf::Message& msg, grpc_byte_buffer** bp) { - GrpcBufferWriter writer(bp); - return msg.SerializeToZeroCopyStream(&writer) - ? Status::OK - : Status(StatusCode::INTERNAL, "Failed to serialize message"); + GPR_TIMER_SCOPE("SerializeProto", 0); + int byte_size = msg.ByteSize(); + if (byte_size <= kMaxBufferLength) { + gpr_slice slice = gpr_slice_malloc(byte_size); + GPR_ASSERT(GPR_SLICE_END_PTR(slice) == + msg.SerializeWithCachedSizesToArray(GPR_SLICE_START_PTR(slice))); + *bp = grpc_raw_byte_buffer_create(&slice, 1); + gpr_slice_unref(slice); + return Status::OK; + } else { + GrpcBufferWriter writer(bp); + return msg.SerializeToZeroCopyStream(&writer) + ? Status::OK + : Status(StatusCode::INTERNAL, "Failed to serialize message"); + } } Status DeserializeProto(grpc_byte_buffer* buffer, grpc::protobuf::Message* msg, int max_message_size) { + GPR_TIMER_SCOPE("DeserializeProto", 0); if (!buffer) { return Status(StatusCode::INTERNAL, "No payload"); } diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc index dfa9229c98..d472667a7e 100644 --- a/src/cpp/server/secure_server_credentials.cc +++ b/src/cpp/server/secure_server_credentials.cc @@ -35,7 +35,6 @@ #include <map> #include <memory> - #include "src/cpp/common/secure_auth_context.h" #include "src/cpp/server/secure_server_credentials.h" @@ -43,7 +42,7 @@ namespace grpc { -void AuthMetadataProcessorAyncWrapper::Destroy(void *wrapper) { +void AuthMetadataProcessorAyncWrapper::Destroy(void* wrapper) { auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper); delete w; } @@ -52,7 +51,7 @@ void AuthMetadataProcessorAyncWrapper::Process( void* wrapper, grpc_auth_context* context, const grpc_metadata* md, size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) { auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper); - if (w->processor_ == nullptr) { + if (!w->processor_) { // Early exit. cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_OK, nullptr); return; @@ -68,8 +67,7 @@ void AuthMetadataProcessorAyncWrapper::Process( } void AuthMetadataProcessorAyncWrapper::InvokeProcessor( - grpc_auth_context* ctx, - const grpc_metadata* md, size_t num_md, + grpc_auth_context* ctx, const grpc_metadata* md, size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) { AuthMetadataProcessor::InputMetadata metadata; for (size_t i = 0; i < num_md; i++) { @@ -86,22 +84,26 @@ void AuthMetadataProcessorAyncWrapper::InvokeProcessor( std::vector<grpc_metadata> consumed_md; for (auto it = consumed_metadata.begin(); it != consumed_metadata.end(); ++it) { - consumed_md.push_back({it->first.c_str(), - it->second.data(), - it->second.size(), - 0, - {{nullptr, nullptr, nullptr, nullptr}}}); + grpc_metadata md_entry; + md_entry.key = it->first.c_str(); + md_entry.value = it->second.data(); + md_entry.value_length = it->second.size(); + md_entry.flags = 0; + consumed_md.push_back(md_entry); } std::vector<grpc_metadata> response_md; for (auto it = response_metadata.begin(); it != response_metadata.end(); ++it) { - response_md.push_back({it->first.c_str(), - it->second.data(), - it->second.size(), - 0, - {{nullptr, nullptr, nullptr, nullptr}}}); + grpc_metadata md_entry; + md_entry.key = it->first.c_str(); + md_entry.value = it->second.data(); + md_entry.value_length = it->second.size(); + md_entry.flags = 0; + response_md.push_back(md_entry); } - cb(user_data, &consumed_md[0], consumed_md.size(), &response_md[0], + auto consumed_md_data = consumed_md.empty() ? nullptr : &consumed_md[0]; + auto response_md_data = response_md.empty() ? nullptr : &response_md[0]; + cb(user_data, consumed_md_data, consumed_md.size(), response_md_data, response_md.size(), static_cast<grpc_status_code>(status.error_code()), status.error_message().c_str()); } @@ -113,7 +115,7 @@ int SecureServerCredentials::AddPortToServer(const grpc::string& addr, void SecureServerCredentials::SetAuthMetadataProcessor( const std::shared_ptr<AuthMetadataProcessor>& processor) { - auto *wrapper = new AuthMetadataProcessorAyncWrapper(processor); + auto* wrapper = new AuthMetadataProcessorAyncWrapper(processor); grpc_server_credentials_set_auth_metadata_processor( creds_, {AuthMetadataProcessorAyncWrapper::Process, AuthMetadataProcessorAyncWrapper::Destroy, wrapper}); @@ -130,8 +132,8 @@ std::shared_ptr<ServerCredentials> SslServerCredentials( } grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create( options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(), - &pem_key_cert_pairs[0], pem_key_cert_pairs.size(), - options.force_client_auth, nullptr); + pem_key_cert_pairs.empty() ? nullptr : &pem_key_cert_pairs[0], + pem_key_cert_pairs.size(), options.force_client_auth, nullptr); return std::shared_ptr<ServerCredentials>( new SecureServerCredentials(c_creds)); } diff --git a/src/cpp/server/secure_server_credentials.h b/src/cpp/server/secure_server_credentials.h index 4f003c6b7e..5460f4a02c 100644 --- a/src/cpp/server/secure_server_credentials.h +++ b/src/cpp/server/secure_server_credentials.h @@ -46,7 +46,7 @@ namespace grpc { class AuthMetadataProcessorAyncWrapper GRPC_FINAL { public: - static void Destroy(void *wrapper); + static void Destroy(void* wrapper); static void Process(void* wrapper, grpc_auth_context* context, const grpc_metadata* md, size_t num_md, diff --git a/src/cpp/server/server.cc b/src/cpp/server/server.cc index d67205e822..f5063a079e 100644 --- a/src/cpp/server/server.cc +++ b/src/cpp/server/server.cc @@ -153,8 +153,7 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag { GPR_ASSERT((*req)->in_flight_); return true; } - gpr_log(GPR_ERROR, "Should never reach here"); - abort(); + GPR_UNREACHABLE_CODE(return false); } void SetupRequest() { cq_ = grpc_completion_queue_create(nullptr); } @@ -252,28 +251,36 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag { grpc_completion_queue* cq_; }; -static grpc_server* CreateServer(int max_message_size) { +static grpc_server* CreateServer( + int max_message_size, const grpc_compression_options& compression_options) { + grpc_arg args[2]; + size_t args_idx = 0; if (max_message_size > 0) { - grpc_arg arg; - arg.type = GRPC_ARG_INTEGER; - arg.key = const_cast<char*>(GRPC_ARG_MAX_MESSAGE_LENGTH); - arg.value.integer = max_message_size; - grpc_channel_args args = {1, &arg}; - return grpc_server_create(&args, nullptr); - } else { - return grpc_server_create(nullptr, nullptr); + args[args_idx].type = GRPC_ARG_INTEGER; + args[args_idx].key = const_cast<char*>(GRPC_ARG_MAX_MESSAGE_LENGTH); + args[args_idx].value.integer = max_message_size; + args_idx++; } + + args[args_idx].type = GRPC_ARG_INTEGER; + args[args_idx].key = const_cast<char*>(GRPC_COMPRESSION_ALGORITHM_STATE_ARG); + args[args_idx].value.integer = compression_options.enabled_algorithms_bitset; + args_idx++; + + grpc_channel_args channel_args = {args_idx, args}; + return grpc_server_create(&channel_args, nullptr); } Server::Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned, - int max_message_size) + int max_message_size, + grpc_compression_options compression_options) : max_message_size_(max_message_size), started_(false), shutdown_(false), num_running_cb_(0), sync_methods_(new std::list<SyncRequest>), has_generic_service_(false), - server_(CreateServer(max_message_size)), + server_(CreateServer(max_message_size, compression_options)), thread_pool_(thread_pool), thread_pool_owned_(thread_pool_owned) { grpc_server_register_completion_queue(server_, cq_.cq(), nullptr); @@ -317,7 +324,7 @@ bool Server::RegisterAsyncService(const grpc::string* host, GPR_ASSERT(service->server_ == nullptr && "Can only register an asynchronous service against one server."); service->server_ = this; - service->request_args_ = new void*[service->method_count_]; + service->request_args_ = new void* [service->method_count_]; for (size_t i = 0; i < service->method_count_; ++i) { void* tag = grpc_server_register_method(server_, service->method_names_[i], host ? host->c_str() : nullptr); @@ -533,6 +540,7 @@ void Server::ScheduleCallback() { void Server::RunRpc() { // Wait for one more incoming rpc. bool ok; + GPR_TIMER_SCOPE("Server::RunRpc", 0); auto* mrd = SyncRequest::Wait(&cq_, &ok); if (mrd) { ScheduleCallback(); @@ -548,6 +556,7 @@ void Server::RunRpc() { mrd->TeardownRequest(); } } + GPR_TIMER_SCOPE("cd.Run()", 0); cd.Run(); } } diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc index b739cbfe62..b8094aa8f6 100644 --- a/src/cpp/server/server_builder.cc +++ b/src/cpp/server/server_builder.cc @@ -43,7 +43,9 @@ namespace grpc { ServerBuilder::ServerBuilder() - : max_message_size_(-1), generic_service_(nullptr), thread_pool_(nullptr) {} + : max_message_size_(-1), generic_service_(nullptr), thread_pool_(nullptr) { + grpc_compression_options_init(&compression_options_); +} std::unique_ptr<ServerCompletionQueue> ServerBuilder::AddCompletionQueue() { ServerCompletionQueue* cq = new ServerCompletionQueue(); @@ -99,8 +101,9 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() { thread_pool_ = CreateDefaultThreadPool(); thread_pool_owned = true; } - std::unique_ptr<Server> server( - new Server(thread_pool_, thread_pool_owned, max_message_size_)); + std::unique_ptr<Server> server(new Server(thread_pool_, thread_pool_owned, + max_message_size_, + compression_options_)); for (auto cq = cqs_.begin(); cq != cqs_.end(); ++cq) { grpc_server_register_completion_queue(server->server_, (*cq)->cq(), nullptr); @@ -128,7 +131,8 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() { *port->selected_port = r; } } - if (!server->Start(&cqs_[0], cqs_.size())) { + auto cqs_data = cqs_.empty() ? nullptr : &cqs_[0]; + if (!server->Start(cqs_data, cqs_.size())) { return nullptr; } return server; diff --git a/src/cpp/util/byte_buffer.cc b/src/cpp/util/byte_buffer.cc index e46e656beb..755234d7e8 100644 --- a/src/cpp/util/byte_buffer.cc +++ b/src/cpp/util/byte_buffer.cc @@ -45,6 +45,12 @@ ByteBuffer::ByteBuffer(const Slice* slices, size_t nslices) { buffer_ = grpc_raw_byte_buffer_create(c_slices.data(), nslices); } +ByteBuffer::~ByteBuffer() { + if (buffer_) { + grpc_byte_buffer_destroy(buffer_); + } +} + void ByteBuffer::Clear() { if (buffer_) { grpc_byte_buffer_destroy(buffer_); diff --git a/src/cpp/util/string_ref.cc b/src/cpp/util/string_ref.cc index d12701f2c9..66c79a1818 100644 --- a/src/cpp/util/string_ref.cc +++ b/src/cpp/util/string_ref.cc @@ -36,6 +36,7 @@ #include <string.h> #include <algorithm> +#include <iostream> namespace grpc { @@ -84,29 +85,17 @@ size_t string_ref::find(char c) const { return it == cend() ? npos : std::distance(cbegin(), it); } -bool operator==(string_ref x, string_ref y) { - return x.compare(y) == 0; -} +bool operator==(string_ref x, string_ref y) { return x.compare(y) == 0; } -bool operator!=(string_ref x, string_ref y) { - return x.compare(y) != 0; -} +bool operator!=(string_ref x, string_ref y) { return x.compare(y) != 0; } -bool operator<(string_ref x, string_ref y) { - return x.compare(y) < 0; -} +bool operator<(string_ref x, string_ref y) { return x.compare(y) < 0; } -bool operator<=(string_ref x, string_ref y) { - return x.compare(y) <= 0; -} +bool operator<=(string_ref x, string_ref y) { return x.compare(y) <= 0; } -bool operator>(string_ref x, string_ref y) { - return x.compare(y) > 0; -} +bool operator>(string_ref x, string_ref y) { return x.compare(y) > 0; } -bool operator>=(string_ref x, string_ref y) { - return x.compare(y) >= 0; -} +bool operator>=(string_ref x, string_ref y) { return x.compare(y) >= 0; } std::ostream& operator<<(std::ostream& out, const string_ref& string) { return out << grpc::string(string.begin(), string.end()); diff --git a/src/cpp/util/time.cc b/src/cpp/util/time.cc index b3401eb26b..6157a37745 100644 --- a/src/cpp/util/time.cc +++ b/src/cpp/util/time.cc @@ -57,8 +57,8 @@ void Timepoint2Timespec(const system_clock::time_point& from, return; } nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs); - to->tv_sec = secs.count(); - to->tv_nsec = nsecs.count(); + to->tv_sec = (time_t)secs.count(); + to->tv_nsec = (int)nsecs.count(); to->clock_type = GPR_CLOCK_REALTIME; } @@ -73,8 +73,8 @@ void TimepointHR2Timespec(const high_resolution_clock::time_point& from, return; } nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs); - to->tv_sec = secs.count(); - to->tv_nsec = nsecs.count(); + to->tv_sec = (time_t)secs.count(); + to->tv_nsec = (int)nsecs.count(); to->clock_type = GPR_CLOCK_REALTIME; } diff --git a/src/csharp/.gitignore b/src/csharp/.gitignore index deac55029e..0f96a48221 100644 --- a/src/csharp/.gitignore +++ b/src/csharp/.gitignore @@ -7,6 +7,7 @@ Grpc.v12.suo Grpc.sdf TestResult.xml +coverage_results.xml /TestResults .vs/ *.nupkg diff --git a/src/csharp/.nuget/packages.config b/src/csharp/.nuget/packages.config index a7df95cf6b..89a310ac56 100644 --- a/src/csharp/.nuget/packages.config +++ b/src/csharp/.nuget/packages.config @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="NUnit.Runners" version="2.6.4" /> + <package id="OpenCover" version="4.6.166" /> + <package id="ReportGenerator" version="2.3.2.0" /> </packages>
\ No newline at end of file diff --git a/src/csharp/Grpc.Auth/AuthInterceptors.cs b/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs index c8ab4d9af6..1c14c5bb5b 100644 --- a/src/csharp/Grpc.Auth/AuthInterceptors.cs +++ b/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs @@ -41,40 +41,38 @@ using Grpc.Core.Utils; namespace Grpc.Auth { /// <summary> - /// Factory methods to create authorization interceptors. Interceptors created can be registered with gRPC client classes (autogenerated client stubs that - /// inherit from <see cref="Grpc.Core.ClientBase"/>). + /// Factory methods to create authorization interceptors for Google credentials. + /// <seealso cref="GoogleGrpcCredentials"/> /// </summary> - public static class AuthInterceptors + public static class GoogleAuthInterceptors { private const string AuthorizationHeader = "Authorization"; private const string Schema = "Bearer"; /// <summary> - /// Creates interceptor that will obtain access token from any credential type that implements + /// Creates an <see cref="AsyncAuthInterceptor"/> that will obtain access token from any credential type that implements /// <c>ITokenAccess</c>. (e.g. <c>GoogleCredential</c>). /// </summary> /// <param name="credential">The credential to use to obtain access tokens.</param> - /// <returns>The header interceptor.</returns> - public static HeaderInterceptor FromCredential(ITokenAccess credential) + /// <returns>The interceptor.</returns> + public static AsyncAuthInterceptor FromCredential(ITokenAccess credential) { - return new HeaderInterceptor((method, authUri, metadata) => + return new AsyncAuthInterceptor(async (authUri, metadata) => { - // TODO(jtattermusch): Rethink synchronous wait to obtain the result. - var accessToken = credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None) - .ConfigureAwait(false).GetAwaiter().GetResult(); + var accessToken = await credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None).ConfigureAwait(false); metadata.Add(CreateBearerTokenHeader(accessToken)); }); } /// <summary> - /// Creates OAuth2 interceptor that will use given access token as authorization. + /// Creates an <see cref="AsyncAuthInterceptor"/> that will use given access token as authorization. /// </summary> /// <param name="accessToken">OAuth2 access token.</param> - /// <returns>The header interceptor.</returns> - public static HeaderInterceptor FromAccessToken(string accessToken) + /// <returns>The interceptor.</returns> + public static AsyncAuthInterceptor FromAccessToken(string accessToken) { Preconditions.CheckNotNull(accessToken); - return new HeaderInterceptor((method, authUri, metadata) => + return new AsyncAuthInterceptor(async (authUri, metadata) => { metadata.Add(CreateBearerTokenHeader(accessToken)); }); diff --git a/src/csharp/Grpc.Auth/GoogleGrpcCredentials.cs b/src/csharp/Grpc.Auth/GoogleGrpcCredentials.cs new file mode 100644 index 0000000000..a1e7db13bd --- /dev/null +++ b/src/csharp/Grpc.Auth/GoogleGrpcCredentials.cs @@ -0,0 +1,96 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; + +using Google.Apis.Auth.OAuth2; +using Grpc.Core; +using Grpc.Core.Utils; + +namespace Grpc.Auth +{ + /// <summary> + /// Factory/extension methods to create instances of <see cref="ChannelCredentials"/> and <see cref="CallCredentials"/> classes + /// based on credential objects originating from Google auth library. + /// </summary> + public static class GoogleGrpcCredentials + { + /// <summary> + /// Retrieves an instance of Google's Application Default Credentials using + /// <c>GoogleCredential.GetApplicationDefaultAsync()</c> and converts them + /// into a gRPC <see cref="ChannelCredentials"/> that use the default SSL credentials. + /// </summary> + /// <returns>The <c>ChannelCredentials</c> instance.</returns> + public static async Task<ChannelCredentials> GetApplicationDefaultAsync() + { + var googleCredential = await GoogleCredential.GetApplicationDefaultAsync().ConfigureAwait(false); + return googleCredential.ToChannelCredentials(); + } + + /// <summary> + /// Creates an instance of <see cref="CallCredentials"/> that will use given access token to authenticate + /// with a gRPC service. + /// </summary> + /// <param name="accessToken">OAuth2 access token.</param> + /// /// <returns>The <c>MetadataCredentials</c> instance.</returns> + public static CallCredentials FromAccessToken(string accessToken) + { + return CallCredentials.FromInterceptor(GoogleAuthInterceptors.FromAccessToken(accessToken)); + } + + /// <summary> + /// Converts a <c>ITokenAccess</c> (e.g. <c>GoogleCredential</c>) object + /// into a gRPC <see cref="CallCredentials"/> object. + /// </summary> + /// <param name="credential">The credential to use to obtain access tokens.</param> + /// <returns>The <c>CallCredentials</c> instance.</returns> + public static CallCredentials ToCallCredentials(this ITokenAccess credential) + { + return CallCredentials.FromInterceptor(GoogleAuthInterceptors.FromCredential(credential)); + } + + /// <summary> + /// Converts a <c>ITokenAccess</c> (e.g. <c>GoogleCredential</c>) object + /// into a gRPC <see cref="ChannelCredentials"/> object. + /// Default SSL credentials are used. + /// </summary> + /// <param name="googleCredential">The credential to use to obtain access tokens.</param> + /// <returns>>The <c>ChannelCredentials</c> instance.</returns> + public static ChannelCredentials ToChannelCredentials(this ITokenAccess googleCredential) + { + return ChannelCredentials.Create(new SslCredentials(), googleCredential.ToCallCredentials()); + } + } +} diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj index 4fb087d4a3..55bde6e194 100644 --- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj +++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj @@ -78,8 +78,9 @@ <Compile Include="..\Grpc.Core\Version.cs"> <Link>Version.cs</Link> </Compile> + <Compile Include="GoogleGrpcCredentials.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="AuthInterceptors.cs" /> + <Compile Include="GoogleAuthInterceptors.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> diff --git a/src/csharp/Grpc.Core.Tests/ClientBaseTest.cs b/src/csharp/Grpc.Core.Tests/CallCredentialsTest.cs index 2dc10ebe97..82f881969e 100644 --- a/src/csharp/Grpc.Core.Tests/ClientBaseTest.cs +++ b/src/csharp/Grpc.Core.Tests/CallCredentialsTest.cs @@ -32,6 +32,10 @@ #endregion using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; using Grpc.Core; using Grpc.Core.Internal; using Grpc.Core.Utils; @@ -39,24 +43,23 @@ using NUnit.Framework; namespace Grpc.Core.Tests { - public class ClientBaseTest + public class CallCredentialsTest { [Test] - public void GetAuthUriBase_Valid() + public void CallCredentials_ComposeAtLeastTwo() { - Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com")); - Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com/")); - Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com:443/")); - Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com:443/")); + Assert.Throws(typeof(ArgumentException), () => CallCredentials.Compose(new FakeCallCredentials())); } [Test] - public void GetAuthUriBase_Invalid() + public void CallCredentials_ToNativeCredentials() { - Assert.IsNull(ClientBase.GetAuthUriBase("some.googleapi.com:")); - Assert.IsNull(ClientBase.GetAuthUriBase("https://some.googleapi.com/")); - Assert.IsNull(ClientBase.GetAuthUriBase("dns://some.googleapi.com:443")); // just two slashes - Assert.IsNull(ClientBase.GetAuthUriBase("")); + var composite = CallCredentials.Compose( + CallCredentials.FromInterceptor(async (uri, m) => { await Task.Delay(1); }), + CallCredentials.FromInterceptor(async (uri, m) => { await Task.Delay(2); })); + using (var nativeComposite = composite.ToNativeCredentials()) + { + } } } } diff --git a/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs new file mode 100644 index 0000000000..d5315ed39b --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs @@ -0,0 +1,67 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ChannelCredentialsTest + { + [Test] + public void InsecureCredentials_IsNonComposable() + { + Assert.IsFalse(ChannelCredentials.Insecure.IsComposable); + } + + [Test] + public void ChannelCredentials_CreateComposite() + { + var composite = ChannelCredentials.Create(new FakeChannelCredentials(true), new FakeCallCredentials()); + Assert.IsFalse(composite.IsComposable); + + Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(null, new FakeCallCredentials())); + Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(new FakeChannelCredentials(true), null)); + + // forbid composing non-composable + Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials())); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ChannelTest.cs b/src/csharp/Grpc.Core.Tests/ChannelTest.cs index dfbd92879e..f4ae9abefd 100644 --- a/src/csharp/Grpc.Core.Tests/ChannelTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelTest.cs @@ -44,13 +44,13 @@ namespace Grpc.Core.Tests [Test] public void Constructor_RejectsInvalidParams() { - Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, Credentials.Insecure)); + Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, ChannelCredentials.Insecure)); } [Test] public void State_IdleAfterCreation() { - var channel = new Channel("localhost", Credentials.Insecure); + var channel = new Channel("localhost", ChannelCredentials.Insecure); Assert.AreEqual(ChannelState.Idle, channel.State); channel.ShutdownAsync().Wait(); } @@ -58,7 +58,7 @@ namespace Grpc.Core.Tests [Test] public void WaitForStateChangedAsync_InvalidArgument() { - var channel = new Channel("localhost", Credentials.Insecure); + var channel = new Channel("localhost", ChannelCredentials.Insecure); Assert.Throws(typeof(ArgumentException), () => channel.WaitForStateChangedAsync(ChannelState.FatalFailure)); channel.ShutdownAsync().Wait(); } @@ -66,7 +66,7 @@ namespace Grpc.Core.Tests [Test] public void ResolvedTarget() { - var channel = new Channel("127.0.0.1", Credentials.Insecure); + var channel = new Channel("127.0.0.1", ChannelCredentials.Insecure); Assert.IsTrue(channel.ResolvedTarget.Contains("127.0.0.1")); channel.ShutdownAsync().Wait(); } @@ -74,7 +74,7 @@ namespace Grpc.Core.Tests [Test] public void Shutdown_AllowedOnlyOnce() { - var channel = new Channel("localhost", Credentials.Insecure); + var channel = new Channel("localhost", ChannelCredentials.Insecure); channel.ShutdownAsync().Wait(); Assert.Throws(typeof(InvalidOperationException), () => channel.ShutdownAsync().GetAwaiter().GetResult()); } diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 68279a2007..25a5a27c8e 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -38,6 +38,7 @@ using System.Threading; using System.Threading.Tasks; using Grpc.Core; using Grpc.Core.Internal; +using Grpc.Core.Profiling; using Grpc.Core.Utils; using NUnit.Framework; @@ -200,19 +201,6 @@ namespace Grpc.Core.Tests Assert.AreEqual(headers[1].Key, trailers[1].Key); CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes); } - - [Test] - public void UnaryCallPerformance() - { - helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => - { - return request; - }); - - var callDetails = helper.CreateUnaryCall(); - BenchmarkUtil.RunBenchmark(100, 100, - () => { Calls.BlockingUnaryCall(callDetails, "ABC"); }); - } [Test] public void UnknownMethodHandler() diff --git a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs new file mode 100644 index 0000000000..87d55cd276 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs @@ -0,0 +1,73 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + internal class FakeChannelCredentials : ChannelCredentials + { + readonly bool composable; + + public FakeChannelCredentials(bool composable) + { + this.composable = composable; + } + + internal override bool IsComposable + { + get { return composable; } + } + + internal override CredentialsSafeHandle ToNativeCredentials() + { + return null; + } + } + + internal class FakeCallCredentials : CallCredentials + { + internal override CredentialsSafeHandle ToNativeCredentials() + { + return null; + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index f730936062..e5ffa31989 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -63,8 +63,10 @@ <Compile Include="..\Grpc.Core\Version.cs"> <Link>Version.cs</Link> </Compile> - <Compile Include="ClientBaseTest.cs" /> + <Compile Include="CallCredentialsTest.cs" /> + <Compile Include="FakeCredentials.cs" /> <Compile Include="MarshallingErrorsTest.cs" /> + <Compile Include="ChannelCredentialsTest.cs" /> <Compile Include="ShutdownTest.cs" /> <Compile Include="Internal\AsyncCallTest.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> @@ -86,6 +88,7 @@ <Compile Include="CompressionTest.cs" /> <Compile Include="ContextPropagationTest.cs" /> <Compile Include="MetadataTest.cs" /> + <Compile Include="PerformanceTest.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs index 685c5f7d6c..246072bff1 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs @@ -49,7 +49,7 @@ namespace Grpc.Core.Internal.Tests [SetUp] public void Init() { - channel = new Channel("localhost", Credentials.Insecure); + channel = new Channel("localhost", ChannelCredentials.Insecure); fakeCall = new FakeNativeCall(); diff --git a/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs b/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs index a2ee183272..c6843f10af 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs @@ -45,17 +45,21 @@ namespace Grpc.Core.Internal.Tests [Test] public void CreateAndDestroy() { + GrpcEnvironment.AddRef(); var cq = CompletionQueueSafeHandle.Create(); cq.Dispose(); + GrpcEnvironment.Release(); } [Test] public void CreateAndShutdown() { + GrpcEnvironment.AddRef(); var cq = CompletionQueueSafeHandle.Create(); cq.Shutdown(); var ev = cq.Next(); cq.Dispose(); + GrpcEnvironment.Release(); Assert.AreEqual(GRPCCompletionType.Shutdown, ev.type); Assert.AreNotEqual(IntPtr.Zero, ev.success); Assert.AreEqual(IntPtr.Zero, ev.tag); diff --git a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs index 874df02baa..9be5450d81 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs @@ -34,6 +34,7 @@ using System; using System.Runtime.InteropServices; using Grpc.Core.Internal; +using Grpc.Core.Utils; using NUnit.Framework; namespace Grpc.Core.Internal.Tests @@ -198,5 +199,23 @@ namespace Grpc.Core.Internal.Tests Console.WriteLine("Test cannot be run on this platform, skipping the test."); } } + + // Test attribute commented out to prevent running as part of the default test suite. + // [Test] + // [Category("Performance")] + public void NowBenchmark() + { + // approx Timespec.Now latency <33ns + BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.Now; }); + } + + // Test attribute commented out to prevent running as part of the default test suite. + // [Test] + // [Category("Performance")] + public void PreciseNowBenchmark() + { + // approx Timespec.PreciseNow latency <18ns (when compiled with GRPC_TIMERS_RDTSC) + BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.PreciseNow; }); + } } } diff --git a/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs b/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs index 83707e0c6d..37fb36946a 100644 --- a/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs +++ b/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs @@ -119,7 +119,7 @@ namespace Grpc.Core.Tests [Test] public void RequestParsingError_UnaryRequest() { - helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => + helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => { return Task.FromResult("RESPONSE"); }); @@ -161,7 +161,7 @@ namespace Grpc.Core.Tests { helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) => { - CollectionAssert.AreEqual(new [] {"A", "B"}, await requestStream.ToListAsync()); + CollectionAssert.AreEqual(new[] { "A", "B" }, await requestStream.ToListAsync()); return "RESPONSE"; }); var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs index 765732c768..567e04eddc 100644 --- a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs +++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs @@ -154,7 +154,7 @@ namespace Grpc.Core.Tests { if (channel == null) { - channel = new Channel(Host, GetServer().Ports.Single().BoundPort, Credentials.Insecure); + channel = new Channel(Host, GetServer().Ports.Single().BoundPort, ChannelCredentials.Insecure); } return channel; } diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs index 714c2f7494..073c502daf 100644 --- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs +++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs @@ -60,7 +60,7 @@ namespace Grpc.Core.Tests public void CompletionQueueCreateDestroyBenchmark() { BenchmarkUtil.RunBenchmark( - 100000, 1000000, + 10, 10, () => { CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create(); diff --git a/src/csharp/Grpc.Core.Tests/PerformanceTest.cs b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs new file mode 100644 index 0000000000..5516cd3377 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs @@ -0,0 +1,99 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Profiling; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class PerformanceTest + { + const string Host = "127.0.0.1"; + + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(Host); + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + // Test attribute commented out to prevent running as part of the default test suite. + //[Test] + //[Category("Performance")] + public void UnaryCallPerformance() + { + var profiler = new BasicProfiler(); + Profilers.SetForCurrentThread(profiler); + + helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => + { + return request; + }); + + var callDetails = helper.CreateUnaryCall(); + for(int i = 0; i < 3000; i++) + { + Calls.BlockingUnaryCall(callDetails, "ABC"); + } + + profiler.Reset(); + + for(int i = 0; i < 3000; i++) + { + Calls.BlockingUnaryCall(callDetails, "ABC"); + } + profiler.Dump("latency_trace_csharp.txt"); + } + } +} diff --git a/src/csharp/Grpc.Core/CallCredentials.cs b/src/csharp/Grpc.Core/CallCredentials.cs new file mode 100644 index 0000000000..400a9825de --- /dev/null +++ b/src/csharp/Grpc.Core/CallCredentials.cs @@ -0,0 +1,152 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// <summary> + /// Asynchronous authentication interceptor for <see cref="CallCredentials"/>. + /// </summary> + /// <param name="authUri">URL of a service to which current remote call needs to authenticate</param> + /// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param> + /// <returns></returns> + public delegate Task AsyncAuthInterceptor(string authUri, Metadata metadata); + + /// <summary> + /// Client-side call credentials. Provide authorization with per-call granularity. + /// </summary> + public abstract class CallCredentials + { + /// <summary> + /// Composes multiple multiple <c>CallCredentials</c> objects into + /// a single <c>CallCredentials</c> object. + /// </summary> + /// <param name="credentials">credentials to compose</param> + /// <returns>The new <c>CompositeCallCredentials</c></returns> + public static CallCredentials Compose(params CallCredentials[] credentials) + { + return new CompositeCallCredentials(credentials); + } + + /// <summary> + /// Creates a new instance of <c>CallCredentials</c> class from an + /// interceptor that can attach metadata to outgoing calls. + /// </summary> + /// <param name="interceptor">authentication interceptor</param> + public static CallCredentials FromInterceptor(AsyncAuthInterceptor interceptor) + { + return new MetadataCredentials(interceptor); + } + + /// <summary> + /// Creates native object for the credentials. + /// </summary> + /// <returns>The native credentials.</returns> + internal abstract CredentialsSafeHandle ToNativeCredentials(); + } + + /// <summary> + /// Client-side credentials that delegate metadata based auth to an interceptor. + /// The interceptor is automatically invoked for each remote call that uses <c>MetadataCredentials.</c> + /// </summary> + internal sealed class MetadataCredentials : CallCredentials + { + readonly AsyncAuthInterceptor interceptor; + + /// <summary> + /// Initializes a new instance of <c>MetadataCredentials</c> class. + /// </summary> + /// <param name="interceptor">authentication interceptor</param> + public MetadataCredentials(AsyncAuthInterceptor interceptor) + { + this.interceptor = Preconditions.CheckNotNull(interceptor); + } + + internal override CredentialsSafeHandle ToNativeCredentials() + { + NativeMetadataCredentialsPlugin plugin = new NativeMetadataCredentialsPlugin(interceptor); + return plugin.Credentials; + } + } + + /// <summary> + /// Credentials that allow composing multiple credentials objects into one <see cref="CallCredentials"/> object. + /// </summary> + internal sealed class CompositeCallCredentials : CallCredentials + { + readonly List<CallCredentials> credentials; + + /// <summary> + /// Initializes a new instance of <c>CompositeCallCredentials</c> class. + /// The resulting credentials object will be composite of all the credentials specified as parameters. + /// </summary> + /// <param name="credentials">credentials to compose</param> + public CompositeCallCredentials(params CallCredentials[] credentials) + { + Preconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials."); + this.credentials = new List<CallCredentials>(credentials); + } + + internal override CredentialsSafeHandle ToNativeCredentials() + { + return ToNativeRecursive(0); + } + + // Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier. + // In practice, we won't usually see composites from more than two credentials anyway. + private CredentialsSafeHandle ToNativeRecursive(int startIndex) + { + if (startIndex == credentials.Count - 1) + { + return credentials[startIndex].ToNativeCredentials(); + } + + using (var cred1 = credentials[startIndex].ToNativeCredentials()) + using (var cred2 = ToNativeRecursive(startIndex + 1)) + { + var nativeComposite = CredentialsSafeHandle.CreateComposite(cred1, cred2); + if (nativeComposite.IsInvalid) + { + throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials."); + } + return nativeComposite; + } + } + } +} diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs index c3bc9c3156..c0f94c63c2 100644 --- a/src/csharp/Grpc.Core/CallOptions.cs +++ b/src/csharp/Grpc.Core/CallOptions.cs @@ -49,6 +49,7 @@ namespace Grpc.Core CancellationToken cancellationToken; WriteOptions writeOptions; ContextPropagationToken propagationToken; + CallCredentials credentials; /// <summary> /// Creates a new instance of <c>CallOptions</c> struct. @@ -58,14 +59,16 @@ namespace Grpc.Core /// <param name="cancellationToken">Can be used to request cancellation of the call.</param> /// <param name="writeOptions">Write options that will be used for this call.</param> /// <param name="propagationToken">Context propagation token obtained from <see cref="ServerCallContext"/>.</param> + /// <param name="credentials">Credentials to use for this call.</param> public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken), - WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null) + WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null, CallCredentials credentials = null) { this.headers = headers; this.deadline = deadline; this.cancellationToken = cancellationToken; this.writeOptions = writeOptions; this.propagationToken = propagationToken; + this.credentials = credentials; } /// <summary> @@ -115,6 +118,17 @@ namespace Grpc.Core } /// <summary> + /// Credentials to use for this call. + /// </summary> + public CallCredentials Credentials + { + get + { + return this.credentials; + } + } + + /// <summary> /// Returns new instance of <see cref="CallOptions"/> with /// <c>Headers</c> set to the value provided. Values of all other fields are preserved. /// </summary> diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index f1942727cd..6b99055d4c 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -68,7 +68,7 @@ namespace Grpc.Core /// <param name="target">Target of the channel.</param> /// <param name="credentials">Credentials to secure the channel.</param> /// <param name="options">Channel options.</param> - public Channel(string target, Credentials credentials, IEnumerable<ChannelOption> options = null) + public Channel(string target, ChannelCredentials credentials, IEnumerable<ChannelOption> options = null) { this.target = Preconditions.CheckNotNull(target, "target"); this.environment = GrpcEnvironment.AddRef(); @@ -96,7 +96,7 @@ namespace Grpc.Core /// <param name="port">The port.</param> /// <param name="credentials">Credentials to secure the channel.</param> /// <param name="options">Channel options.</param> - public Channel(string host, int port, Credentials credentials, IEnumerable<ChannelOption> options = null) : + public Channel(string host, int port, ChannelCredentials credentials, IEnumerable<ChannelOption> options = null) : this(string.Format("{0}:{1}", host, port), credentials, options) { } diff --git a/src/csharp/Grpc.Core/Credentials.cs b/src/csharp/Grpc.Core/ChannelCredentials.cs index 4fcac0c4c0..9d2bcdabe8 100644 --- a/src/csharp/Grpc.Core/Credentials.cs +++ b/src/csharp/Grpc.Core/ChannelCredentials.cs @@ -32,22 +32,26 @@ #endregion using System; +using System.Collections.Generic; +using System.Threading.Tasks; + using Grpc.Core.Internal; +using Grpc.Core.Utils; namespace Grpc.Core { /// <summary> - /// Client-side credentials. Used for creation of a secure channel. + /// Client-side channel credentials. Used for creation of a secure channel. /// </summary> - public abstract class Credentials + public abstract class ChannelCredentials { - static readonly Credentials InsecureInstance = new InsecureCredentialsImpl(); + static readonly ChannelCredentials InsecureInstance = new InsecureCredentialsImpl(); /// <summary> - /// Returns instance of credential that provides no security and + /// Returns instance of credentials that provides no security and /// will result in creating an unsecure channel with no encryption whatsoever. /// </summary> - public static Credentials Insecure + public static ChannelCredentials Insecure { get { @@ -56,13 +60,33 @@ namespace Grpc.Core } /// <summary> + /// Creates a new instance of <c>ChannelCredentials</c> class by composing + /// given channel credentials with call credentials. + /// </summary> + /// <param name="channelCredentials">Channel credentials.</param> + /// <param name="callCredentials">Call credentials.</param> + /// <returns>The new composite <c>ChannelCredentials</c></returns> + public static ChannelCredentials Create(ChannelCredentials channelCredentials, CallCredentials callCredentials) + { + return new CompositeChannelCredentials(channelCredentials, callCredentials); + } + + /// <summary> /// Creates native object for the credentials. May return null if insecure channel /// should be created. /// </summary> /// <returns>The native credentials.</returns> internal abstract CredentialsSafeHandle ToNativeCredentials(); - private sealed class InsecureCredentialsImpl : Credentials + /// <summary> + /// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>. + /// </summary> + internal virtual bool IsComposable + { + get { return false; } + } + + private sealed class InsecureCredentialsImpl : ChannelCredentials { internal override CredentialsSafeHandle ToNativeCredentials() { @@ -74,7 +98,7 @@ namespace Grpc.Core /// <summary> /// Client-side SSL credentials. /// </summary> - public sealed class SslCredentials : Credentials + public sealed class SslCredentials : ChannelCredentials { readonly string rootCertificates; readonly KeyCertificatePair keyCertificatePair; @@ -130,9 +154,52 @@ namespace Grpc.Core } } + // Composing composite makes no sense. + internal override bool IsComposable + { + get { return true; } + } + internal override CredentialsSafeHandle ToNativeCredentials() { return CredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair); } } + + /// <summary> + /// Credentials that allow composing one <see cref="ChannelCredentials"/> object and + /// one or more <see cref="CallCredentials"/> objects into a single <see cref="ChannelCredentials"/>. + /// </summary> + internal sealed class CompositeChannelCredentials : ChannelCredentials + { + readonly ChannelCredentials channelCredentials; + readonly CallCredentials callCredentials; + + /// <summary> + /// Initializes a new instance of <c>CompositeChannelCredentials</c> class. + /// The resulting credentials object will be composite of all the credentials specified as parameters. + /// </summary> + /// <param name="channelCredentials">channelCredentials to compose</param> + /// <param name="callCredentials">channelCredentials to compose</param> + public CompositeChannelCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials) + { + this.channelCredentials = Preconditions.CheckNotNull(channelCredentials); + this.callCredentials = Preconditions.CheckNotNull(callCredentials); + Preconditions.CheckArgument(channelCredentials.IsComposable, "Supplied channel credentials do not allow composition."); + } + + internal override CredentialsSafeHandle ToNativeCredentials() + { + using (var cred1 = channelCredentials.ToNativeCredentials()) + using (var cred2 = callCredentials.ToNativeCredentials()) + { + var nativeComposite = CredentialsSafeHandle.CreateComposite(cred1, cred2); + if (nativeComposite.IsInvalid) + { + throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials."); + } + return nativeComposite; + } + } + } } diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index f4533e735c..e5b398062b 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -40,18 +40,17 @@ namespace Grpc.Core /// <summary> /// Interceptor for call headers. /// </summary> - public delegate void HeaderInterceptor(IMethod method, string authUri, Metadata metadata); + /// <remarks>Header interceptor is no longer to recommented way to perform authentication. + /// For header (initial metadata) based auth such as OAuth2 or JWT access token, use <see cref="MetadataCredentials"/>. + /// </remarks> + public delegate void HeaderInterceptor(IMethod method, Metadata metadata); /// <summary> /// Base class for client-side stubs. /// </summary> public abstract class ClientBase { - // Regex for removal of the optional DNS scheme, trailing port, and trailing backslash - static readonly Regex ChannelTargetPattern = new Regex(@"^(dns:\/{3})?([^:\/]+)(:\d+)?\/?$"); - readonly Channel channel; - readonly string authUriBase; /// <summary> /// Initializes a new instance of <c>ClientBase</c> class. @@ -60,13 +59,14 @@ namespace Grpc.Core public ClientBase(Channel channel) { this.channel = channel; - this.authUriBase = GetAuthUriBase(channel.Target); } /// <summary> - /// Can be used to register a custom header (request metadata) interceptor. + /// Can be used to register a custom header interceptor. /// The interceptor is invoked each time a new call on this client is started. + /// It is not recommented to use header interceptor to add auth headers to RPC calls. /// </summary> + /// <seealso cref="HeaderInterceptor"/> public HeaderInterceptor HeaderInterceptor { get; @@ -115,24 +115,9 @@ namespace Grpc.Core { options = options.WithHeaders(new Metadata()); } - var authUri = authUriBase != null ? authUriBase + method.ServiceName : null; - interceptor(method, authUri, options.Headers); + interceptor(method, options.Headers); } return new CallInvocationDetails<TRequest, TResponse>(channel, method, Host, options); } - - /// <summary> - /// Creates Auth URI base from channel's target (the one passed at channel creation). - /// Fully-qualified service name is to be appended to this. - /// </summary> - internal static string GetAuthUriBase(string target) - { - var match = ChannelTargetPattern.Match(target); - if (!match.Success) - { - return null; - } - return "https://" + match.Groups[2].Value + "/"; - } } } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index ad2af17bc7..0aab7bdd8a 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -48,7 +48,9 @@ <ItemGroup> <Compile Include="AsyncDuplexStreamingCall.cs" /> <Compile Include="AsyncServerStreamingCall.cs" /> + <Compile Include="CallCredentials.cs" /> <Compile Include="IClientStreamWriter.cs" /> + <Compile Include="Internal\NativeMetadataCredentialsPlugin.cs" /> <Compile Include="Internal\INativeCall.cs" /> <Compile Include="IServerStreamWriter.cs" /> <Compile Include="IAsyncStreamWriter.cs" /> @@ -79,7 +81,7 @@ <Compile Include="Utils\AsyncStreamExtensions.cs" /> <Compile Include="Utils\BenchmarkUtil.cs" /> <Compile Include="Internal\CredentialsSafeHandle.cs" /> - <Compile Include="Credentials.cs" /> + <Compile Include="ChannelCredentials.cs" /> <Compile Include="Internal\ChannelArgsSafeHandle.cs" /> <Compile Include="Internal\AsyncCompletion.cs" /> <Compile Include="Internal\AsyncCallBase.cs" /> @@ -117,6 +119,10 @@ <Compile Include="CompressionLevel.cs" /> <Compile Include="WriteOptions.cs" /> <Compile Include="ContextPropagationToken.cs" /> + <Compile Include="Profiling\ProfilerEntry.cs" /> + <Compile Include="Profiling\ProfilerScope.cs" /> + <Compile Include="Profiling\IProfiler.cs" /> + <Compile Include="Profiling\Profilers.cs" /> </ItemGroup> <ItemGroup> <None Include="Grpc.Core.nuspec" /> @@ -148,4 +154,7 @@ <Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" /> <Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" /> <ItemGroup /> + <ItemGroup> + <Folder Include="Profiling\" /> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.Core/Grpc.Core.nuspec b/src/csharp/Grpc.Core/Grpc.Core.nuspec index 06de55c8c3..67a04afc22 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.nuspec +++ b/src/csharp/Grpc.Core/Grpc.Core.nuspec @@ -16,7 +16,7 @@ <tags>gRPC RPC Protocol HTTP/2</tags> <dependencies> <dependency id="Ix-Async" version="1.2.3" /> - <dependency id="grpc.native.csharp_ext" version="$GrpcNativeCsharpExtVersion$" /> + <dependency id="grpc.native.csharp" version="$GrpcNativeCsharpVersion$" /> </dependencies> </metadata> <files> diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index e3b00781c6..e3ecc47282 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -39,6 +39,7 @@ using System.Threading; using System.Threading.Tasks; using Grpc.Core.Internal; using Grpc.Core.Logging; +using Grpc.Core.Profiling; using Grpc.Core.Utils; namespace Grpc.Core.Internal @@ -87,6 +88,9 @@ namespace Grpc.Core.Internal /// </summary> public TResponse UnaryCall(TRequest msg) { + var profiler = Profilers.ForCurrentThread(); + + using (profiler.NewScope("AsyncCall.UnaryCall")) using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create()) { byte[] payload = UnsafeSerialize(msg); @@ -104,24 +108,26 @@ namespace Grpc.Core.Internal } using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) + using (var ctx = BatchContextSafeHandle.Create()) { - using (var ctx = BatchContextSafeHandle.Create()) - { - call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall()); - var ev = cq.Pluck(ctx.Handle); + call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall()); + + var ev = cq.Pluck(ctx.Handle); - bool success = (ev.success != 0); - try + bool success = (ev.success != 0); + try + { + using (profiler.NewScope("AsyncCall.UnaryCall.HandleBatch")) { HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessage(), ctx.GetReceivedInitialMetadata()); } - catch (Exception e) - { - Logger.Error(e, "Exception occured while invoking completion delegate."); - } + } + catch (Exception e) + { + Logger.Error(e, "Exception occured while invoking completion delegate."); } } - + // Once the blocking call returns, the result should be available synchronously. // Note that GetAwaiter().GetResult() doesn't wrap exceptions in AggregateException. return unaryResponseTcs.Task.GetAwaiter().GetResult(); @@ -329,24 +335,36 @@ namespace Grpc.Core.Internal private void Initialize(CompletionQueueSafeHandle cq) { - var call = CreateNativeCall(cq); - details.Channel.AddCallReference(this); - InitializeInternal(call); - RegisterCancellationCallback(); + using (Profilers.ForCurrentThread().NewScope("AsyncCall.Initialize")) + { + var call = CreateNativeCall(cq); + + details.Channel.AddCallReference(this); + InitializeInternal(call); + RegisterCancellationCallback(); + } } private INativeCall CreateNativeCall(CompletionQueueSafeHandle cq) { - if (injectedNativeCall != null) - { - return injectedNativeCall; // allows injecting a mock INativeCall in tests. - } + using (Profilers.ForCurrentThread().NewScope("AsyncCall.CreateNativeCall")) + { + if (injectedNativeCall != null) + { + return injectedNativeCall; // allows injecting a mock INativeCall in tests. + } - var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance; + var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance; - return details.Channel.Handle.CreateCall(environment.CompletionRegistry, - parentCall, ContextPropagationToken.DefaultMask, cq, - details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value)); + var credentials = details.Options.Credentials; + using (var nativeCredentials = credentials != null ? credentials.ToNativeCredentials() : null) + { + var result = details.Channel.Handle.CreateCall(environment.CompletionRegistry, + parentCall, ContextPropagationToken.DefaultMask, cq, + details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value), nativeCredentials); + return result; + } + } } // Make sure that once cancellationToken for this call is cancelled, Cancel() will be called. @@ -381,33 +399,37 @@ namespace Grpc.Core.Internal /// </summary> private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders) { - TResponse msg = default(TResponse); - var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null; - - lock (myLock) + using (Profilers.ForCurrentThread().NewScope("AsyncCall.HandleUnaryResponse")) { - finished = true; + TResponse msg = default(TResponse); + var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null; - if (deserializeException != null && receivedStatus.Status.StatusCode == StatusCode.OK) + lock (myLock) { - receivedStatus = new ClientSideStatus(DeserializeResponseFailureStatus, receivedStatus.Trailers); + finished = true; + + if (deserializeException != null && receivedStatus.Status.StatusCode == StatusCode.OK) + { + receivedStatus = new ClientSideStatus(DeserializeResponseFailureStatus, receivedStatus.Trailers); + } + finishedStatus = receivedStatus; + + ReleaseResourcesIfPossible(); + } - finishedStatus = receivedStatus; - ReleaseResourcesIfPossible(); - } + responseHeadersTcs.SetResult(responseHeaders); - responseHeadersTcs.SetResult(responseHeaders); + var status = receivedStatus.Status; - var status = receivedStatus.Status; + if (!success || status.StatusCode != StatusCode.OK) + { + unaryResponseTcs.SetException(new RpcException(status)); + return; + } - if (!success || status.StatusCode != StatusCode.OK) - { - unaryResponseTcs.SetException(new RpcException(status)); - return; + unaryResponseTcs.SetResult(msg); } - - unaryResponseTcs.SetResult(msg); } /// <summary> diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs index 3e2c57c9b5..953f61aa1e 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs @@ -41,6 +41,7 @@ using System.Threading.Tasks; using Grpc.Core.Internal; using Grpc.Core.Logging; +using Grpc.Core.Profiling; using Grpc.Core.Utils; namespace Grpc.Core.Internal @@ -167,16 +168,19 @@ namespace Grpc.Core.Internal /// </summary> protected bool ReleaseResourcesIfPossible() { - if (!disposed && call != null) + using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.ReleaseResourcesIfPossible")) { - bool noMoreSendCompletions = sendCompletionDelegate == null && (halfcloseRequested || cancelRequested || finished); - if (noMoreSendCompletions && readingDone && finished) + if (!disposed && call != null) { - ReleaseResources(); - return true; + bool noMoreSendCompletions = sendCompletionDelegate == null && (halfcloseRequested || cancelRequested || finished); + if (noMoreSendCompletions && readingDone && finished) + { + ReleaseResources(); + return true; + } } + return false; } - return false; } protected abstract bool IsClient @@ -228,7 +232,10 @@ namespace Grpc.Core.Internal protected byte[] UnsafeSerialize(TWrite msg) { - return serializer(msg); + using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.UnsafeSerialize")) + { + return serializer(msg); + } } protected Exception TrySerialize(TWrite msg, out byte[] payload) @@ -247,15 +254,20 @@ namespace Grpc.Core.Internal protected Exception TryDeserialize(byte[] payload, out TRead msg) { - try - { - msg = deserializer(payload); - return null; - } - catch (Exception e) + using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.TryDeserialize")) { - msg = default(TRead); - return e; + try + { + + msg = deserializer(payload); + return null; + + } + catch (Exception e) + { + msg = default(TRead); + return e; + } } } diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index c3611a7761..ddeedebd11 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -34,6 +34,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using Grpc.Core; using Grpc.Core.Utils; +using Grpc.Core.Profiling; namespace Grpc.Core.Internal { @@ -99,6 +100,9 @@ namespace Grpc.Core.Internal BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_set_credentials(CallSafeHandle call, CredentialsSafeHandle credentials); + + [DllImport("grpc_csharp_ext.dll")] static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call); [DllImport("grpc_csharp_ext.dll")] @@ -113,6 +117,11 @@ namespace Grpc.Core.Internal this.completionRegistry = completionRegistry; } + public void SetCredentials(CredentialsSafeHandle credentials) + { + grpcsharp_call_set_credentials(this, credentials).CheckOk(); + } + public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) { var ctx = BatchContextSafeHandle.Create(); @@ -123,8 +132,11 @@ namespace Grpc.Core.Internal public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) { - grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags) - .CheckOk(); + using (Profilers.ForCurrentThread().NewScope("CallSafeHandle.StartUnary")) + { + grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags) + .CheckOk(); + } } public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray) diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs index 7a1c6e3dac..5f9169bcb2 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs @@ -32,6 +32,7 @@ using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Grpc.Core.Profiling; namespace Grpc.Core.Internal { @@ -82,11 +83,18 @@ namespace Grpc.Core.Internal return grpcsharp_secure_channel_create(credentials, target, channelArgs); } - public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline) + public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline, CredentialsSafeHandle credentials) { - var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline); - result.SetCompletionRegistry(registry); - return result; + using (Profilers.ForCurrentThread().NewScope("ChannelSafeHandle.CreateCall")) + { + var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline); + if (credentials != null) + { + result.SetCredentials(credentials); + } + result.SetCompletionRegistry(registry); + return result; + } } public ChannelState CheckConnectivityState(bool tryToConnect) diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs index f7a3471bb4..9de2bc7950 100644 --- a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs @@ -31,6 +31,7 @@ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; +using Grpc.Core.Profiling; namespace Grpc.Core.Internal { @@ -70,7 +71,10 @@ namespace Grpc.Core.Internal public CompletionQueueEvent Pluck(IntPtr tag) { - return grpcsharp_completion_queue_pluck(this, tag); + using (Profilers.ForCurrentThread().NewScope("CompletionQueueSafeHandle.Pluck")) + { + return grpcsharp_completion_queue_pluck(this, tag); + } } public void Shutdown() diff --git a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs index feed335362..bab45108e0 100644 --- a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs @@ -44,6 +44,9 @@ namespace Grpc.Core.Internal static extern CredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); [DllImport("grpc_csharp_ext.dll")] + static extern CredentialsSafeHandle grpcsharp_composite_credentials_create(CredentialsSafeHandle creds1, CredentialsSafeHandle creds2); + + [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_credentials_release(IntPtr credentials); private CredentialsSafeHandle() @@ -69,6 +72,11 @@ namespace Grpc.Core.Internal } } + public static CredentialsSafeHandle CreateComposite(CredentialsSafeHandle creds1, CredentialsSafeHandle creds2) + { + return grpcsharp_composite_credentials_create(creds1, creds2); + } + protected override bool ReleaseHandle() { grpcsharp_credentials_release(handle); diff --git a/src/csharp/Grpc.Core/Internal/Enums.cs b/src/csharp/Grpc.Core/Internal/Enums.cs index 185098160b..b0eab2001b 100644 --- a/src/csharp/Grpc.Core/Internal/Enums.cs +++ b/src/csharp/Grpc.Core/Internal/Enums.cs @@ -102,6 +102,9 @@ namespace Grpc.Core.Internal /* Realtime clock */ Realtime, + /* Precise clock good for performance profiling. */ + Precise, + /* Timespan - the distance between two time points */ Timespan } diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs index 31b834c979..ed1bd24498 100644 --- a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs @@ -31,6 +31,7 @@ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; +using Grpc.Core.Profiling; namespace Grpc.Core.Internal { @@ -66,14 +67,17 @@ namespace Grpc.Core.Internal public static MetadataArraySafeHandle Create(Metadata metadata) { - // TODO(jtattermusch): we might wanna check that the metadata is readonly - var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count)); - for (int i = 0; i < metadata.Count; i++) + using (Profilers.ForCurrentThread().NewScope("MetadataArraySafeHandle.Create")) { - var valueBytes = metadata[i].GetSerializedValueUnsafe(); - grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length)); + // TODO(jtattermusch): we might wanna check that the metadata is readonly + var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count)); + for (int i = 0; i < metadata.Count; i++) + { + var valueBytes = metadata[i].GetSerializedValueUnsafe(); + grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length)); + } + return metadataArray; } - return metadataArray; } /// <summary> diff --git a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs new file mode 100644 index 0000000000..f76492cba4 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs @@ -0,0 +1,113 @@ +#region Copyright notice and license +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy); + + internal class NativeMetadataCredentialsPlugin + { + const string GetMetadataExceptionMsg = "Exception occured in metadata credentials plugin."; + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<NativeMetadataCredentialsPlugin>(); + + [DllImport("grpc_csharp_ext.dll")] + static extern CredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor); + + [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] + static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); + + AsyncAuthInterceptor interceptor; + GCHandle gcHandle; + NativeMetadataInterceptor nativeInterceptor; + CredentialsSafeHandle credentials; + + public NativeMetadataCredentialsPlugin(AsyncAuthInterceptor interceptor) + { + this.interceptor = Preconditions.CheckNotNull(interceptor, "interceptor"); + this.nativeInterceptor = NativeMetadataInterceptorHandler; + + // Make sure the callback doesn't get garbage collected until it is destroyed. + this.gcHandle = GCHandle.Alloc(this.nativeInterceptor, GCHandleType.Normal); + this.credentials = grpcsharp_metadata_credentials_create_from_plugin(nativeInterceptor); + } + + public CredentialsSafeHandle Credentials + { + get { return credentials; } + } + + private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy) + { + if (isDestroy) + { + gcHandle.Free(); + return; + } + + try + { + string serviceUrl = Marshal.PtrToStringAnsi(serviceUrlPtr); + StartGetMetadata(serviceUrl, callbackPtr, userDataPtr); + } + catch (Exception e) + { + grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, MetadataArraySafeHandle.Create(Metadata.Empty), StatusCode.Unknown, GetMetadataExceptionMsg); + Logger.Error(e, GetMetadataExceptionMsg); + } + } + + private async void StartGetMetadata(string serviceUrl, IntPtr callbackPtr, IntPtr userDataPtr) + { + try + { + var metadata = new Metadata(); + await interceptor(serviceUrl, metadata); + + using (var metadataArray = MetadataArraySafeHandle.Create(metadata)) + { + grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, metadataArray, StatusCode.OK, null); + } + } + catch (Exception e) + { + grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, MetadataArraySafeHandle.Create(Metadata.Empty), StatusCode.Unknown, GetMetadataExceptionMsg); + Logger.Error(e, GetMetadataExceptionMsg); + } + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs index daf85d5f61..38fc067d9f 100644 --- a/src/csharp/Grpc.Core/Internal/Timespec.cs +++ b/src/csharp/Grpc.Core/Internal/Timespec.cs @@ -239,6 +239,19 @@ namespace Grpc.Core.Internal } } + /// <summary> + /// Gets current timestamp using <c>GPRClockType.Precise</c>. + /// Only available internally because core needs to be compiled with + /// GRPC_TIMERS_RDTSC support for this to use RDTSC. + /// </summary> + internal static Timespec PreciseNow + { + get + { + return gprsharp_now(GPRClockType.Precise); + } + } + internal static int NativeSize { get diff --git a/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs index 35561d25d8..5c5b802164 100644 --- a/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs +++ b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs @@ -79,48 +79,72 @@ namespace Grpc.Core.Logging } /// <summary>Logs a message with severity Debug.</summary> - public void Debug(string message, params object[] formatArgs) + public void Debug(string message) { - Log("D", message, formatArgs); + Log("D", message); + } + + /// <summary>Logs a formatted message with severity Debug.</summary> + public void Debug(string format, params object[] formatArgs) + { + Debug(string.Format(format, formatArgs)); } /// <summary>Logs a message with severity Info.</summary> - public void Info(string message, params object[] formatArgs) + public void Info(string message) + { + Log("I", message); + } + + /// <summary>Logs a formatted message with severity Info.</summary> + public void Info(string format, params object[] formatArgs) { - Log("I", message, formatArgs); + Info(string.Format(format, formatArgs)); } /// <summary>Logs a message with severity Warning.</summary> - public void Warning(string message, params object[] formatArgs) + public void Warning(string message) { - Log("W", message, formatArgs); + Log("W", message); + } + + /// <summary>Logs a formatted message with severity Warning.</summary> + public void Warning(string format, params object[] formatArgs) + { + Warning(string.Format(format, formatArgs)); } /// <summary>Logs a message and an associated exception with severity Warning.</summary> - public void Warning(Exception exception, string message, params object[] formatArgs) + public void Warning(Exception exception, string message) { - Log("W", message + " " + exception, formatArgs); + Warning(message + " " + exception); } /// <summary>Logs a message with severity Error.</summary> - public void Error(string message, params object[] formatArgs) + public void Error(string message) + { + Log("E", message); + } + + /// <summary>Logs a formatted message with severity Error.</summary> + public void Error(string format, params object[] formatArgs) { - Log("E", message, formatArgs); + Error(string.Format(format, formatArgs)); } /// <summary>Logs a message and an associated exception with severity Error.</summary> - public void Error(Exception exception, string message, params object[] formatArgs) + public void Error(Exception exception, string message) { - Log("E", message + " " + exception, formatArgs); + Error(message + " " + exception); } - private void Log(string severityString, string message, object[] formatArgs) + private void Log(string severityString, string message) { Console.Error.WriteLine("{0}{1} {2}{3}", severityString, DateTime.Now, forTypeString, - string.Format(message, formatArgs)); + message); } } } diff --git a/src/csharp/Grpc.Core/Logging/ILogger.cs b/src/csharp/Grpc.Core/Logging/ILogger.cs index 61e0c91388..7c0326422f 100644 --- a/src/csharp/Grpc.Core/Logging/ILogger.cs +++ b/src/csharp/Grpc.Core/Logging/ILogger.cs @@ -43,21 +43,33 @@ namespace Grpc.Core.Logging ILogger ForType<T>(); /// <summary>Logs a message with severity Debug.</summary> - void Debug(string message, params object[] formatArgs); + void Debug(string message); + + /// <summary>Logs a formatted message with severity Debug.</summary> + void Debug(string format, params object[] formatArgs); /// <summary>Logs a message with severity Info.</summary> - void Info(string message, params object[] formatArgs); + void Info(string message); + + /// <summary>Logs a formatted message with severity Info.</summary> + void Info(string format, params object[] formatArgs); /// <summary>Logs a message with severity Warning.</summary> - void Warning(string message, params object[] formatArgs); + void Warning(string message); + + /// <summary>Logs a formatted message with severity Warning.</summary> + void Warning(string format, params object[] formatArgs); /// <summary>Logs a message and an associated exception with severity Warning.</summary> - void Warning(Exception exception, string message, params object[] formatArgs); + void Warning(Exception exception, string message); /// <summary>Logs a message with severity Error.</summary> - void Error(string message, params object[] formatArgs); + void Error(string message); + + /// <summary>Logs a formatted message with severity Error.</summary> + void Error(string format, params object[] formatArgs); /// <summary>Logs a message and an associated exception with severity Error.</summary> - void Error(Exception exception, string message, params object[] formatArgs); + void Error(Exception exception, string message); } } diff --git a/src/csharp/Grpc.Core/Profiling/IProfiler.cs b/src/csharp/Grpc.Core/Profiling/IProfiler.cs new file mode 100644 index 0000000000..c426c365d2 --- /dev/null +++ b/src/csharp/Grpc.Core/Profiling/IProfiler.cs @@ -0,0 +1,47 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.IO; +using System.Threading; +using Grpc.Core.Internal; + +namespace Grpc.Core.Profiling +{ + internal interface IProfiler + { + void Begin(string tag); + void End(string tag); + void Mark(string tag); + } +} diff --git a/src/csharp/Grpc.Core/Profiling/ProfilerEntry.cs b/src/csharp/Grpc.Core/Profiling/ProfilerEntry.cs new file mode 100644 index 0000000000..5cc4c3c054 --- /dev/null +++ b/src/csharp/Grpc.Core/Profiling/ProfilerEntry.cs @@ -0,0 +1,87 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.IO; +using System.Threading; +using Grpc.Core.Internal; + +namespace Grpc.Core.Profiling +{ + internal struct ProfilerEntry + { + public enum Type { + BEGIN, + END, + MARK + } + + public ProfilerEntry(Timespec timespec, Type type, string tag) + { + this.timespec = timespec; + this.type = type; + this.tag = tag; + } + + public Timespec timespec; + public Type type; + public string tag; + + public override string ToString() + { + // mimic the output format used by C core. + return string.Format( + "{{\"t\": {0}.{1}, \"thd\":\"unknown\", \"type\": \"{2}\", \"tag\": \"{3}\", " + + "\"file\": \"unknown\", \"line\": 0, \"imp\": 0}}", + timespec.TimevalSeconds, timespec.TimevalNanos.ToString("D9"), + GetTypeAbbreviation(type), tag); + } + + internal static string GetTypeAbbreviation(Type type) + { + switch (type) + { + case Type.BEGIN: + return "{"; + + case Type.END: + return "}"; + + case Type.MARK: + return "."; + default: + throw new ArgumentException("Unknown type"); + } + } + } +} diff --git a/src/node/examples/stock.proto b/src/csharp/Grpc.Core/Profiling/ProfilerScope.cs index 5ee2bcbce6..413f3a1a35 100644 --- a/src/node/examples/stock.proto +++ b/src/csharp/Grpc.Core/Profiling/ProfilerScope.cs @@ -1,3 +1,5 @@ +#region Copyright notice and license + // Copyright 2015, Google Inc. // All rights reserved. // @@ -27,36 +29,32 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -syntax = "proto3"; - -package examples; - -// Protocol type definitions -message StockRequest { - string symbol = 1; - int32 num_trades_to_watch = 2; -} - -message StockReply { - float price = 1; - string symbol = 2; -} +#endregion +using System; +using System.IO; +using System.Threading; +using Grpc.Core.Internal; -// Interface exported by the server -service Stock { - // Simple blocking RPC - rpc GetLastTradePrice(StockRequest) returns (StockReply) { - } - // Bidirectional streaming RPC - rpc GetLastTradePriceMultiple(stream StockRequest) returns - (stream StockReply) { - } - // Unidirectional server-to-client streaming RPC - rpc WatchFutureTrades(StockRequest) returns (stream StockReply) { - } - // Unidirectional client-to-server streaming RPC - rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) { - } +namespace Grpc.Core.Profiling +{ + // Allows declaring Begin and End of a profiler scope with a using statement. + // declared as struct for better performance. + internal struct ProfilerScope : IDisposable + { + readonly IProfiler profiler; + readonly string tag; + public ProfilerScope(IProfiler profiler, string tag) + { + this.profiler = profiler; + this.tag = tag; + this.profiler.Begin(this.tag); + } + + public void Dispose() + { + profiler.End(tag); + } + } } diff --git a/src/csharp/Grpc.Core/Profiling/Profilers.cs b/src/csharp/Grpc.Core/Profiling/Profilers.cs new file mode 100644 index 0000000000..c8123347f2 --- /dev/null +++ b/src/csharp/Grpc.Core/Profiling/Profilers.cs @@ -0,0 +1,131 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.IO; +using System.Threading; +using Grpc.Core.Internal; + +namespace Grpc.Core.Profiling +{ + internal static class Profilers + { + static readonly NopProfiler defaultProfiler = new NopProfiler(); + static readonly ThreadLocal<IProfiler> profilers = new ThreadLocal<IProfiler>(); + + public static IProfiler ForCurrentThread() + { + return profilers.Value ?? defaultProfiler; + } + + public static void SetForCurrentThread(IProfiler profiler) + { + profilers.Value = profiler; + } + + public static ProfilerScope NewScope(this IProfiler profiler, string tag) + { + return new ProfilerScope(profiler, tag); + } + } + + internal class NopProfiler : IProfiler + { + public void Begin(string tag) + { + } + + public void End(string tag) + { + } + + public void Mark(string tag) + { + } + } + + // Profiler using Timespec.PreciseNow + internal class BasicProfiler : IProfiler + { + ProfilerEntry[] entries; + int count; + + public BasicProfiler() : this(1024*1024) + { + } + + public BasicProfiler(int capacity) + { + this.entries = new ProfilerEntry[capacity]; + } + + public void Begin(string tag) { + AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.BEGIN, tag)); + } + + public void End(string tag) { + AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.END, tag)); + } + + public void Mark(string tag) { + AddEntry(new ProfilerEntry(Timespec.PreciseNow, ProfilerEntry.Type.MARK, tag)); + } + + public void Reset() + { + count = 0; + } + + public void Dump(string filepath) + { + using (var stream = new StreamWriter(filepath)) + { + Dump(stream); + } + } + + public void Dump(TextWriter stream) + { + for (int i = 0; i < count; i++) + { + var entry = entries[i]; + stream.WriteLine(entry.ToString()); + } + } + + // NOT THREADSAFE! + void AddEntry(ProfilerEntry entry) { + entries[count++] = entry; + } + } +} diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs index eda821bc31..e9ec30e923 100644 --- a/src/csharp/Grpc.Core/VersionInfo.cs +++ b/src/csharp/Grpc.Core/VersionInfo.cs @@ -41,6 +41,6 @@ namespace Grpc.Core /// <summary> /// Current version of gRPC C# /// </summary> - public const string CurrentVersion = "0.7.0"; + public const string CurrentVersion = "0.7.1"; } } diff --git a/src/csharp/Grpc.Examples.MathClient/MathClient.cs b/src/csharp/Grpc.Examples.MathClient/MathClient.cs index 01e4a80bab..64e429ed5a 100644 --- a/src/csharp/Grpc.Examples.MathClient/MathClient.cs +++ b/src/csharp/Grpc.Examples.MathClient/MathClient.cs @@ -39,7 +39,7 @@ namespace Math { public static void Main(string[] args) { - var channel = new Channel("127.0.0.1", 23456, Credentials.Insecure); + var channel = new Channel("127.0.0.1", 23456, ChannelCredentials.Insecure); Math.IMathClient client = new Math.MathClient(channel); MathExamples.DivExample(client); diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs index e2975b5da9..290d42808e 100644 --- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs +++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs @@ -61,7 +61,7 @@ namespace Math.Tests Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } }; server.Start(); - channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); + channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure); client = Math.NewClient(channel); } diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs index 6c3a53bec0..d90f21c2e1 100644 --- a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs @@ -63,7 +63,7 @@ namespace Grpc.HealthCheck.Tests Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } }; server.Start(); - channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); + channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure); client = Grpc.Health.V1Alpha.Health.NewClient(channel); } diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj index 2c38c9645c..8bc2082a1d 100644 --- a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj +++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj @@ -9,6 +9,7 @@ <AssemblyName>Grpc.IntegrationTesting.Client</AssemblyName> <StartupObject>Grpc.IntegrationTesting.Client.Program</StartupObject> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <NuGetPackageImportStamp>6d22e68f</NuGetPackageImportStamp> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -38,7 +39,47 @@ <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> + <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> + </Reference> + <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath> + </Reference> + <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> + </Reference> + <Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Threading.Tasks.Extensions"> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> + </Reference> + <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> + </Reference> <Reference Include="System" /> + <Reference Include="System.Net" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.Extensions"> + <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath> + </Reference> + <Reference Include="System.Net.Http.Primitives"> + <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath> + </Reference> + <Reference Include="System.Net.Http.WebRequest" /> </ItemGroup> <ItemGroup> <Compile Include="..\Grpc.Core\Version.cs"> @@ -60,5 +101,13 @@ </ItemGroup> <ItemGroup> <None Include="app.config" /> + <None Include="packages.config" /> </ItemGroup> + <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> + </Target> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Client/packages.config b/src/csharp/Grpc.IntegrationTesting.Client/packages.config new file mode 100644 index 0000000000..7a02c95db9 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/packages.config @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="BouncyCastle" version="1.7.0" targetFramework="net45" /> + <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" /> + <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" /> + <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" /> + <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" /> + <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> + <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> + <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj index 949ad61375..1eadbeb920 100644 --- a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj +++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj @@ -9,6 +9,7 @@ <AssemblyName>Grpc.IntegrationTesting.Server</AssemblyName> <StartupObject>Grpc.IntegrationTesting.Server.Program</StartupObject> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <NuGetPackageImportStamp>d9ee8e52</NuGetPackageImportStamp> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -38,7 +39,47 @@ <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> + <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> + </Reference> + <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath> + </Reference> + <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> + </Reference> + <Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Threading.Tasks.Extensions"> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> + </Reference> + <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> + </Reference> <Reference Include="System" /> + <Reference Include="System.Net" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.Extensions"> + <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath> + </Reference> + <Reference Include="System.Net.Http.Primitives"> + <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath> + </Reference> + <Reference Include="System.Net.Http.WebRequest" /> </ItemGroup> <ItemGroup> <Compile Include="..\Grpc.Core\Version.cs"> @@ -60,5 +101,13 @@ </ItemGroup> <ItemGroup> <None Include="app.config" /> + <None Include="packages.config" /> </ItemGroup> + <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> + </Target> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Server/packages.config b/src/csharp/Grpc.IntegrationTesting.Server/packages.config new file mode 100644 index 0000000000..7a02c95db9 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/packages.config @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="BouncyCastle" version="1.7.0" targetFramework="net45" /> + <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" /> + <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" /> + <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" /> + <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" /> + <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> + <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> + <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/Empty.cs b/src/csharp/Grpc.IntegrationTesting/Empty.cs index 28c28c9afd..9bf2b8f7cf 100644 --- a/src/csharp/Grpc.IntegrationTesting/Empty.cs +++ b/src/csharp/Grpc.IntegrationTesting/Empty.cs @@ -1,5 +1,5 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! -// source: empty.proto +// source: test/proto/empty.proto #pragma warning disable 1591, 0612, 3021 #region Designer generated code @@ -23,7 +23,8 @@ namespace Grpc.Testing { static Empty() { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( - "CgtlbXB0eS5wcm90bxIMZ3JwYy50ZXN0aW5nIgcKBUVtcHR5YgZwcm90bzM=")); + "ChZ0ZXN0L3Byb3RvL2VtcHR5LnByb3RvEgxncnBjLnRlc3RpbmciBwoFRW1w", + "dHliBnByb3RvMw==")); descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] { diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index a5945be922..2b75305731 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -42,6 +42,9 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> </Reference> + <Reference Include="CommandLine"> + <HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath> + </Reference> <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath> @@ -93,6 +96,7 @@ <Compile Include="Empty.cs" /> <Compile Include="Messages.cs" /> <Compile Include="InteropClientServerTest.cs" /> + <Compile Include="MetadataCredentialsTest.cs" /> <Compile Include="TestServiceImpl.cs" /> <Compile Include="InteropServer.cs" /> <Compile Include="InteropClient.cs" /> @@ -115,9 +119,6 @@ <ItemGroup> <None Include="app.config" /> <None Include="packages.config" /> - <None Include="proto\test.proto" /> - <None Include="proto\empty.proto" /> - <None Include="proto\messages.proto" /> <None Include="data\README"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index 8343e54122..5eec11abf7 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -33,36 +33,69 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using CommandLine; +using CommandLine.Text; using Google.Apis.Auth.OAuth2; using Google.Protobuf; using Grpc.Auth; using Grpc.Core; using Grpc.Core.Utils; using Grpc.Testing; +using Newtonsoft.Json.Linq; using NUnit.Framework; namespace Grpc.IntegrationTesting { public class InteropClient { - private const string ServiceAccountUser = "155450119199-3psnrh1sdr3d8cpj1v46naggf81mhdnk@developer.gserviceaccount.com"; - private const string ComputeEngineUser = "155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel@developer.gserviceaccount.com"; - private const string AuthScope = "https://www.googleapis.com/auth/xapi.zoo"; - private const string AuthScopeResponse = "xapi.zoo"; - private class ClientOptions { - public bool help; - public string serverHost = "127.0.0.1"; - public string serverHostOverride = TestCredentials.DefaultHostOverride; - public int? serverPort; - public string testCase = "large_unary"; - public bool useTls; - public bool useTestCa; + [Option("server_host", DefaultValue = "127.0.0.1")] + public string ServerHost { get; set; } + + [Option("server_host_override", DefaultValue = TestCredentials.DefaultHostOverride)] + public string ServerHostOverride { get; set; } + + [Option("server_port", Required = true)] + public int ServerPort { get; set; } + + [Option("test_case", DefaultValue = "large_unary")] + public string TestCase { get; set; } + + // Deliberately using nullable bool type to allow --use_tls=true syntax (as opposed to --use_tls) + [Option("use_tls", DefaultValue = false)] + public bool? UseTls { get; set; } + + // Deliberately using nullable bool type to allow --use_test_ca=true syntax (as opposed to --use_test_ca) + [Option("use_test_ca", DefaultValue = false)] + public bool? UseTestCa { get; set; } + + [Option("default_service_account", Required = false)] + public string DefaultServiceAccount { get; set; } + + [Option("oauth_scope", Required = false)] + public string OAuthScope { get; set; } + + [Option("service_account_key_file", Required = false)] + public string ServiceAccountKeyFile { get; set; } + + [HelpOption] + public string GetUsage() + { + var help = new HelpText + { + Heading = "gRPC C# interop testing client", + AddDashesToOption = true + }; + help.AddPreOptionsLine("Usage:"); + help.AddOptions(this); + return help; + } } ClientOptions options; @@ -74,26 +107,9 @@ namespace Grpc.IntegrationTesting public static void Run(string[] args) { - Console.WriteLine("gRPC C# interop testing client"); - ClientOptions options = ParseArguments(args); - - if (options.serverHost == null || !options.serverPort.HasValue || options.testCase == null) - { - Console.WriteLine("Missing required argument."); - Console.WriteLine(); - options.help = true; - } - - if (options.help) + var options = new ClientOptions(); + if (!Parser.Default.ParseArguments(args, options)) { - Console.WriteLine("Usage:"); - Console.WriteLine(" --server_host=HOSTNAME"); - Console.WriteLine(" --server_host_override=HOSTNAME"); - Console.WriteLine(" --server_port=PORT"); - Console.WriteLine(" --test_case=TESTCASE"); - Console.WriteLine(" --use_tls=BOOLEAN"); - Console.WriteLine(" --use_test_ca=BOOLEAN"); - Console.WriteLine(); Environment.Exit(1); } @@ -103,30 +119,49 @@ namespace Grpc.IntegrationTesting private async Task Run() { - Credentials credentials = null; - if (options.useTls) - { - credentials = TestCredentials.CreateTestClientCredentials(options.useTestCa); - } - + var credentials = await CreateCredentialsAsync(); + List<ChannelOption> channelOptions = null; - if (!string.IsNullOrEmpty(options.serverHostOverride)) + if (!string.IsNullOrEmpty(options.ServerHostOverride)) { channelOptions = new List<ChannelOption> { - new ChannelOption(ChannelOptions.SslTargetNameOverride, options.serverHostOverride) + new ChannelOption(ChannelOptions.SslTargetNameOverride, options.ServerHostOverride) }; } - - var channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions); + var channel = new Channel(options.ServerHost, options.ServerPort, credentials, channelOptions); TestService.TestServiceClient client = new TestService.TestServiceClient(channel); - await RunTestCaseAsync(options.testCase, client); - channel.ShutdownAsync().Wait(); + await RunTestCaseAsync(client, options); + await channel.ShutdownAsync(); + } + + private async Task<ChannelCredentials> CreateCredentialsAsync() + { + var credentials = ChannelCredentials.Insecure; + if (options.UseTls.Value) + { + credentials = options.UseTestCa.Value ? TestCredentials.CreateSslCredentials() : new SslCredentials(); + } + + if (options.TestCase == "jwt_token_creds") + { + var googleCredential = await GoogleCredential.GetApplicationDefaultAsync(); + Assert.IsTrue(googleCredential.IsCreateScopedRequired); + credentials = ChannelCredentials.Create(credentials, googleCredential.ToCallCredentials()); + } + + if (options.TestCase == "compute_engine_creds") + { + var googleCredential = await GoogleCredential.GetApplicationDefaultAsync(); + Assert.IsFalse(googleCredential.IsCreateScopedRequired); + credentials = ChannelCredentials.Create(credentials, googleCredential.ToCallCredentials()); + } + return credentials; } - private async Task RunTestCaseAsync(string testCase, TestService.TestServiceClient client) + private async Task RunTestCaseAsync(TestService.TestServiceClient client, ClientOptions options) { - switch (testCase) + switch (options.TestCase) { case "empty_unary": RunEmptyUnary(client); @@ -146,20 +181,17 @@ namespace Grpc.IntegrationTesting case "empty_stream": await RunEmptyStreamAsync(client); break; - case "service_account_creds": - await RunServiceAccountCredsAsync(client); - break; case "compute_engine_creds": - await RunComputeEngineCredsAsync(client); + RunComputeEngineCreds(client, options.DefaultServiceAccount, options.OAuthScope); break; case "jwt_token_creds": - await RunJwtTokenCredsAsync(client); + RunJwtTokenCreds(client); break; case "oauth2_auth_token": - await RunOAuth2AuthTokenAsync(client); + await RunOAuth2AuthTokenAsync(client, options.OAuthScope); break; case "per_rpc_creds": - await RunPerRpcCredsAsync(client); + await RunPerRpcCredsAsync(client, options.OAuthScope); break; case "cancel_after_begin": await RunCancelAfterBeginAsync(client); @@ -174,7 +206,7 @@ namespace Grpc.IntegrationTesting RunBenchmarkEmptyUnary(client); break; default: - throw new ArgumentException("Unknown test case " + testCase); + throw new ArgumentException("Unknown test case " + options.TestCase); } } @@ -313,38 +345,10 @@ namespace Grpc.IntegrationTesting Console.WriteLine("Passed!"); } - public static async Task RunServiceAccountCredsAsync(TestService.TestServiceClient client) - { - Console.WriteLine("running service_account_creds"); - var credential = await GoogleCredential.GetApplicationDefaultAsync(); - credential = credential.CreateScoped(new[] { AuthScope }); - client.HeaderInterceptor = AuthInterceptors.FromCredential(credential); - - var request = new SimpleRequest - { - ResponseType = PayloadType.COMPRESSABLE, - ResponseSize = 314159, - Payload = CreateZerosPayload(271828), - FillUsername = true, - FillOauthScope = true - }; - - var response = client.UnaryCall(request); - - Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); - Assert.AreEqual(314159, response.Payload.Body.Length); - Assert.AreEqual(AuthScopeResponse, response.OauthScope); - Assert.AreEqual(ServiceAccountUser, response.Username); - Console.WriteLine("Passed!"); - } - - public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client) + public static void RunComputeEngineCreds(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope) { Console.WriteLine("running compute_engine_creds"); - var credential = await GoogleCredential.GetApplicationDefaultAsync(); - Assert.IsFalse(credential.IsCreateScopedRequired); - client.HeaderInterceptor = AuthInterceptors.FromCredential(credential); - + var request = new SimpleRequest { ResponseType = PayloadType.COMPRESSABLE, @@ -354,81 +358,73 @@ namespace Grpc.IntegrationTesting FillOauthScope = true }; + // not setting credentials here because they were set on channel already var response = client.UnaryCall(request); Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); Assert.AreEqual(314159, response.Payload.Body.Length); - Assert.AreEqual(AuthScopeResponse, response.OauthScope); - Assert.AreEqual(ComputeEngineUser, response.Username); + Assert.False(string.IsNullOrEmpty(response.OauthScope)); + Assert.True(oauthScope.Contains(response.OauthScope)); + Assert.AreEqual(defaultServiceAccount, response.Username); Console.WriteLine("Passed!"); } - public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client) + public static void RunJwtTokenCreds(TestService.TestServiceClient client) { Console.WriteLine("running jwt_token_creds"); - var credential = await GoogleCredential.GetApplicationDefaultAsync(); - // check this a credential with scope support, but don't add the scope. - Assert.IsTrue(credential.IsCreateScopedRequired); - client.HeaderInterceptor = AuthInterceptors.FromCredential(credential); - + var request = new SimpleRequest { ResponseType = PayloadType.COMPRESSABLE, ResponseSize = 314159, Payload = CreateZerosPayload(271828), FillUsername = true, - FillOauthScope = true }; + // not setting credentials here because they were set on channel already var response = client.UnaryCall(request); Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); Assert.AreEqual(314159, response.Payload.Body.Length); - Assert.AreEqual(ServiceAccountUser, response.Username); + Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username); Console.WriteLine("Passed!"); } - public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client) + public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client, string oauthScope) { Console.WriteLine("running oauth2_auth_token"); - ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope }); + ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope }); string oauth2Token = await credential.GetAccessTokenForRequestAsync(); - client.HeaderInterceptor = AuthInterceptors.FromAccessToken(oauth2Token); - + var credentials = GoogleGrpcCredentials.FromAccessToken(oauth2Token); var request = new SimpleRequest { FillUsername = true, FillOauthScope = true }; - var response = client.UnaryCall(request); + var response = client.UnaryCall(request, new CallOptions(credentials: credentials)); - Assert.AreEqual(AuthScopeResponse, response.OauthScope); - Assert.AreEqual(ServiceAccountUser, response.Username); + Assert.False(string.IsNullOrEmpty(response.OauthScope)); + Assert.True(oauthScope.Contains(response.OauthScope)); + Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username); Console.WriteLine("Passed!"); } - public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client) + public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client, string oauthScope) { Console.WriteLine("running per_rpc_creds"); + ITokenAccess googleCredential = await GoogleCredential.GetApplicationDefaultAsync(); - ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope }); - string oauth2Token = await credential.GetAccessTokenForRequestAsync(); - var headerInterceptor = AuthInterceptors.FromAccessToken(oauth2Token); - + var credentials = googleCredential.ToCallCredentials(); var request = new SimpleRequest { FillUsername = true, - FillOauthScope = true }; - var headers = new Metadata(); - headerInterceptor(null, "", headers); - var response = client.UnaryCall(request, headers: headers); + var response = client.UnaryCall(request, new CallOptions(credentials: credentials)); - Assert.AreEqual(AuthScopeResponse, response.OauthScope); - Assert.AreEqual(ServiceAccountUser, response.Username); + Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username); Console.WriteLine("Passed!"); } @@ -509,67 +505,16 @@ namespace Grpc.IntegrationTesting return new Payload { Body = ByteString.CopyFrom(new byte[size]) }; } - private static ClientOptions ParseArguments(string[] args) - { - var options = new ClientOptions(); - foreach (string arg in args) - { - ParseArgument(arg, options); - if (options.help) - { - break; - } - } - return options; - } - - private static void ParseArgument(string arg, ClientOptions options) + // extracts the client_email field from service account file used for auth test cases + private static string GetEmailFromServiceAccountFile() { - Match match; - match = Regex.Match(arg, "--server_host=(.*)"); - if (match.Success) - { - options.serverHost = match.Groups[1].Value.Trim(); - return; - } - - match = Regex.Match(arg, "--server_host_override=(.*)"); - if (match.Success) - { - options.serverHostOverride = match.Groups[1].Value.Trim(); - return; - } - - match = Regex.Match(arg, "--server_port=(.*)"); - if (match.Success) - { - options.serverPort = int.Parse(match.Groups[1].Value.Trim()); - return; - } - - match = Regex.Match(arg, "--test_case=(.*)"); - if (match.Success) - { - options.testCase = match.Groups[1].Value.Trim(); - return; - } - - match = Regex.Match(arg, "--use_tls=(.*)"); - if (match.Success) - { - options.useTls = bool.Parse(match.Groups[1].Value.Trim()); - return; - } - - match = Regex.Match(arg, "--use_test_ca=(.*)"); - if (match.Success) - { - options.useTestCa = bool.Parse(match.Groups[1].Value.Trim()); - return; - } + string keyFile = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS"); + Assert.IsNotNull(keyFile); - Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg)); - options.help = true; + var jobject = JObject.Parse(File.ReadAllText(keyFile)); + string email = jobject.GetValue("client_email").Value<string>(); + Assert.IsTrue(email.Length > 0); // spec requires nonempty client email. + return email; } } } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index 7bc17a207f..837ae74c45 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -59,7 +59,7 @@ namespace Grpc.IntegrationTesting server = new Server { Services = { TestService.BindService(new TestServiceImpl()) }, - Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateTestServerCredentials() } } + Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } } }; server.Start(); @@ -68,7 +68,7 @@ namespace Grpc.IntegrationTesting new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; int port = server.Ports.Single().BoundPort; - channel = new Channel(Host, port, TestCredentials.CreateTestClientCredentials(true), options); + channel = new Channel(Host, port, TestCredentials.CreateSslCredentials(), options); client = TestService.NewClient(channel); } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs index 718278f30a..cd47e31c2b 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -37,6 +37,9 @@ using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; + +using CommandLine; +using CommandLine.Text; using Grpc.Core; using Grpc.Core.Utils; using Grpc.Testing; @@ -48,9 +51,25 @@ namespace Grpc.IntegrationTesting { private class ServerOptions { - public bool help; - public int? port = 8070; - public bool useTls; + [Option("port", DefaultValue = 8070)] + public int Port { get; set; } + + // Deliberately using nullable bool type to allow --use_tls=true syntax (as opposed to --use_tls) + [Option("use_tls", DefaultValue = false)] + public bool? UseTls { get; set; } + + [HelpOption] + public string GetUsage() + { + var help = new HelpText + { + Heading = "gRPC C# interop testing server", + AddDashesToOption = true + }; + help.AddPreOptionsLine("Usage:"); + help.AddOptions(this); + return help; + } } ServerOptions options; @@ -62,22 +81,9 @@ namespace Grpc.IntegrationTesting public static void Run(string[] args) { - Console.WriteLine("gRPC C# interop testing server"); - ServerOptions options = ParseArguments(args); - - if (!options.port.HasValue) - { - Console.WriteLine("Missing required argument."); - Console.WriteLine(); - options.help = true; - } - - if (options.help) + var options = new ServerOptions(); + if (!Parser.Default.ParseArguments(args, options)) { - Console.WriteLine("Usage:"); - Console.WriteLine(" --port=PORT"); - Console.WriteLine(" --use_tls=BOOLEAN"); - Console.WriteLine(); Environment.Exit(1); } @@ -93,54 +99,19 @@ namespace Grpc.IntegrationTesting }; string host = "0.0.0.0"; - int port = options.port.Value; - if (options.useTls) + int port = options.Port; + if (options.UseTls.Value) { - server.Ports.Add(host, port, TestCredentials.CreateTestServerCredentials()); + server.Ports.Add(host, port, TestCredentials.CreateSslServerCredentials()); } else { - server.Ports.Add(host, options.port.Value, ServerCredentials.Insecure); + server.Ports.Add(host, options.Port, ServerCredentials.Insecure); } Console.WriteLine("Running server on " + string.Format("{0}:{1}", host, port)); server.Start(); server.ShutdownTask.Wait(); } - - private static ServerOptions ParseArguments(string[] args) - { - var options = new ServerOptions(); - foreach (string arg in args) - { - ParseArgument(arg, options); - if (options.help) - { - break; - } - } - return options; - } - - private static void ParseArgument(string arg, ServerOptions options) - { - Match match; - match = Regex.Match(arg, "--port=(.*)"); - if (match.Success) - { - options.port = int.Parse(match.Groups[1].Value.Trim()); - return; - } - - match = Regex.Match(arg, "--use_tls=(.*)"); - if (match.Success) - { - options.useTls = bool.Parse(match.Groups[1].Value.Trim()); - return; - } - - Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg)); - options.help = true; - } } } diff --git a/src/csharp/Grpc.IntegrationTesting/Messages.cs b/src/csharp/Grpc.IntegrationTesting/Messages.cs index a3cbb7d76e..51a56f067b 100644 --- a/src/csharp/Grpc.IntegrationTesting/Messages.cs +++ b/src/csharp/Grpc.IntegrationTesting/Messages.cs @@ -1,5 +1,5 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! -// source: messages.proto +// source: test/proto/messages.proto #pragma warning disable 1591, 0612, 3021 #region Designer generated code @@ -21,37 +21,48 @@ namespace Grpc.Testing { static Messages() { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( - "Cg5tZXNzYWdlcy5wcm90bxIMZ3JwYy50ZXN0aW5nIkAKB1BheWxvYWQSJwoE", - "dHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXlsb2FkVHlwZRIMCgRib2R5", - "GAIgASgMIrEBCg1TaW1wbGVSZXF1ZXN0EjAKDXJlc3BvbnNlX3R5cGUYASAB", - "KA4yGS5ncnBjLnRlc3RpbmcuUGF5bG9hZFR5cGUSFQoNcmVzcG9uc2Vfc2l6", - "ZRgCIAEoBRImCgdwYXlsb2FkGAMgASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxv", - "YWQSFQoNZmlsbF91c2VybmFtZRgEIAEoCBIYChBmaWxsX29hdXRoX3Njb3Bl", - "GAUgASgIIl8KDlNpbXBsZVJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5n", - "cnBjLnRlc3RpbmcuUGF5bG9hZBIQCgh1c2VybmFtZRgCIAEoCRITCgtvYXV0", - "aF9zY29wZRgDIAEoCSJDChlTdHJlYW1pbmdJbnB1dENhbGxSZXF1ZXN0EiYK", - "B3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3RpbmcuUGF5bG9hZCI9ChpTdHJl", - "YW1pbmdJbnB1dENhbGxSZXNwb25zZRIfChdhZ2dyZWdhdGVkX3BheWxvYWRf", - "c2l6ZRgBIAEoBSI3ChJSZXNwb25zZVBhcmFtZXRlcnMSDAoEc2l6ZRgBIAEo", - "BRITCgtpbnRlcnZhbF91cxgCIAEoBSK1AQoaU3RyZWFtaW5nT3V0cHV0Q2Fs", - "bFJlcXVlc3QSMAoNcmVzcG9uc2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGlu", - "Zy5QYXlsb2FkVHlwZRI9ChNyZXNwb25zZV9wYXJhbWV0ZXJzGAIgAygLMiAu", - "Z3JwYy50ZXN0aW5nLlJlc3BvbnNlUGFyYW1ldGVycxImCgdwYXlsb2FkGAMg", - "ASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxvYWQiRQobU3RyZWFtaW5nT3V0cHV0", - "Q2FsbFJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3Rpbmcu", - "UGF5bG9hZCo/CgtQYXlsb2FkVHlwZRIQCgxDT01QUkVTU0FCTEUQABISCg5V", - "TkNPTVBSRVNTQUJMRRABEgoKBlJBTkRPTRACYgZwcm90bzM=")); + "Chl0ZXN0L3Byb3RvL21lc3NhZ2VzLnByb3RvEgxncnBjLnRlc3RpbmciQAoH", + "UGF5bG9hZBInCgR0eXBlGAEgASgOMhkuZ3JwYy50ZXN0aW5nLlBheWxvYWRU", + "eXBlEgwKBGJvZHkYAiABKAwiKwoKRWNob1N0YXR1cxIMCgRjb2RlGAEgASgF", + "Eg8KB21lc3NhZ2UYAiABKAkioQIKDVNpbXBsZVJlcXVlc3QSMAoNcmVzcG9u", + "c2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXlsb2FkVHlwZRIVCg1y", + "ZXNwb25zZV9zaXplGAIgASgFEiYKB3BheWxvYWQYAyABKAsyFS5ncnBjLnRl", + "c3RpbmcuUGF5bG9hZBIVCg1maWxsX3VzZXJuYW1lGAQgASgIEhgKEGZpbGxf", + "b2F1dGhfc2NvcGUYBSABKAgSOwoUcmVzcG9uc2VfY29tcHJlc3Npb24YBiAB", + "KA4yHS5ncnBjLnRlc3RpbmcuQ29tcHJlc3Npb25UeXBlEjEKD3Jlc3BvbnNl", + "X3N0YXR1cxgHIAEoCzIYLmdycGMudGVzdGluZy5FY2hvU3RhdHVzIl8KDlNp", + "bXBsZVJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3Rpbmcu", + "UGF5bG9hZBIQCgh1c2VybmFtZRgCIAEoCRITCgtvYXV0aF9zY29wZRgDIAEo", + "CSJDChlTdHJlYW1pbmdJbnB1dENhbGxSZXF1ZXN0EiYKB3BheWxvYWQYASAB", + "KAsyFS5ncnBjLnRlc3RpbmcuUGF5bG9hZCI9ChpTdHJlYW1pbmdJbnB1dENh", + "bGxSZXNwb25zZRIfChdhZ2dyZWdhdGVkX3BheWxvYWRfc2l6ZRgBIAEoBSI3", + "ChJSZXNwb25zZVBhcmFtZXRlcnMSDAoEc2l6ZRgBIAEoBRITCgtpbnRlcnZh", + "bF91cxgCIAEoBSKlAgoaU3RyZWFtaW5nT3V0cHV0Q2FsbFJlcXVlc3QSMAoN", + "cmVzcG9uc2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXlsb2FkVHlw", + "ZRI9ChNyZXNwb25zZV9wYXJhbWV0ZXJzGAIgAygLMiAuZ3JwYy50ZXN0aW5n", + "LlJlc3BvbnNlUGFyYW1ldGVycxImCgdwYXlsb2FkGAMgASgLMhUuZ3JwYy50", + "ZXN0aW5nLlBheWxvYWQSOwoUcmVzcG9uc2VfY29tcHJlc3Npb24YBiABKA4y", + "HS5ncnBjLnRlc3RpbmcuQ29tcHJlc3Npb25UeXBlEjEKD3Jlc3BvbnNlX3N0", + "YXR1cxgHIAEoCzIYLmdycGMudGVzdGluZy5FY2hvU3RhdHVzIkUKG1N0cmVh", + "bWluZ091dHB1dENhbGxSZXNwb25zZRImCgdwYXlsb2FkGAEgASgLMhUuZ3Jw", + "Yy50ZXN0aW5nLlBheWxvYWQiMwoNUmVjb25uZWN0SW5mbxIOCgZwYXNzZWQY", + "ASABKAgSEgoKYmFja29mZl9tcxgCIAMoBSo/CgtQYXlsb2FkVHlwZRIQCgxD", + "T01QUkVTU0FCTEUQABISCg5VTkNPTVBSRVNTQUJMRRABEgoKBlJBTkRPTRAC", + "KjIKD0NvbXByZXNzaW9uVHlwZRIICgROT05FEAASCAoER1pJUBABEgsKB0RF", + "RkxBVEUQAmIGcHJvdG8z")); descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, new pbr::FileDescriptor[] { }, - new pbr::GeneratedCodeInfo(new[] {typeof(global::Grpc.Testing.PayloadType), }, new pbr::GeneratedCodeInfo[] { + new pbr::GeneratedCodeInfo(new[] {typeof(global::Grpc.Testing.PayloadType), typeof(global::Grpc.Testing.CompressionType), }, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.Payload), new[]{ "Type", "Body" }, null, null, null), - new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleRequest), new[]{ "ResponseType", "ResponseSize", "Payload", "FillUsername", "FillOauthScope" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.EchoStatus), new[]{ "Code", "Message" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleRequest), new[]{ "ResponseType", "ResponseSize", "Payload", "FillUsername", "FillOauthScope", "ResponseCompression", "ResponseStatus" }, null, null, null), new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleResponse), new[]{ "Payload", "Username", "OauthScope" }, null, null, null), new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingInputCallRequest), new[]{ "Payload" }, null, null, null), new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingInputCallResponse), new[]{ "AggregatedPayloadSize" }, null, null, null), new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ResponseParameters), new[]{ "Size", "IntervalUs" }, null, null, null), - new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallRequest), new[]{ "ResponseType", "ResponseParameters", "Payload" }, null, null, null), - new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallResponse), new[]{ "Payload" }, null, null, null) + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallRequest), new[]{ "ResponseType", "ResponseParameters", "Payload", "ResponseCompression", "ResponseStatus" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallResponse), new[]{ "Payload" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ReconnectInfo), new[]{ "Passed", "BackoffMs" }, null, null, null) })); } #endregion @@ -64,6 +75,12 @@ namespace Grpc.Testing { RANDOM = 2, } + public enum CompressionType { + NONE = 0, + GZIP = 1, + DEFLATE = 2, + } + #endregion #region Messages @@ -196,12 +213,140 @@ namespace Grpc.Testing { } [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class EchoStatus : pb::IMessage<EchoStatus> { + private static readonly pb::MessageParser<EchoStatus> _parser = new pb::MessageParser<EchoStatus>(() => new EchoStatus()); + public static pb::MessageParser<EchoStatus> Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[1]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public EchoStatus() { + OnConstruction(); + } + + partial void OnConstruction(); + + public EchoStatus(EchoStatus other) : this() { + code_ = other.code_; + message_ = other.message_; + } + + public EchoStatus Clone() { + return new EchoStatus(this); + } + + public const int CodeFieldNumber = 1; + private int code_; + public int Code { + get { return code_; } + set { + code_ = value; + } + } + + public const int MessageFieldNumber = 2; + private string message_ = ""; + public string Message { + get { return message_; } + set { + message_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as EchoStatus); + } + + public bool Equals(EchoStatus other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Code != other.Code) return false; + if (Message != other.Message) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Code != 0) hash ^= Code.GetHashCode(); + if (Message.Length != 0) hash ^= Message.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Code != 0) { + output.WriteRawTag(8); + output.WriteInt32(Code); + } + if (Message.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Message); + } + } + + public int CalculateSize() { + int size = 0; + if (Code != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Code); + } + if (Message.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Message); + } + return size; + } + + public void MergeFrom(EchoStatus other) { + if (other == null) { + return; + } + if (other.Code != 0) { + Code = other.Code; + } + if (other.Message.Length != 0) { + Message = other.Message; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Code = input.ReadInt32(); + break; + } + case 18: { + Message = input.ReadString(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] public sealed partial class SimpleRequest : pb::IMessage<SimpleRequest> { private static readonly pb::MessageParser<SimpleRequest> _parser = new pb::MessageParser<SimpleRequest>(() => new SimpleRequest()); public static pb::MessageParser<SimpleRequest> Parser { get { return _parser; } } public static pbr::MessageDescriptor Descriptor { - get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[1]; } + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[2]; } } pbr::MessageDescriptor pb::IMessage.Descriptor { @@ -220,6 +365,8 @@ namespace Grpc.Testing { Payload = other.payload_ != null ? other.Payload.Clone() : null; fillUsername_ = other.fillUsername_; fillOauthScope_ = other.fillOauthScope_; + responseCompression_ = other.responseCompression_; + ResponseStatus = other.responseStatus_ != null ? other.ResponseStatus.Clone() : null; } public SimpleRequest Clone() { @@ -271,6 +418,24 @@ namespace Grpc.Testing { } } + public const int ResponseCompressionFieldNumber = 6; + private global::Grpc.Testing.CompressionType responseCompression_ = global::Grpc.Testing.CompressionType.NONE; + public global::Grpc.Testing.CompressionType ResponseCompression { + get { return responseCompression_; } + set { + responseCompression_ = value; + } + } + + public const int ResponseStatusFieldNumber = 7; + private global::Grpc.Testing.EchoStatus responseStatus_; + public global::Grpc.Testing.EchoStatus ResponseStatus { + get { return responseStatus_; } + set { + responseStatus_ = value; + } + } + public override bool Equals(object other) { return Equals(other as SimpleRequest); } @@ -287,6 +452,8 @@ namespace Grpc.Testing { if (!object.Equals(Payload, other.Payload)) return false; if (FillUsername != other.FillUsername) return false; if (FillOauthScope != other.FillOauthScope) return false; + if (ResponseCompression != other.ResponseCompression) return false; + if (!object.Equals(ResponseStatus, other.ResponseStatus)) return false; return true; } @@ -297,6 +464,8 @@ namespace Grpc.Testing { if (payload_ != null) hash ^= Payload.GetHashCode(); if (FillUsername != false) hash ^= FillUsername.GetHashCode(); if (FillOauthScope != false) hash ^= FillOauthScope.GetHashCode(); + if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) hash ^= ResponseCompression.GetHashCode(); + if (responseStatus_ != null) hash ^= ResponseStatus.GetHashCode(); return hash; } @@ -325,6 +494,14 @@ namespace Grpc.Testing { output.WriteRawTag(40); output.WriteBool(FillOauthScope); } + if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) { + output.WriteRawTag(48); + output.WriteEnum((int) ResponseCompression); + } + if (responseStatus_ != null) { + output.WriteRawTag(58); + output.WriteMessage(ResponseStatus); + } } public int CalculateSize() { @@ -344,6 +521,12 @@ namespace Grpc.Testing { if (FillOauthScope != false) { size += 1 + 1; } + if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ResponseCompression); + } + if (responseStatus_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(ResponseStatus); + } return size; } @@ -369,6 +552,15 @@ namespace Grpc.Testing { if (other.FillOauthScope != false) { FillOauthScope = other.FillOauthScope; } + if (other.ResponseCompression != global::Grpc.Testing.CompressionType.NONE) { + ResponseCompression = other.ResponseCompression; + } + if (other.responseStatus_ != null) { + if (responseStatus_ == null) { + responseStatus_ = new global::Grpc.Testing.EchoStatus(); + } + ResponseStatus.MergeFrom(other.ResponseStatus); + } } public void MergeFrom(pb::CodedInputStream input) { @@ -401,6 +593,17 @@ namespace Grpc.Testing { FillOauthScope = input.ReadBool(); break; } + case 48: { + responseCompression_ = (global::Grpc.Testing.CompressionType) input.ReadEnum(); + break; + } + case 58: { + if (responseStatus_ == null) { + responseStatus_ = new global::Grpc.Testing.EchoStatus(); + } + input.ReadMessage(responseStatus_); + break; + } } } } @@ -413,7 +616,7 @@ namespace Grpc.Testing { public static pb::MessageParser<SimpleResponse> Parser { get { return _parser; } } public static pbr::MessageDescriptor Descriptor { - get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[2]; } + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[3]; } } pbr::MessageDescriptor pb::IMessage.Descriptor { @@ -573,7 +776,7 @@ namespace Grpc.Testing { public static pb::MessageParser<StreamingInputCallRequest> Parser { get { return _parser; } } public static pbr::MessageDescriptor Descriptor { - get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[3]; } + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[4]; } } pbr::MessageDescriptor pb::IMessage.Descriptor { @@ -681,7 +884,7 @@ namespace Grpc.Testing { public static pb::MessageParser<StreamingInputCallResponse> Parser { get { return _parser; } } public static pbr::MessageDescriptor Descriptor { - get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[4]; } + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[5]; } } pbr::MessageDescriptor pb::IMessage.Descriptor { @@ -783,7 +986,7 @@ namespace Grpc.Testing { public static pb::MessageParser<ResponseParameters> Parser { get { return _parser; } } public static pbr::MessageDescriptor Descriptor { - get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[5]; } + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[6]; } } pbr::MessageDescriptor pb::IMessage.Descriptor { @@ -911,7 +1114,7 @@ namespace Grpc.Testing { public static pb::MessageParser<StreamingOutputCallRequest> Parser { get { return _parser; } } public static pbr::MessageDescriptor Descriptor { - get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[6]; } + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[7]; } } pbr::MessageDescriptor pb::IMessage.Descriptor { @@ -928,6 +1131,8 @@ namespace Grpc.Testing { responseType_ = other.responseType_; responseParameters_ = other.responseParameters_.Clone(); Payload = other.payload_ != null ? other.Payload.Clone() : null; + responseCompression_ = other.responseCompression_; + ResponseStatus = other.responseStatus_ != null ? other.ResponseStatus.Clone() : null; } public StreamingOutputCallRequest Clone() { @@ -960,6 +1165,24 @@ namespace Grpc.Testing { } } + public const int ResponseCompressionFieldNumber = 6; + private global::Grpc.Testing.CompressionType responseCompression_ = global::Grpc.Testing.CompressionType.NONE; + public global::Grpc.Testing.CompressionType ResponseCompression { + get { return responseCompression_; } + set { + responseCompression_ = value; + } + } + + public const int ResponseStatusFieldNumber = 7; + private global::Grpc.Testing.EchoStatus responseStatus_; + public global::Grpc.Testing.EchoStatus ResponseStatus { + get { return responseStatus_; } + set { + responseStatus_ = value; + } + } + public override bool Equals(object other) { return Equals(other as StreamingOutputCallRequest); } @@ -974,6 +1197,8 @@ namespace Grpc.Testing { if (ResponseType != other.ResponseType) return false; if(!responseParameters_.Equals(other.responseParameters_)) return false; if (!object.Equals(Payload, other.Payload)) return false; + if (ResponseCompression != other.ResponseCompression) return false; + if (!object.Equals(ResponseStatus, other.ResponseStatus)) return false; return true; } @@ -982,6 +1207,8 @@ namespace Grpc.Testing { if (ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) hash ^= ResponseType.GetHashCode(); hash ^= responseParameters_.GetHashCode(); if (payload_ != null) hash ^= Payload.GetHashCode(); + if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) hash ^= ResponseCompression.GetHashCode(); + if (responseStatus_ != null) hash ^= ResponseStatus.GetHashCode(); return hash; } @@ -999,6 +1226,14 @@ namespace Grpc.Testing { output.WriteRawTag(26); output.WriteMessage(Payload); } + if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) { + output.WriteRawTag(48); + output.WriteEnum((int) ResponseCompression); + } + if (responseStatus_ != null) { + output.WriteRawTag(58); + output.WriteMessage(ResponseStatus); + } } public int CalculateSize() { @@ -1010,6 +1245,12 @@ namespace Grpc.Testing { if (payload_ != null) { size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload); } + if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ResponseCompression); + } + if (responseStatus_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(ResponseStatus); + } return size; } @@ -1027,6 +1268,15 @@ namespace Grpc.Testing { } Payload.MergeFrom(other.Payload); } + if (other.ResponseCompression != global::Grpc.Testing.CompressionType.NONE) { + ResponseCompression = other.ResponseCompression; + } + if (other.responseStatus_ != null) { + if (responseStatus_ == null) { + responseStatus_ = new global::Grpc.Testing.EchoStatus(); + } + ResponseStatus.MergeFrom(other.ResponseStatus); + } } public void MergeFrom(pb::CodedInputStream input) { @@ -1051,6 +1301,17 @@ namespace Grpc.Testing { input.ReadMessage(payload_); break; } + case 48: { + responseCompression_ = (global::Grpc.Testing.CompressionType) input.ReadEnum(); + break; + } + case 58: { + if (responseStatus_ == null) { + responseStatus_ = new global::Grpc.Testing.EchoStatus(); + } + input.ReadMessage(responseStatus_); + break; + } } } } @@ -1063,7 +1324,7 @@ namespace Grpc.Testing { public static pb::MessageParser<StreamingOutputCallResponse> Parser { get { return _parser; } } public static pbr::MessageDescriptor Descriptor { - get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[7]; } + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[8]; } } pbr::MessageDescriptor pb::IMessage.Descriptor { @@ -1165,6 +1426,127 @@ namespace Grpc.Testing { } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class ReconnectInfo : pb::IMessage<ReconnectInfo> { + private static readonly pb::MessageParser<ReconnectInfo> _parser = new pb::MessageParser<ReconnectInfo>(() => new ReconnectInfo()); + public static pb::MessageParser<ReconnectInfo> Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[9]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public ReconnectInfo() { + OnConstruction(); + } + + partial void OnConstruction(); + + public ReconnectInfo(ReconnectInfo other) : this() { + passed_ = other.passed_; + backoffMs_ = other.backoffMs_.Clone(); + } + + public ReconnectInfo Clone() { + return new ReconnectInfo(this); + } + + public const int PassedFieldNumber = 1; + private bool passed_; + public bool Passed { + get { return passed_; } + set { + passed_ = value; + } + } + + public const int BackoffMsFieldNumber = 2; + private static readonly pb::FieldCodec<int> _repeated_backoffMs_codec + = pb::FieldCodec.ForInt32(18); + private readonly pbc::RepeatedField<int> backoffMs_ = new pbc::RepeatedField<int>(); + public pbc::RepeatedField<int> BackoffMs { + get { return backoffMs_; } + } + + public override bool Equals(object other) { + return Equals(other as ReconnectInfo); + } + + public bool Equals(ReconnectInfo other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Passed != other.Passed) return false; + if(!backoffMs_.Equals(other.backoffMs_)) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Passed != false) hash ^= Passed.GetHashCode(); + hash ^= backoffMs_.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Passed != false) { + output.WriteRawTag(8); + output.WriteBool(Passed); + } + backoffMs_.WriteTo(output, _repeated_backoffMs_codec); + } + + public int CalculateSize() { + int size = 0; + if (Passed != false) { + size += 1 + 1; + } + size += backoffMs_.CalculateSize(_repeated_backoffMs_codec); + return size; + } + + public void MergeFrom(ReconnectInfo other) { + if (other == null) { + return; + } + if (other.Passed != false) { + Passed = other.Passed; + } + backoffMs_.Add(other.backoffMs_); + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Passed = input.ReadBool(); + break; + } + case 18: + case 16: { + backoffMs_.AddEntriesFrom(input, _repeated_backoffMs_codec); + break; + } + } + } + } + + } + #endregion } diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs new file mode 100644 index 0000000000..3d56678b99 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs @@ -0,0 +1,97 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Testing; +using NUnit.Framework; + +namespace Grpc.IntegrationTesting +{ + public class MetadataCredentialsTest + { + const string Host = "localhost"; + Server server; + Channel channel; + TestService.ITestServiceClient client; + + [TestFixtureSetUp] + public void Init() + { + var serverCredentials = new SslServerCredentials(new[] { new KeyCertificatePair(File.ReadAllText(TestCredentials.ServerCertChainPath), File.ReadAllText(TestCredentials.ServerPrivateKeyPath)) }); + server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, serverCredentials } } + }; + server.Start(); + + var options = new List<ChannelOption> + { + new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) + }; + + var asyncAuthInterceptor = new AsyncAuthInterceptor(async (authUri, metadata) => + { + await Task.Delay(100); // make sure the operation is asynchronous. + metadata.Add("authorization", "SECRET_TOKEN"); + }); + + var clientCredentials = ChannelCredentials.Create( + new SslCredentials(File.ReadAllText(TestCredentials.ClientCertAuthorityPath)), + CallCredentials.FromInterceptor(asyncAuthInterceptor)); + channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); + client = TestService.NewClient(channel); + } + + [TestFixtureTearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void MetadataCredentials() + { + var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 }); + Assert.AreEqual(10, response.Payload.Body.Length); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/Test.cs b/src/csharp/Grpc.IntegrationTesting/Test.cs index 466ec57d3d..cf47707058 100644 --- a/src/csharp/Grpc.IntegrationTesting/Test.cs +++ b/src/csharp/Grpc.IntegrationTesting/Test.cs @@ -1,5 +1,5 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! -// source: test.proto +// source: test/proto/test.proto #pragma warning disable 1591, 0612, 3021 #region Designer generated code @@ -21,21 +21,26 @@ namespace Grpc.Testing { static Test() { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( - "Cgp0ZXN0LnByb3RvEgxncnBjLnRlc3RpbmcaC2VtcHR5LnByb3RvGg5tZXNz", - "YWdlcy5wcm90bzK7BAoLVGVzdFNlcnZpY2USNQoJRW1wdHlDYWxsEhMuZ3Jw", - "Yy50ZXN0aW5nLkVtcHR5GhMuZ3JwYy50ZXN0aW5nLkVtcHR5EkYKCVVuYXJ5", - "Q2FsbBIbLmdycGMudGVzdGluZy5TaW1wbGVSZXF1ZXN0GhwuZ3JwYy50ZXN0", - "aW5nLlNpbXBsZVJlc3BvbnNlEmwKE1N0cmVhbWluZ091dHB1dENhbGwSKC5n", - "cnBjLnRlc3RpbmcuU3RyZWFtaW5nT3V0cHV0Q2FsbFJlcXVlc3QaKS5ncnBj", - "LnRlc3RpbmcuU3RyZWFtaW5nT3V0cHV0Q2FsbFJlc3BvbnNlMAESaQoSU3Ry", - "ZWFtaW5nSW5wdXRDYWxsEicuZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ0lucHV0", - "Q2FsbFJlcXVlc3QaKC5ncnBjLnRlc3RpbmcuU3RyZWFtaW5nSW5wdXRDYWxs", - "UmVzcG9uc2UoARJpCg5GdWxsRHVwbGV4Q2FsbBIoLmdycGMudGVzdGluZy5T", - "dHJlYW1pbmdPdXRwdXRDYWxsUmVxdWVzdBopLmdycGMudGVzdGluZy5TdHJl", - "YW1pbmdPdXRwdXRDYWxsUmVzcG9uc2UoATABEmkKDkhhbGZEdXBsZXhDYWxs", - "EiguZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ091dHB1dENhbGxSZXF1ZXN0Giku", - "Z3JwYy50ZXN0aW5nLlN0cmVhbWluZ091dHB1dENhbGxSZXNwb25zZSgBMAFi", - "BnByb3RvMw==")); + "ChV0ZXN0L3Byb3RvL3Rlc3QucHJvdG8SDGdycGMudGVzdGluZxoWdGVzdC9w", + "cm90by9lbXB0eS5wcm90bxoZdGVzdC9wcm90by9tZXNzYWdlcy5wcm90bzK7", + "BAoLVGVzdFNlcnZpY2USNQoJRW1wdHlDYWxsEhMuZ3JwYy50ZXN0aW5nLkVt", + "cHR5GhMuZ3JwYy50ZXN0aW5nLkVtcHR5EkYKCVVuYXJ5Q2FsbBIbLmdycGMu", + "dGVzdGluZy5TaW1wbGVSZXF1ZXN0GhwuZ3JwYy50ZXN0aW5nLlNpbXBsZVJl", + "c3BvbnNlEmwKE1N0cmVhbWluZ091dHB1dENhbGwSKC5ncnBjLnRlc3Rpbmcu", + "U3RyZWFtaW5nT3V0cHV0Q2FsbFJlcXVlc3QaKS5ncnBjLnRlc3RpbmcuU3Ry", + "ZWFtaW5nT3V0cHV0Q2FsbFJlc3BvbnNlMAESaQoSU3RyZWFtaW5nSW5wdXRD", + "YWxsEicuZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ0lucHV0Q2FsbFJlcXVlc3Qa", + "KC5ncnBjLnRlc3RpbmcuU3RyZWFtaW5nSW5wdXRDYWxsUmVzcG9uc2UoARJp", + "Cg5GdWxsRHVwbGV4Q2FsbBIoLmdycGMudGVzdGluZy5TdHJlYW1pbmdPdXRw", + "dXRDYWxsUmVxdWVzdBopLmdycGMudGVzdGluZy5TdHJlYW1pbmdPdXRwdXRD", + "YWxsUmVzcG9uc2UoATABEmkKDkhhbGZEdXBsZXhDYWxsEiguZ3JwYy50ZXN0", + "aW5nLlN0cmVhbWluZ091dHB1dENhbGxSZXF1ZXN0GikuZ3JwYy50ZXN0aW5n", + "LlN0cmVhbWluZ091dHB1dENhbGxSZXNwb25zZSgBMAEyVQoUVW5pbXBsZW1l", + "bnRlZFNlcnZpY2USPQoRVW5pbXBsZW1lbnRlZENhbGwSEy5ncnBjLnRlc3Rp", + "bmcuRW1wdHkaEy5ncnBjLnRlc3RpbmcuRW1wdHkyfwoQUmVjb25uZWN0U2Vy", + "dmljZRIxCgVTdGFydBITLmdycGMudGVzdGluZy5FbXB0eRoTLmdycGMudGVz", + "dGluZy5FbXB0eRI4CgRTdG9wEhMuZ3JwYy50ZXN0aW5nLkVtcHR5GhsuZ3Jw", + "Yy50ZXN0aW5nLlJlY29ubmVjdEluZm9iBnByb3RvMw==")); descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, new pbr::FileDescriptor[] { global::Grpc.Testing.Proto.Empty.Descriptor, global::Grpc.Testing.Messages.Descriptor, }, new pbr::GeneratedCodeInfo(null, null)); diff --git a/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs index 7a48d6e92e..ce108d808b 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs @@ -51,26 +51,15 @@ namespace Grpc.IntegrationTesting public const string DefaultHostOverride = "foo.test.google.fr"; public const string ClientCertAuthorityPath = "data/ca.pem"; - public const string ClientCertAuthorityEnvName = "SSL_CERT_FILE"; - public const string ServerCertChainPath = "data/server1.pem"; public const string ServerPrivateKeyPath = "data/server1.key"; - public static SslCredentials CreateTestClientCredentials(bool useTestCa) + public static SslCredentials CreateSslCredentials() { - string caPath = ClientCertAuthorityPath; - if (!useTestCa) - { - caPath = Environment.GetEnvironmentVariable(ClientCertAuthorityEnvName); - if (string.IsNullOrEmpty(caPath)) - { - throw new ArgumentException("CA path environment variable is not set."); - } - } - return new SslCredentials(File.ReadAllText(caPath)); + return new SslCredentials(File.ReadAllText(ClientCertAuthorityPath)); } - public static SslServerCredentials CreateTestServerCredentials() + public static SslServerCredentials CreateSslServerCredentials() { var keyCertPair = new KeyCertificatePair( File.ReadAllText(ServerCertChainPath), diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs index f63e148475..8c884b7408 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs @@ -1,5 +1,5 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! -// source: test.proto +// source: test/proto/test.proto #region Designer generated code using System; @@ -207,5 +207,191 @@ namespace Grpc.Testing { } } + public static class UnimplementedService + { + static readonly string __ServiceName = "grpc.testing.UnimplementedService"; + + static readonly Marshaller<global::Grpc.Testing.Empty> __Marshaller_Empty = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.Empty.Parser.ParseFrom); + + static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_UnimplementedCall = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>( + MethodType.Unary, + __ServiceName, + "UnimplementedCall", + __Marshaller_Empty, + __Marshaller_Empty); + + // service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Grpc.Testing.Test.Descriptor.Services[1]; } + } + + // client interface + public interface IUnimplementedServiceClient + { + global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options); + AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options); + } + + // server-side interface + public interface IUnimplementedService + { + Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, ServerCallContext context); + } + + // client stub + public class UnimplementedServiceClient : ClientBase, IUnimplementedServiceClient + { + public UnimplementedServiceClient(Channel channel) : base(channel) + { + } + public global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_UnimplementedCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options) + { + var call = CreateCall(__Method_UnimplementedCall, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_UnimplementedCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options) + { + var call = CreateCall(__Method_UnimplementedCall, options); + return Calls.AsyncUnaryCall(call, request); + } + } + + // creates service definition that can be registered with a server + public static ServerServiceDefinition BindService(IUnimplementedService serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(__ServiceName) + .AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build(); + } + + // creates a new client + public static UnimplementedServiceClient NewClient(Channel channel) + { + return new UnimplementedServiceClient(channel); + } + + } + public static class ReconnectService + { + static readonly string __ServiceName = "grpc.testing.ReconnectService"; + + static readonly Marshaller<global::Grpc.Testing.Empty> __Marshaller_Empty = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.Empty.Parser.ParseFrom); + static readonly Marshaller<global::Grpc.Testing.ReconnectInfo> __Marshaller_ReconnectInfo = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.ReconnectInfo.Parser.ParseFrom); + + static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_Start = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>( + MethodType.Unary, + __ServiceName, + "Start", + __Marshaller_Empty, + __Marshaller_Empty); + + static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.ReconnectInfo> __Method_Stop = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.ReconnectInfo>( + MethodType.Unary, + __ServiceName, + "Stop", + __Marshaller_Empty, + __Marshaller_ReconnectInfo); + + // service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Grpc.Testing.Test.Descriptor.Services[2]; } + } + + // client interface + public interface IReconnectServiceClient + { + global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, CallOptions options); + AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, CallOptions options); + global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, CallOptions options); + AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, CallOptions options); + } + + // server-side interface + public interface IReconnectService + { + Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.Empty request, ServerCallContext context); + Task<global::Grpc.Testing.ReconnectInfo> Stop(global::Grpc.Testing.Empty request, ServerCallContext context); + } + + // client stub + public class ReconnectServiceClient : ClientBase, IReconnectServiceClient + { + public ReconnectServiceClient(Channel channel) : base(channel) + { + } + public global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Start, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, CallOptions options) + { + var call = CreateCall(__Method_Start, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Start, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, CallOptions options) + { + var call = CreateCall(__Method_Start, options); + return Calls.AsyncUnaryCall(call, request); + } + public global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Stop, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, CallOptions options) + { + var call = CreateCall(__Method_Stop, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Stop, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, CallOptions options) + { + var call = CreateCall(__Method_Stop, options); + return Calls.AsyncUnaryCall(call, request); + } + } + + // creates service definition that can be registered with a server + public static ServerServiceDefinition BindService(IReconnectService serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(__ServiceName) + .AddMethod(__Method_Start, serviceImpl.Start) + .AddMethod(__Method_Stop, serviceImpl.Stop).Build(); + } + + // creates a new client + public static ReconnectServiceClient NewClient(Channel channel) + { + return new ReconnectServiceClient(channel); + } + + } } #endregion diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config index 8dfded1964..bdb3dadf44 100644 --- a/src/csharp/Grpc.IntegrationTesting/packages.config +++ b/src/csharp/Grpc.IntegrationTesting/packages.config @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="BouncyCastle" version="1.7.0" targetFramework="net45" /> + <package id="CommandLineParser" version="1.9.71" targetFramework="net45" /> <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" /> <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" /> <package id="Google.Protobuf" version="3.0.0-alpha4" targetFramework="net45" /> diff --git a/src/csharp/README.md b/src/csharp/README.md index 3fbc1c5f05..65ae0b5efd 100644 --- a/src/csharp/README.md +++ b/src/csharp/README.md @@ -1,3 +1,4 @@ +[![Nuget](https://img.shields.io/nuget/v/Grpc.svg)](http://www.nuget.org/packages/Grpc/) gRPC C# ======= @@ -6,65 +7,76 @@ A C# implementation of gRPC. Status ------ -Alpha : Ready for early adopters. +Beta -Usage: Windows +PREREQUISITES -------------- -- Prerequisites: .NET Framework 4.5+, Visual Studio 2013 with NuGet extension installed (VS2015 should work). +- Windows: .NET Framework 4.5+, Visual Studio 2013 or 2015. +- Linux: Mono 3.2.8+, MonoDevelop 5.9 with NuGet add-in installed. +- Mac OS X: [homebrew][], Xamarin Studio with NuGet add-in installed. + +HOW TO USE +-------------- + +**Windows** - Open Visual Studio and start a new project/solution. - Add NuGet package `Grpc` as a dependency (Project options -> Manage NuGet Packages). That will also pull all the transitive dependencies (including the native libraries that - gRPC C# is internally using). + gRPC C# is using internally). -- Helloworld project example can be found in https://github.com/grpc/grpc/tree/master/examples/csharp. +**Linux (Debian)** -Usage: Linux (Mono) --------------- +- Add [Debian jessie-backports][] to your `sources.list` file. Example: -- Prerequisites: Mono 3.2.8+, MonoDevelop 5.9 with NuGet add-in installed. - -- Install Linuxbrew and gRPC C Core using instructions in https://github.com/grpc/homebrew-grpc - -- gRPC C# depends on native shared library libgrpc_csharp_ext.so (Unix flavor of grpc_csharp_ext.dll). - This library will be installed to `~/.linuxbrew/lib` by the previous step. - To make it visible to mono, you need to: + ```sh + echo "deb http://http.debian.net/debian jessie-backports main" | \ + sudo tee -a /etc/apt/sources.list + ``` - - (preferred approach) add `libgrpc_csharp_ext.so` to `/etc/ld.so.cache` by running: +- Install the gRPC Debian package - ```sh - $ echo "$HOME/.linuxbrew/lib" | sudo tee /etc/ld.so.conf.d/zzz_brew_lib.conf - $ sudo ldconfig - ``` + ```sh + sudo apt-get update + sudo apt-get install libgrpc0 + ``` - - (adhoc approach) set `LD_LIBRARY_PATH` environment variable to point to directory containing `libgrpc_csharp_ext.so`: +- gRPC C# depends on native shared library `libgrpc_csharp_ext.so` (Unix flavor of grpc_csharp_ext.dll). + This library is not part of the base gRPC debian package and needs to be installed manually from + a `.deb` file. Download the debian package `libgrpc_csharp_ext` from corresponding gRPC release on GitHub + and install it using `dpkg`. - ```sh - $ export LD_LIBRARY_PATH=$HOME/.linuxbrew/lib:${LD_LIBRARY_PATH} - ``` - - (if you are contributor) installing gRPC from sources using `sudo make install_grpc_csharp_ext` also works. + ```sh + # choose version corresponding to the version of libgrpc you've installed. + wget https://github.com/grpc/grpc/releases/download/release-0_11_0/libgrpc-csharp-ext0_0.11.0.0-1_amd64.deb + dpkg -i libgrpc-csharp-ext0_0.11.0.0-1_amd64.deb + ``` - Open MonoDevelop and start a new project/solution. - Add NuGet package `Grpc` as a dependency (Project -> Add NuGet packages). -- Helloworld project example can be found in https://github.com/grpc/grpc/tree/master/examples/csharp. +- NOTE: Currently, there are no debian packages for the latest version Protocol Buffers compiler (_protoc_) + and the gRPC _protoc_ plugin. You can install them using [gRPC Linuxbrew instructions][]. -Usage: MacOS (Mono) --------------- +**Mac OS X** - WARNING: As of now gRPC C# only works on 64bit version of Mono (because we don't compile the native extension for C# in 32bit mode yet). That means your development experience with Xamarin Studio on MacOS will not be great, as you won't be able to run your code directly from Xamarin Studio (which requires 32bit version of Mono). -- Prerequisites: Xamarin Studio with NuGet add-in installed. +- Install [homebrew][]. Run the following command to install gRPC C# native dependencies. -- Install Homebrew and gRPC C Core using instructions in https://github.com/grpc/homebrew-grpc + ```sh + $ curl -fsSL https://goo.gl/getgrpc | bash - + ``` + This will download and run the [gRPC install script][], then install the latest version of gRPC C core and native C# extension. + It also installs Protocol Buffers compiler (_protoc_) and the gRPC _protoc_ plugin for C#. -- Install 64-bit version of mono with command `brew install mono` (assumes you've already installed Homebrew). +- Install 64-bit version of mono with command `brew install mono`. - Open Xamarin Studio and start a new project/solution. @@ -73,35 +85,27 @@ Usage: MacOS (Mono) - *You will be able to build your project in Xamarin Studio, but to run or test it, you will need to run it under 64-bit version of Mono.* -- Helloworld project example can be found in https://github.com/grpc/grpc/tree/master/examples/csharp. - -Building: Windows +BUILD FROM SOURCE ----------------- You only need to go through these steps if you are planning to develop gRPC C#. If you are a user of gRPC C#, go to Usage section above. -- Prerequisites for development: NET Framework 4.5+, Visual Studio 2013 (with NuGet and NUnit extensions installed). +**Windows** -- The grpc_csharp_ext native library needs to be built so you can build the Grpc C# solution. You can +- The grpc_csharp_ext native library needs to be built so you can build the gRPC C# solution. You can either build the native solution in `vsprojects/grpc.sln` from Visual Studio manually, or you can use a convenience batch script that builds everything for you. ``` + > REM From src/csharp directory > buildall.bat ``` -- Open Grpc.sln using Visual Studio 2013. NuGet dependencies will be restored +- Open Grpc.sln using Visual Studio. NuGet dependencies will be restored upon build (you need to have NuGet add-in installed). - -Building: Linux (Mono) ----------------------- - -You only need to go through these steps if you are planning to develop gRPC C#. -If you are a user of gRPC C#, go to Usage section above. - -- Prerequisites for development: Mono 3.2.8+, MonoDevelop 5.9 with NuGet and NUnit add-ins installed. +**Linux** ```sh $ sudo apt-get install mono-devel @@ -123,8 +127,8 @@ don't support NuGet add-in. - Build the solution & run all the tests from test view. -Tests ------ +RUNNING TESTS +------------- gRPC C# is using NUnit as the testing framework. @@ -136,12 +140,25 @@ Then you should be able to run all the test from the Test View. After building the solution, you can also run the tests from command line using nunit-console tool. + ```sh # from Grpc.Core.Test/bin/Debug directory $ nunit-console Grpc.Core.Tests.dll ``` -Contents +gRPC team uses a Python script to simplify facilitate running tests for +different languages. + +``` +tools/run_tests/run_tests.py -l csharp +``` + +DOCUMENTATION +------------- +- the gRPC C# reference documentation is available online at [grpc.io][] +- [Helloworld example][] + +CONTENTS -------- - ext: @@ -159,7 +176,7 @@ Contents - Grpc.IntegrationTesting: Cross-language gRPC implementation testing (interop testing). -Troubleshooting +TROUBLESHOOTING --------------- ### Problem: Unable to load DLL 'grpc_csharp_ext.dll' @@ -170,8 +187,18 @@ Internally, gRPC C# uses a native library written in C (gRPC C core) and invokes - If you are running your application that is using gRPC on Windows machine that doesn't have Visual Studio installed, you might need to install [Visual C++ 2013 redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=40784) that contains some system .dll libraries that `grpc_csharp_ext.dll` depends on (see #905 for more details). -- On Linux (or Docker), you need to first install gRPC C core and `libgrpc_csharp_ext.so` shared libraries. Currently, the libraries can be installed by `make install_grpc_csharp_ext` or using Linuxbrew (a Debian package is coming soon). Installation on a machine where your application is going to be deployed is no different. +- On Linux (or Docker), you need to first install gRPC C core and `libgrpc_csharp_ext.so` shared libraries. + See [How to Use](#how-to-use) section for details how to install it. + Installation on a machine where your application is going to be deployed is no different. -- On Mac, you need to first install gRPC C core and `libgrpc_csharp_ext.dylib` shared libraries using Homebrew. See above for installation instruction. Installation on a machine where your application is going to be deployed is no different. +- On Mac, you need to first install gRPC C core and `libgrpc_csharp_ext.dylib` shared libraries using Homebrew. See above for installation instruction. + Installation on a machine where your application is going to be deployed is no different. - Possible cause for the problem is that the `grpc_csharp_ext` library is installed, but it has different bitness (32/64bit) than your C# runtime (in case you are using mono) or C# application. + +[gRPC Linuxbrew instructions]:https://github.com/grpc/homebrew-grpc#quick-install-linux +[homebrew]:http://brew.sh +[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install +[grpc.io]: http://www.grpc.io/docs/installation/csharp.html +[Debian jessie-backports]:http://backports.debian.org/Instructions/ +[Helloworld example]:../../examples/csharp/helloworld diff --git a/src/csharp/build_packages.bat b/src/csharp/build_packages.bat index 255b7469ab..b864a955a8 100644 --- a/src/csharp/build_packages.bat +++ b/src/csharp/build_packages.bat @@ -1,10 +1,13 @@ @rem Builds gRPC NuGet packages @rem Current package versions -set VERSION=0.7.0 -set CORE_VERSION=0.11.0 +set VERSION=0.7.1 +set CORE_VERSION=0.11.1 set PROTOBUF_VERSION=3.0.0-alpha4 +@rem Packages that depend on prerelease packages (like Google.Protobuf) need to have prerelease suffix as well. +set VERSION_WITH_BETA=%VERSION%-beta + @rem Adjust the location of nuget.exe set NUGET=C:\nuget\nuget.exe @@ -17,10 +20,10 @@ endlocal @call ..\..\vsprojects\build_plugins.bat || goto :error -%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec -Version %CORE_VERSION% || goto :error +%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp.nuspec -Version %CORE_VERSION% || goto :error %NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols -Version %VERSION% || goto :error -%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% -Properties GrpcNativeCsharpExtVersion=%CORE_VERSION% || goto :error -%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION% -Properties ProtobufVersion=%PROTOBUF_VERSION% || goto :error +%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% -Properties GrpcNativeCsharpVersion=%CORE_VERSION% || goto :error +%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION_WITH_BETA% -Properties ProtobufVersion=%PROTOBUF_VERSION% || goto :error %NUGET% pack Grpc.Tools.nuspec -Version %VERSION% || goto :error %NUGET% pack Grpc.nuspec -Version %VERSION% || goto :error diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 70c0fbcc50..679ca43d74 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -68,7 +68,7 @@ grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) { /* * Helper to maintain lifetime of batch op inputs and store batch op outputs. */ -typedef struct gprcsharp_batch_context { +typedef struct grpcsharp_batch_context { grpc_metadata_array send_initial_metadata; grpc_byte_buffer *send_message; struct { @@ -252,7 +252,7 @@ GPR_EXPORT gpr_intptr GPR_CALLTYPE grpcsharp_batch_context_recv_message_length( if (!ctx->recv_message) { return -1; } - return grpc_byte_buffer_length(ctx->recv_message); + return (gpr_intptr)grpc_byte_buffer_length(ctx->recv_message); } /* @@ -665,16 +665,16 @@ grpcsharp_call_start_duplex_streaming(grpc_call *call, } GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_recv_initial_metadata( - grpc_call *call, grpcsharp_batch_context *ctx) { - /* TODO: don't use magic number */ - grpc_op ops[1]; - ops[0].op = GRPC_OP_RECV_INITIAL_METADATA; - ops[0].data.recv_initial_metadata = &(ctx->recv_initial_metadata); - ops[0].flags = 0; - ops[0].reserved = NULL; + grpc_call *call, grpcsharp_batch_context *ctx) { + /* TODO: don't use magic number */ + grpc_op ops[1]; + ops[0].op = GRPC_OP_RECV_INITIAL_METADATA; + ops[0].data.recv_initial_metadata = &(ctx->recv_initial_metadata); + ops[0].flags = 0; + ops[0].reserved = NULL; - return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, - NULL); + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); } GPR_EXPORT grpc_call_error GPR_CALLTYPE @@ -785,6 +785,11 @@ grpcsharp_call_send_initial_metadata(grpc_call *call, NULL); } +GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_set_credentials(grpc_call *call, + grpc_credentials *creds) { + return grpc_call_set_credentials(call, creds); +} + /* Server */ GPR_EXPORT grpc_server *GPR_CALLTYPE @@ -892,6 +897,47 @@ grpcsharp_server_add_secure_http2_port(grpc_server *server, const char *addr, return grpc_server_add_secure_http2_port(server, addr, creds); } +GPR_EXPORT grpc_credentials *GPR_CALLTYPE grpcsharp_composite_credentials_create( + grpc_credentials *creds1, + grpc_credentials *creds2) { + return grpc_composite_credentials_create(creds1, creds2, NULL); +} + +/* Metadata credentials plugin */ + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin( + grpc_credentials_plugin_metadata_cb cb, + void *user_data, grpc_metadata_array *metadata, + grpc_status_code status, const char *error_details) { + cb(user_data, metadata->metadata, metadata->count, status, error_details); +} + +typedef void(GPR_CALLTYPE *grpcsharp_metadata_interceptor_func)( + void *state, const char *service_url, grpc_credentials_plugin_metadata_cb cb, + void *user_data, gpr_int32 is_destroy); + +static void grpcsharp_get_metadata_handler(void *state, const char *service_url, + grpc_credentials_plugin_metadata_cb cb, void *user_data) { + grpcsharp_metadata_interceptor_func interceptor = + (grpcsharp_metadata_interceptor_func)(gpr_intptr)state; + interceptor(state, service_url, cb, user_data, 0); +} + +static void grpcsharp_metadata_credentials_destroy_handler(void *state) { + grpcsharp_metadata_interceptor_func interceptor = + (grpcsharp_metadata_interceptor_func)(gpr_intptr)state; + interceptor(state, NULL, NULL, NULL, 1); +} + +GPR_EXPORT grpc_credentials *GPR_CALLTYPE grpcsharp_metadata_credentials_create_from_plugin( + grpcsharp_metadata_interceptor_func metadata_interceptor) { + grpc_metadata_credentials_plugin plugin; + plugin.get_metadata = grpcsharp_get_metadata_handler; + plugin.destroy = grpcsharp_metadata_credentials_destroy_handler; + plugin.state = (void*)(gpr_intptr)metadata_interceptor; + return grpc_metadata_credentials_create_from_plugin(plugin, NULL); +} + /* Logging */ typedef void(GPR_CALLTYPE *grpcsharp_log_func)(const char *file, gpr_int32 line, diff --git a/src/csharp/generate_proto_csharp.sh b/src/csharp/generate_proto_csharp.sh index a17f45b587..f879e074aa 100755 --- a/src/csharp/generate_proto_csharp.sh +++ b/src/csharp/generate_proto_csharp.sh @@ -42,7 +42,7 @@ $PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \ -I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto $PROTOC --plugin=$PLUGIN --csharp_out=$INTEROP_DIR --grpc_out=$INTEROP_DIR \ - -I $INTEROP_DIR/proto $INTEROP_DIR/proto/*.proto + -I ../.. ../../test/proto/*.proto $PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \ -I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto diff --git a/src/node/LICENSE b/src/node/LICENSE deleted file mode 100644 index 0209b570e1..0000000000 --- a/src/node/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2015, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/node/README.md b/src/node/README.md index 0b97680feb..b46b986243 100644 --- a/src/node/README.md +++ b/src/node/README.md @@ -1,3 +1,4 @@ +[![npm](https://img.shields.io/npm/v/grpc.svg)](https://www.npmjs.com/package/grpc) # Node.js gRPC Library ## Status @@ -5,51 +6,19 @@ Beta ## PREREQUISITES - `node`: This requires `node` to be installed. If you instead have the `nodejs` executable on Debian, you should install the [`nodejs-legacy`](https://packages.debian.org/sid/nodejs-legacy) package. -- [homebrew][] on Mac OS X. These simplify the installation of the gRPC C core. ## INSTALLATION -**Linux (Debian):** - -Add [Debian testing][] to your `sources.list` file. Example: - -```sh -echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" | \ -sudo tee -a /etc/apt/sources.list -``` - -Install the gRPC Debian package - -```sh -sudo apt-get update -sudo apt-get install libgrpc-dev -``` - Install the gRPC NPM package ```sh npm install grpc ``` -**Mac OS X** - -Install [homebrew][]. Run the following command to install gRPC Node.js. -```sh -$ curl -fsSL https://goo.gl/getgrpc | bash -s nodejs -``` -This will download and run the [gRPC install script][], then install the latest version of gRPC Nodejs npm package. - ## BUILD FROM SOURCE 1. Clone [the grpc Git Repository](https://github.com/grpc/grpc). - 2. Follow the instructions in the `INSTALL` file in the root of that repository to install the C core library that this package depends on. 3. Run `npm install`. -If you install the gRPC C core library in a custom location, then you need to set some environment variables to install this library. The command will look like this: - -```sh -CXXFLAGS=-I<custom location>/include LDFLAGS=-L<custom location>/lib npm install [grpc] -``` - ## TESTING To run the test suite, simply run `npm test` in the install location. @@ -110,7 +79,3 @@ ServerCredentials ``` An object with factory methods for creating credential objects for servers. - -[homebrew]:http://brew.sh -[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install -[Debian testing]:https://www.debian.org/releases/stretch/ diff --git a/src/node/bin/README.md b/src/node/bin/README.md deleted file mode 100644 index 2f856e428e..0000000000 --- a/src/node/bin/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Command Line Tools - -# Service Packager - -The command line tool `bin/service_packager`, when called with the following command line: - -```bash -service_packager proto_file -o output_path -n name -v version [-i input_path...] -``` - -Populates `output_path` with a node package consisting of a `package.json` populated with `name` and `version`, an `index.js`, a `LICENSE` file copied from gRPC, and a `service.json`, which is compiled from `proto_file` and the given `input_path`s. `require('output_path')` returns an object that is equivalent to - -```js -{ client: require('grpc').load('service.json'), - auth: require('google-auth-library') } -``` diff --git a/src/node/bin/service_packager b/src/node/bin/service_packager deleted file mode 100755 index c7f2460997..0000000000 --- a/src/node/bin/service_packager +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require(__dirname+'/../cli/service_packager.js').main(process.argv.slice(2));
\ No newline at end of file diff --git a/src/node/binding.gyp b/src/node/binding.gyp deleted file mode 100644 index 734dc8410b..0000000000 --- a/src/node/binding.gyp +++ /dev/null @@ -1,86 +0,0 @@ -{ - "targets" : [ - { - 'include_dirs': [ - "<!(node -e \"require('nan')\")" - ], - 'cflags': [ - '-std=c++0x', - '-Wall', - '-pthread', - '-pedantic', - '-g', - '-zdefs', - '-Werror', - '-Wno-error=deprecated-declarations' - ], - 'ldflags': [ - '-g' - ], - "conditions": [ - ['OS != "win"', { - 'variables': { - 'pkg_config_grpc': '<!(pkg-config --exists grpc >/dev/null 2>&1 && echo true || echo false)' - }, - 'conditions': [ - ['pkg_config_grpc == "true"', { - 'link_settings': { - 'libraries': [ - '<!@(pkg-config --libs-only-l --static grpc)' - ] - }, - 'cflags': [ - '<!@(pkg-config --cflags grpc)' - ], - 'libraries': [ - '<!@(pkg-config --libs-only-L --static grpc)' - ], - 'ldflags': [ - '<!@(pkg-config --libs-only-other --static grpc)' - ] - }, { - 'link_settings': { - 'libraries': [ - '-lpthread', - '-lgrpc', - '-lgpr' - ], - }, - 'conditions':[ - ['OS != "mac"', { - 'link_settings': { - 'libraries': [ - '-lrt' - ] - } - }] - ] - } - ] - ] - }], - ['OS == "mac"', { - 'xcode_settings': { - 'MACOSX_DEPLOYMENT_TARGET': '10.9', - 'OTHER_CFLAGS': [ - '-std=c++11', - '-stdlib=libc++' - ] - } - }] - ], - "target_name": "grpc", - "sources": [ - "ext/byte_buffer.cc", - "ext/call.cc", - "ext/channel.cc", - "ext/completion_queue_async_worker.cc", - "ext/credentials.cc", - "ext/node_grpc.cc", - "ext/server.cc", - "ext/server_credentials.cc", - "ext/timeval.cc" - ] - } - ] -} diff --git a/src/node/cli/service_packager.js b/src/node/cli/service_packager.js deleted file mode 100644 index c92c450a01..0000000000 --- a/src/node/cli/service_packager.js +++ /dev/null @@ -1,142 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -'use strict'; - -var fs = require('fs'); -var path = require('path'); - -var _ = require('lodash'); -var async = require('async'); -var pbjs = require('protobufjs/cli/pbjs'); -var parseArgs = require('minimist'); -var Mustache = require('mustache'); - -var package_json = require('../package.json'); - -var template_path = path.resolve(__dirname, 'service_packager'); - -var package_tpl_path = path.join(template_path, 'package.json.template'); - -var arg_format = { - string: ['include', 'out', 'name', 'version'], - alias: { - include: 'i', - out: 'o', - name: 'n', - version: 'v' - } -}; - -// TODO(mlumish): autogenerate README.md from proto file - -/** - * Render package.json file from template using provided parameters. - * @param {Object} params Map of parameter names to values - * @param {function(Error, string)} callback Callback to pass rendered template - * text to - */ -function generatePackage(params, callback) { - fs.readFile(package_tpl_path, {encoding: 'utf-8'}, function(err, template) { - if (err) { - callback(err); - } else { - var rendered = Mustache.render(template, params); - callback(null, rendered); - } - }); -} - -/** - * Copy a file - * @param {string} src_path The filepath to copy from - * @param {string} dest_path The filepath to copy to - */ -function copyFile(src_path, dest_path) { - fs.createReadStream(src_path).pipe(fs.createWriteStream(dest_path)); -} - -/** - * Run the script. Copies the index.js and LICENSE files to the output path, - * renders the package.json template to the output path, and generates a - * service.json file from the input proto files using pbjs. The arguments are - * taken directly from the command line, and handled as follows: - * -i (--include) : An include path for pbjs (can be dpulicated) - * -o (--output): The output path - * -n (--name): The name of the package - * -v (--version): The package version - * @param {Array} argv The argument vector - */ -function main(argv) { - var args = parseArgs(argv, arg_format); - var out_path = path.resolve(args.out); - var include_dirs = []; - if (args.include) { - include_dirs = _.map(_.flatten([args.include]), function(p) { - return path.resolve(p); - }); - } - args.grpc_version = package_json.version; - generatePackage(args, function(err, rendered) { - if (err) throw err; - fs.writeFile(path.join(out_path, 'package.json'), rendered, function(err) { - if (err) throw err; - }); - }); - copyFile(path.join(template_path, 'index.js'), - path.join(out_path, 'index.js')); - copyFile(path.join(__dirname, '..', 'LICENSE'), - path.join(out_path, 'LICENSE')); - - var service_stream = fs.createWriteStream(path.join(out_path, - 'service.json')); - var pbjs_args = _.flatten(['node', 'pbjs', - args._[0], - '-legacy', - _.map(include_dirs, function(dir) { - return "-path=" + dir; - })]); - var old_stdout = process.stdout; - process.__defineGetter__('stdout', function() { - return service_stream; - }); - var pbjs_status = pbjs.main(pbjs_args); - process.__defineGetter__('stdout', function() { - return old_stdout; - }); - if (pbjs_status !== pbjs.STATUS_OK) { - throw new Error('pbjs failed with status code ' + pbjs_status); - } -} - -exports.main = main; diff --git a/src/node/cli/service_packager/package.json.template b/src/node/cli/service_packager/package.json.template deleted file mode 100644 index 9f9019172e..0000000000 --- a/src/node/cli/service_packager/package.json.template +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "{{{name}}}", - "version": "{{{version}}}", - "author": "Google Inc.", - "description": "Client library for {{{name}}} built on gRPC", - "license": "Apache-2.0", - "dependencies": { - "grpc": "{{{grpc_version}}}", - "google-auth-library": "^0.9.2" - }, - "main": "index.js", - "files": [ - "LICENSE", - "index.js", - "service.json" - ] -} diff --git a/src/node/ext/byte_buffer.cc b/src/node/ext/byte_buffer.cc index 7eff11c2b3..e1786ddba7 100644 --- a/src/node/ext/byte_buffer.cc +++ b/src/node/ext/byte_buffer.cc @@ -44,15 +44,16 @@ namespace grpc { namespace node { + using v8::Context; using v8::Function; -using v8::Handle; +using v8::Local; using v8::Object; using v8::Number; using v8::Value; -grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) { - NanScope(); +grpc_byte_buffer *BufferToByteBuffer(Local<Value> buffer) { + Nan::HandleScope scope; int length = ::node::Buffer::Length(buffer); char *data = ::node::Buffer::Data(buffer); gpr_slice slice = gpr_slice_malloc(length); @@ -62,10 +63,10 @@ grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) { return byte_buffer; } -Handle<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) { - NanEscapableScope(); +Local<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) { + Nan::EscapableHandleScope scope; if (buffer == NULL) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); } size_t length = grpc_byte_buffer_length(buffer); char *result = reinterpret_cast<char *>(calloc(length, sizeof(char))); @@ -77,21 +78,22 @@ Handle<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) { memcpy(result + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); offset += GPR_SLICE_LENGTH(next); } - return NanEscapeScope(MakeFastBuffer(NanNewBufferHandle(result, length))); + return scope.Escape(MakeFastBuffer( + Nan::NewBuffer(result, length).ToLocalChecked())); } -Handle<Value> MakeFastBuffer(Handle<Value> slowBuffer) { - NanEscapableScope(); - Handle<Object> globalObj = NanGetCurrentContext()->Global(); - Handle<Function> bufferConstructor = Handle<Function>::Cast( - globalObj->Get(NanNew("Buffer"))); - Handle<Value> consArgs[3] = { +Local<Value> MakeFastBuffer(Local<Value> slowBuffer) { + Nan::EscapableHandleScope scope; + Local<Object> globalObj = Nan::GetCurrentContext()->Global(); + Local<Function> bufferConstructor = Local<Function>::Cast( + globalObj->Get(Nan::New("Buffer").ToLocalChecked())); + Local<Value> consArgs[3] = { slowBuffer, - NanNew<Number>(::node::Buffer::Length(slowBuffer)), - NanNew<Number>(0) + Nan::New<Number>(::node::Buffer::Length(slowBuffer)), + Nan::New<Number>(0) }; - Handle<Object> fastBuffer = bufferConstructor->NewInstance(3, consArgs); - return NanEscapeScope(fastBuffer); + Local<Object> fastBuffer = bufferConstructor->NewInstance(3, consArgs); + return scope.Escape(fastBuffer); } } // namespace node } // namespace grpc diff --git a/src/node/ext/byte_buffer.h b/src/node/ext/byte_buffer.h index 5083674d39..55bc0ab377 100644 --- a/src/node/ext/byte_buffer.h +++ b/src/node/ext/byte_buffer.h @@ -45,14 +45,14 @@ namespace node { /* Convert a Node.js Buffer to grpc_byte_buffer. Requires that ::node::Buffer::HasInstance(buffer) */ -grpc_byte_buffer *BufferToByteBuffer(v8::Handle<v8::Value> buffer); +grpc_byte_buffer *BufferToByteBuffer(v8::Local<v8::Value> buffer); /* Convert a grpc_byte_buffer to a Node.js Buffer */ -v8::Handle<v8::Value> ByteBufferToBuffer(grpc_byte_buffer *buffer); +v8::Local<v8::Value> ByteBufferToBuffer(grpc_byte_buffer *buffer); /* Convert a ::node::Buffer to a fast Buffer, as defined in the Node Buffer documentation */ -v8::Handle<v8::Value> MakeFastBuffer(v8::Handle<v8::Value> slowBuffer); +v8::Local<v8::Value> MakeFastBuffer(v8::Local<v8::Value> slowBuffer); } // namespace node } // namespace grpc diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc index 560869e6fa..fe11905109 100644 --- a/src/node/ext/call.cc +++ b/src/node/ext/call.cc @@ -39,12 +39,14 @@ #include "grpc/support/log.h" #include "grpc/grpc.h" +#include "grpc/grpc_security.h" #include "grpc/support/alloc.h" #include "grpc/support/time.h" #include "byte_buffer.h" #include "call.h" #include "channel.h" #include "completion_queue_async_worker.h" +#include "call_credentials.h" #include "timeval.h" using std::unique_ptr; @@ -54,52 +56,73 @@ using std::vector; namespace grpc { namespace node { +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + using v8::Array; using v8::Boolean; using v8::Exception; using v8::External; using v8::Function; using v8::FunctionTemplate; -using v8::Handle; -using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Number; using v8::Object; using v8::ObjectTemplate; -using v8::Persistent; using v8::Uint32; using v8::String; using v8::Value; -NanCallback *Call::constructor; +Callback *Call::constructor; Persistent<FunctionTemplate> Call::fun_tpl; +/** + * Helper function for throwing errors with a grpc_call_error value. + * Modified from the answer by Gus Goose to + * http://stackoverflow.com/questions/31794200. + */ +Local<Value> nanErrorWithCode(const char *msg, grpc_call_error code) { + EscapableHandleScope scope; + Local<Object> err = Nan::Error(msg).As<Object>(); + Nan::Set(err, Nan::New("code").ToLocalChecked(), Nan::New<Uint32>(code)); + return scope.Escape(err); +} + bool EndsWith(const char *str, const char *substr) { return strcmp(str+strlen(str)-strlen(substr), substr) == 0; } -bool CreateMetadataArray(Handle<Object> metadata, grpc_metadata_array *array, +bool CreateMetadataArray(Local<Object> metadata, grpc_metadata_array *array, shared_ptr<Resources> resources) { - NanScope(); + HandleScope scope; grpc_metadata_array_init(array); - Handle<Array> keys(metadata->GetOwnPropertyNames()); + Local<Array> keys = Nan::GetOwnPropertyNames(metadata).ToLocalChecked(); for (unsigned int i = 0; i < keys->Length(); i++) { - Handle<String> current_key(keys->Get(i)->ToString()); - if (!metadata->Get(current_key)->IsArray()) { + Local<String> current_key = Nan::To<String>( + Nan::Get(keys, i).ToLocalChecked()).ToLocalChecked(); + Local<Value> value_array = Nan::Get(metadata, current_key).ToLocalChecked(); + if (!value_array->IsArray()) { return false; } - array->capacity += Local<Array>::Cast(metadata->Get(current_key))->Length(); + array->capacity += Local<Array>::Cast(value_array)->Length(); } array->metadata = reinterpret_cast<grpc_metadata*>( gpr_malloc(array->capacity * sizeof(grpc_metadata))); for (unsigned int i = 0; i < keys->Length(); i++) { - Handle<String> current_key(keys->Get(i)->ToString()); - NanUtf8String *utf8_key = new NanUtf8String(current_key); - resources->strings.push_back(unique_ptr<NanUtf8String>(utf8_key)); - Handle<Array> values = Local<Array>::Cast(metadata->Get(current_key)); + Local<String> current_key(keys->Get(i)->ToString()); + Utf8String *utf8_key = new Utf8String(current_key); + resources->strings.push_back(unique_ptr<Utf8String>(utf8_key)); + Local<Array> values = Local<Array>::Cast( + Nan::Get(metadata, current_key).ToLocalChecked()); for (unsigned int j = 0; j < values->Length(); j++) { - Handle<Value> value = values->Get(j); + Local<Value> value = Nan::Get(values, j).ToLocalChecked(); grpc_metadata *current = &array->metadata[array->count]; current->key = **utf8_key; // Only allow binary headers for "-bin" keys @@ -107,18 +130,16 @@ bool CreateMetadataArray(Handle<Object> metadata, grpc_metadata_array *array, if (::node::Buffer::HasInstance(value)) { current->value = ::node::Buffer::Data(value); current->value_length = ::node::Buffer::Length(value); - Persistent<Value> *handle = new Persistent<Value>(); - NanAssignPersistent(*handle, value); - resources->handles.push_back(unique_ptr<PersistentHolder>( - new PersistentHolder(handle))); + PersistentValue *handle = new PersistentValue(value); + resources->handles.push_back(unique_ptr<PersistentValue>(handle)); } else { return false; } } else { if (value->IsString()) { - Handle<String> string_value = value->ToString(); - NanUtf8String *utf8_value = new NanUtf8String(string_value); - resources->strings.push_back(unique_ptr<NanUtf8String>(utf8_value)); + Local<String> string_value = Nan::To<String>(value).ToLocalChecked(); + Utf8String *utf8_value = new Utf8String(string_value); + resources->strings.push_back(unique_ptr<Utf8String>(utf8_value)); current->value = **utf8_value; current->value_length = string_value->Length(); } else { @@ -131,8 +152,8 @@ bool CreateMetadataArray(Handle<Object> metadata, grpc_metadata_array *array, return true; } -Handle<Value> ParseMetadata(const grpc_metadata_array *metadata_array) { - NanEscapableScope(); +Local<Value> ParseMetadata(const grpc_metadata_array *metadata_array) { + EscapableHandleScope scope; grpc_metadata *metadata_elements = metadata_array->metadata; size_t length = metadata_array->count; std::map<const char*, size_t> size_map; @@ -142,49 +163,63 @@ Handle<Value> ParseMetadata(const grpc_metadata_array *metadata_array) { const char *key = metadata_elements[i].key; if (size_map.count(key)) { size_map[key] += 1; + } else { + size_map[key] = 1; } index_map[key] = 0; } - Handle<Object> metadata_object = NanNew<Object>(); + Local<Object> metadata_object = Nan::New<Object>(); for (unsigned int i = 0; i < length; i++) { grpc_metadata* elem = &metadata_elements[i]; - Handle<String> key_string = NanNew(elem->key); - Handle<Array> array; - if (metadata_object->Has(key_string)) { - array = Handle<Array>::Cast(metadata_object->Get(key_string)); + Local<String> key_string = Nan::New(elem->key).ToLocalChecked(); + Local<Array> array; + MaybeLocal<Value> maybe_array = Nan::Get(metadata_object, key_string); + if (maybe_array.IsEmpty() || !maybe_array.ToLocalChecked()->IsArray()) { + array = Nan::New<Array>(size_map[elem->key]); + Nan::Set(metadata_object, key_string, array); } else { - array = NanNew<Array>(size_map[elem->key]); - metadata_object->Set(key_string, array); + array = Local<Array>::Cast(maybe_array.ToLocalChecked()); } if (EndsWith(elem->key, "-bin")) { - array->Set(index_map[elem->key], - NanNewBufferHandle(elem->value, elem->value_length)); + Nan::Set(array, index_map[elem->key], + MakeFastBuffer( + Nan::CopyBuffer(elem->value, + elem->value_length).ToLocalChecked())); } else { - array->Set(index_map[elem->key], NanNew(elem->value)); + Nan::Set(array, index_map[elem->key], + Nan::New(elem->value).ToLocalChecked()); } index_map[elem->key] += 1; } - return NanEscapeScope(metadata_object); + return scope.Escape(metadata_object); +} + +Local<Value> Op::GetOpType() const { + EscapableHandleScope scope; + return scope.Escape(Nan::New(GetTypeString()).ToLocalChecked()); } -Handle<Value> Op::GetOpType() const { - NanEscapableScope(); - return NanEscapeScope(NanNew<String>(GetTypeString())); +Op::~Op() { } class SendMetadataOp : public Op { public: - Handle<Value> GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(NanTrue()); + Local<Value> GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::True()); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { if (!value->IsObject()) { return false; } grpc_metadata_array array; - if (!CreateMetadataArray(value->ToObject(), &array, resources)) { + MaybeLocal<Object> maybe_metadata = Nan::To<Object>(value); + if (maybe_metadata.IsEmpty()) { + return false; + } + if (!CreateMetadataArray(maybe_metadata.ToLocalChecked(), + &array, resources)) { return false; } out->data.send_initial_metadata.count = array.count; @@ -199,27 +234,28 @@ class SendMetadataOp : public Op { class SendMessageOp : public Op { public: - Handle<Value> GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(NanTrue()); + Local<Value> GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::True()); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { if (!::node::Buffer::HasInstance(value)) { return false; } - Handle<Object> object_value = value->ToObject(); - if (object_value->HasOwnProperty(NanNew("grpcWriteFlags"))) { - Handle<Value> flag_value = object_value->Get(NanNew("grpcWriteFlags")); + Local<Object> object_value = Nan::To<Object>(value).ToLocalChecked(); + MaybeLocal<Value> maybe_flag_value = Nan::Get( + object_value, Nan::New("grpcWriteFlags").ToLocalChecked()); + if (!maybe_flag_value.IsEmpty()) { + Local<Value> flag_value = maybe_flag_value.ToLocalChecked(); if (flag_value->IsUint32()) { - out->flags = flag_value->Uint32Value() & GRPC_WRITE_USED_MASK; + Maybe<uint32_t> maybe_flag = Nan::To<uint32_t>(flag_value); + out->flags = maybe_flag.FromMaybe(0) & GRPC_WRITE_USED_MASK; } } out->data.send_message = BufferToByteBuffer(value); - Persistent<Value> *handle = new Persistent<Value>(); - NanAssignPersistent(*handle, value); - resources->handles.push_back(unique_ptr<PersistentHolder>( - new PersistentHolder(handle))); + PersistentValue *handle = new PersistentValue(value); + resources->handles.push_back(unique_ptr<PersistentValue>(handle)); return true; } protected: @@ -230,11 +266,11 @@ class SendMessageOp : public Op { class SendClientCloseOp : public Op { public: - Handle<Value> GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(NanTrue()); + Local<Value> GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::True()); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { return true; } @@ -246,39 +282,55 @@ class SendClientCloseOp : public Op { class SendServerStatusOp : public Op { public: - Handle<Value> GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(NanTrue()); + Local<Value> GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::True()); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { if (!value->IsObject()) { return false; } - Handle<Object> server_status = value->ToObject(); - if (!server_status->Get(NanNew("metadata"))->IsObject()) { + Local<Object> server_status = Nan::To<Object>(value).ToLocalChecked(); + MaybeLocal<Value> maybe_metadata = Nan::Get( + server_status, Nan::New("metadata").ToLocalChecked()); + if (maybe_metadata.IsEmpty()) { return false; } - if (!server_status->Get(NanNew("code"))->IsUint32()) { + if (!maybe_metadata.ToLocalChecked()->IsObject()) { return false; } - if (!server_status->Get(NanNew("details"))->IsString()) { + Local<Object> metadata = Nan::To<Object>( + maybe_metadata.ToLocalChecked()).ToLocalChecked(); + MaybeLocal<Value> maybe_code = Nan::Get(server_status, + Nan::New("code").ToLocalChecked()); + if (maybe_code.IsEmpty()) { return false; } + if (!maybe_code.ToLocalChecked()->IsUint32()) { + return false; + } + uint32_t code = Nan::To<uint32_t>(maybe_code.ToLocalChecked()).FromJust(); + MaybeLocal<Value> maybe_details = Nan::Get( + server_status, Nan::New("details").ToLocalChecked()); + if (maybe_details.IsEmpty()) { + return false; + } + if (!maybe_details.ToLocalChecked()->IsString()) { + return false; + } + Local<String> details = Nan::To<String>( + maybe_details.ToLocalChecked()).ToLocalChecked(); grpc_metadata_array array; - if (!CreateMetadataArray(server_status->Get(NanNew("metadata"))-> - ToObject(), - &array, resources)) { + if (!CreateMetadataArray(metadata, &array, resources)) { return false; } out->data.send_status_from_server.trailing_metadata_count = array.count; out->data.send_status_from_server.trailing_metadata = array.metadata; out->data.send_status_from_server.status = - static_cast<grpc_status_code>( - server_status->Get(NanNew("code"))->Uint32Value()); - NanUtf8String *str = new NanUtf8String( - server_status->Get(NanNew("details"))); - resources->strings.push_back(unique_ptr<NanUtf8String>(str)); + static_cast<grpc_status_code>(code); + Utf8String *str = new Utf8String(details); + resources->strings.push_back(unique_ptr<Utf8String>(str)); out->data.send_status_from_server.status_details = **str; return true; } @@ -298,12 +350,12 @@ class GetMetadataOp : public Op { grpc_metadata_array_destroy(&recv_metadata); } - Handle<Value> GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(ParseMetadata(&recv_metadata)); + Local<Value> GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(ParseMetadata(&recv_metadata)); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { out->data.recv_initial_metadata = &recv_metadata; return true; @@ -325,15 +377,15 @@ class ReadMessageOp : public Op { } ~ReadMessageOp() { if (recv_message != NULL) { - gpr_free(recv_message); + grpc_byte_buffer_destroy(recv_message); } } - Handle<Value> GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(ByteBufferToBuffer(recv_message)); + Local<Value> GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(ByteBufferToBuffer(recv_message)); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { out->data.recv_message = &recv_message; return true; @@ -361,7 +413,7 @@ class ClientStatusOp : public Op { gpr_free(status_details); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { out->data.recv_status_on_client.trailing_metadata = &metadata_array; out->data.recv_status_on_client.status = &status; @@ -370,15 +422,18 @@ class ClientStatusOp : public Op { return true; } - Handle<Value> GetNodeValue() const { - NanEscapableScope(); - Handle<Object> status_obj = NanNew<Object>(); - status_obj->Set(NanNew("code"), NanNew<Number>(status)); + Local<Value> GetNodeValue() const { + EscapableHandleScope scope; + Local<Object> status_obj = Nan::New<Object>(); + Nan::Set(status_obj, Nan::New("code").ToLocalChecked(), + Nan::New<Number>(status)); if (status_details != NULL) { - status_obj->Set(NanNew("details"), NanNew(status_details)); + Nan::Set(status_obj, Nan::New("details").ToLocalChecked(), + Nan::New(status_details).ToLocalChecked()); } - status_obj->Set(NanNew("metadata"), ParseMetadata(&metadata_array)); - return NanEscapeScope(status_obj); + Nan::Set(status_obj, Nan::New("metadata").ToLocalChecked(), + ParseMetadata(&metadata_array)); + return scope.Escape(status_obj); } protected: std::string GetTypeString() const { @@ -393,12 +448,12 @@ class ClientStatusOp : public Op { class ServerCloseResponseOp : public Op { public: - Handle<Value> GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(NanNew<Boolean>(cancelled)); + Local<Value> GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::New<Boolean>(cancelled)); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { out->data.recv_close_on_server.cancelled = &cancelled; return true; @@ -413,7 +468,7 @@ class ServerCloseResponseOp : public Op { int cancelled; }; -tag::tag(NanCallback *callback, OpVec *ops, +tag::tag(Callback *callback, OpVec *ops, shared_ptr<Resources> resources) : callback(callback), ops(ops), resources(resources){ } @@ -423,19 +478,19 @@ tag::~tag() { delete ops; } -Handle<Value> GetTagNodeValue(void *tag) { - NanEscapableScope(); +Local<Value> GetTagNodeValue(void *tag) { + EscapableHandleScope scope; struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); - Handle<Object> tag_obj = NanNew<Object>(); + Local<Object> tag_obj = Nan::New<Object>(); for (vector<unique_ptr<Op> >::iterator it = tag_struct->ops->begin(); it != tag_struct->ops->end(); ++it) { Op *op_ptr = it->get(); - tag_obj->Set(op_ptr->GetOpType(), op_ptr->GetNodeValue()); + Nan::Set(tag_obj, op_ptr->GetOpType(), op_ptr->GetNodeValue()); } - return NanEscapeScope(tag_obj); + return scope.Escape(tag_obj); } -NanCallback *GetTagCallback(void *tag) { +Callback *GetTagCallback(void *tag) { struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); return tag_struct->callback; } @@ -452,140 +507,150 @@ Call::~Call() { grpc_call_destroy(wrapped_call); } -void Call::Init(Handle<Object> exports) { - NanScope(); - Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New); - tpl->SetClassName(NanNew("Call")); +void Call::Init(Local<Object> exports) { + HandleScope scope; + Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); + tpl->SetClassName(Nan::New("Call").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); - NanSetPrototypeTemplate(tpl, "startBatch", - NanNew<FunctionTemplate>(StartBatch)->GetFunction()); - NanSetPrototypeTemplate(tpl, "cancel", - NanNew<FunctionTemplate>(Cancel)->GetFunction()); - NanSetPrototypeTemplate( - tpl, "cancelWithStatus", - NanNew<FunctionTemplate>(CancelWithStatus)->GetFunction()); - NanSetPrototypeTemplate(tpl, "getPeer", - NanNew<FunctionTemplate>(GetPeer)->GetFunction()); - NanAssignPersistent(fun_tpl, tpl); - Handle<Function> ctr = tpl->GetFunction(); - exports->Set(NanNew("Call"), ctr); - constructor = new NanCallback(ctr); + Nan::SetPrototypeMethod(tpl, "startBatch", StartBatch); + Nan::SetPrototypeMethod(tpl, "cancel", Cancel); + Nan::SetPrototypeMethod(tpl, "cancelWithStatus", CancelWithStatus); + Nan::SetPrototypeMethod(tpl, "getPeer", GetPeer); + Nan::SetPrototypeMethod(tpl, "setCredentials", SetCredentials); + fun_tpl.Reset(tpl); + Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(exports, Nan::New("Call").ToLocalChecked(), ctr); + constructor = new Callback(ctr); } -bool Call::HasInstance(Handle<Value> val) { - NanScope(); - return NanHasInstance(fun_tpl, val); +bool Call::HasInstance(Local<Value> val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); } -Handle<Value> Call::WrapStruct(grpc_call *call) { - NanEscapableScope(); +Local<Value> Call::WrapStruct(grpc_call *call) { + EscapableHandleScope scope; if (call == NULL) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); } const int argc = 1; - Handle<Value> argv[argc] = {NanNew<External>(reinterpret_cast<void *>(call))}; - return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv)); + Local<Value> argv[argc] = {Nan::New<External>( + reinterpret_cast<void *>(call))}; + MaybeLocal<Object> maybe_instance = Nan::NewInstance( + constructor->GetFunction(), argc, argv); + if (maybe_instance.IsEmpty()) { + return scope.Escape(Nan::Null()); + } else { + return scope.Escape(maybe_instance.ToLocalChecked()); + } } NAN_METHOD(Call::New) { - NanScope(); - - if (args.IsConstructCall()) { + if (info.IsConstructCall()) { Call *call; - if (args[0]->IsExternal()) { - Handle<External> ext = args[0].As<External>(); + if (info[0]->IsExternal()) { + Local<External> ext = info[0].As<External>(); // This option is used for wrapping an existing call grpc_call *call_value = reinterpret_cast<grpc_call *>(ext->Value()); call = new Call(call_value); } else { - if (!Channel::HasInstance(args[0])) { - return NanThrowTypeError("Call's first argument must be a Channel"); + if (!Channel::HasInstance(info[0])) { + return Nan::ThrowTypeError("Call's first argument must be a Channel"); } - if (!args[1]->IsString()) { - return NanThrowTypeError("Call's second argument must be a string"); + if (!info[1]->IsString()) { + return Nan::ThrowTypeError("Call's second argument must be a string"); } - if (!(args[2]->IsNumber() || args[2]->IsDate())) { - return NanThrowTypeError( + if (!(info[2]->IsNumber() || info[2]->IsDate())) { + return Nan::ThrowTypeError( "Call's third argument must be a date or a number"); } // These arguments are at the end because they are optional grpc_call *parent_call = NULL; - if (Call::HasInstance(args[4])) { - Call *parent_obj = ObjectWrap::Unwrap<Call>(args[4]->ToObject()); + if (Call::HasInstance(info[4])) { + Call *parent_obj = ObjectWrap::Unwrap<Call>( + Nan::To<Object>(info[4]).ToLocalChecked()); parent_call = parent_obj->wrapped_call; - } else if (!(args[4]->IsUndefined() || args[4]->IsNull())) { - return NanThrowTypeError( + } else if (!(info[4]->IsUndefined() || info[4]->IsNull())) { + return Nan::ThrowTypeError( "Call's fifth argument must be another call, if provided"); } gpr_uint32 propagate_flags = GRPC_PROPAGATE_DEFAULTS; - if (args[5]->IsUint32()) { - propagate_flags = args[5]->Uint32Value(); - } else if (!(args[5]->IsUndefined() || args[5]->IsNull())) { - return NanThrowTypeError( + if (info[5]->IsUint32()) { + propagate_flags = Nan::To<uint32_t>(info[5]).FromJust(); + } else if (!(info[5]->IsUndefined() || info[5]->IsNull())) { + return Nan::ThrowTypeError( "Call's sixth argument must be propagate flags, if provided"); } - Handle<Object> channel_object = args[0]->ToObject(); + Local<Object> channel_object = Nan::To<Object>(info[0]).ToLocalChecked(); Channel *channel = ObjectWrap::Unwrap<Channel>(channel_object); if (channel->GetWrappedChannel() == NULL) { - return NanThrowError("Call cannot be created from a closed channel"); + return Nan::ThrowError("Call cannot be created from a closed channel"); } - NanUtf8String method(args[1]); - double deadline = args[2]->NumberValue(); + Utf8String method(info[1]); + double deadline = Nan::To<double>(info[2]).FromJust(); grpc_channel *wrapped_channel = channel->GetWrappedChannel(); grpc_call *wrapped_call; - if (args[3]->IsString()) { - NanUtf8String host_override(args[3]); + if (info[3]->IsString()) { + Utf8String host_override(info[3]); wrapped_call = grpc_channel_create_call( wrapped_channel, parent_call, propagate_flags, CompletionQueueAsyncWorker::GetQueue(), *method, *host_override, MillisecondsToTimespec(deadline), NULL); - } else if (args[3]->IsUndefined() || args[3]->IsNull()) { + } else if (info[3]->IsUndefined() || info[3]->IsNull()) { wrapped_call = grpc_channel_create_call( wrapped_channel, parent_call, propagate_flags, CompletionQueueAsyncWorker::GetQueue(), *method, NULL, MillisecondsToTimespec(deadline), NULL); } else { - return NanThrowTypeError("Call's fourth argument must be a string"); + return Nan::ThrowTypeError("Call's fourth argument must be a string"); } call = new Call(wrapped_call); - args.This()->SetHiddenValue(NanNew("channel_"), channel_object); + info.This()->SetHiddenValue(Nan::New("channel_").ToLocalChecked(), + channel_object); } - call->Wrap(args.This()); - NanReturnValue(args.This()); + call->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); } else { const int argc = 4; - Local<Value> argv[argc] = {args[0], args[1], args[2], args[3]}; - NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); + Local<Value> argv[argc] = {info[0], info[1], info[2], info[3]}; + MaybeLocal<Object> maybe_instance = constructor->GetFunction()->NewInstance( + argc, argv); + if (maybe_instance.IsEmpty()) { + // There's probably a pending exception + return; + } else { + info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); + } } } NAN_METHOD(Call::StartBatch) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("startBatch can only be called on Call objects"); + if (!Call::HasInstance(info.This())) { + return Nan::ThrowTypeError("startBatch can only be called on Call objects"); } - if (!args[0]->IsObject()) { - return NanThrowError("startBatch's first argument must be an object"); + if (!info[0]->IsObject()) { + return Nan::ThrowError("startBatch's first argument must be an object"); } - if (!args[1]->IsFunction()) { - return NanThrowError("startBatch's second argument must be a callback"); + if (!info[1]->IsFunction()) { + return Nan::ThrowError("startBatch's second argument must be a callback"); } - Handle<Function> callback_func = args[1].As<Function>(); - Call *call = ObjectWrap::Unwrap<Call>(args.This()); + Local<Function> callback_func = info[1].As<Function>(); + Call *call = ObjectWrap::Unwrap<Call>(info.This()); shared_ptr<Resources> resources(new Resources); - Handle<Object> obj = args[0]->ToObject(); - Handle<Array> keys = obj->GetOwnPropertyNames(); + Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked(); + Local<Array> keys = Nan::GetOwnPropertyNames(obj).ToLocalChecked(); size_t nops = keys->Length(); vector<grpc_op> ops(nops); unique_ptr<OpVec> op_vector(new OpVec()); for (unsigned int i = 0; i < nops; i++) { unique_ptr<Op> op; - if (!keys->Get(i)->IsUint32()) { - return NanThrowError( + MaybeLocal<Value> maybe_key = Nan::Get(keys, i); + if (maybe_key.IsEmpty() || (!maybe_key.ToLocalChecked()->IsUint32())) { + return Nan::ThrowError( "startBatch's first argument's keys must be integers"); } - uint32_t type = keys->Get(i)->Uint32Value(); + uint32_t type = Nan::To<uint32_t>(maybe_key.ToLocalChecked()).FromJust(); ops[i].op = static_cast<grpc_op_type>(type); ops[i].flags = 0; ops[i].reserved = NULL; @@ -615,67 +680,89 @@ NAN_METHOD(Call::StartBatch) { op.reset(new ServerCloseResponseOp()); break; default: - return NanThrowError("Argument object had an unrecognized key"); + return Nan::ThrowError("Argument object had an unrecognized key"); } if (!op->ParseOp(obj->Get(type), &ops[i], resources)) { - return NanThrowTypeError("Incorrectly typed arguments to startBatch"); + return Nan::ThrowTypeError("Incorrectly typed arguments to startBatch"); } op_vector->push_back(std::move(op)); } - NanCallback *callback = new NanCallback(callback_func); + Callback *callback = new Callback(callback_func); grpc_call_error error = grpc_call_start_batch( call->wrapped_call, &ops[0], nops, new struct tag( callback, op_vector.release(), resources), NULL); if (error != GRPC_CALL_OK) { - return NanThrowError(nanErrorWithCode("startBatch failed", error)); + return Nan::ThrowError(nanErrorWithCode("startBatch failed", error)); } CompletionQueueAsyncWorker::Next(); - NanReturnUndefined(); } NAN_METHOD(Call::Cancel) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("cancel can only be called on Call objects"); + if (!Call::HasInstance(info.This())) { + return Nan::ThrowTypeError("cancel can only be called on Call objects"); } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); + Call *call = ObjectWrap::Unwrap<Call>(info.This()); grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL); if (error != GRPC_CALL_OK) { - return NanThrowError(nanErrorWithCode("cancel failed", error)); + return Nan::ThrowError(nanErrorWithCode("cancel failed", error)); } - NanReturnUndefined(); } NAN_METHOD(Call::CancelWithStatus) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("cancel can only be called on Call objects"); + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("cancel can only be called on Call objects"); } - if (!args[0]->IsUint32()) { - return NanThrowTypeError( + if (!info[0]->IsUint32()) { + return Nan::ThrowTypeError( "cancelWithStatus's first argument must be a status code"); } - if (!args[1]->IsString()) { - return NanThrowTypeError( + if (!info[1]->IsString()) { + return Nan::ThrowTypeError( "cancelWithStatus's second argument must be a string"); } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); - grpc_status_code code = static_cast<grpc_status_code>(args[0]->Uint32Value()); - NanUtf8String details(args[0]); + Call *call = ObjectWrap::Unwrap<Call>(info.This()); + grpc_status_code code = static_cast<grpc_status_code>( + Nan::To<uint32_t>(info[0]).FromJust()); + if (code == GRPC_STATUS_OK) { + return Nan::ThrowRangeError( + "cancelWithStatus cannot be called with OK status"); + } + Utf8String details(info[1]); grpc_call_cancel_with_status(call->wrapped_call, code, *details, NULL); - NanReturnUndefined(); } NAN_METHOD(Call::GetPeer) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("getPeer can only be called on Call objects"); + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("getPeer can only be called on Call objects"); } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); + Call *call = ObjectWrap::Unwrap<Call>(info.This()); char *peer = grpc_call_get_peer(call->wrapped_call); - Handle<Value> peer_value = NanNew(peer); + Local<Value> peer_value = Nan::New(peer).ToLocalChecked(); gpr_free(peer); - NanReturnValue(peer_value); + info.GetReturnValue().Set(peer_value); +} + +NAN_METHOD(Call::SetCredentials) { + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( + "setCredentials can only be called on Call objects"); + } + if (!CallCredentials::HasInstance(info[0])) { + return Nan::ThrowTypeError( + "setCredentials' first argument must be a CallCredentials"); + } + Call *call = ObjectWrap::Unwrap<Call>(info.This()); + CallCredentials *creds_object = ObjectWrap::Unwrap<CallCredentials>( + Nan::To<Object>(info[0]).ToLocalChecked()); + grpc_credentials *creds = creds_object->GetWrappedCredentials(); + grpc_call_error error = GRPC_CALL_ERROR; + if (creds) { + error = grpc_call_set_credentials(call->wrapped_call, creds); + } + info.GetReturnValue().Set(Nan::New<Uint32>(error)); } } // namespace node diff --git a/src/node/ext/call.h b/src/node/ext/call.h index 89f81dcf4d..1e3c3ba18d 100644 --- a/src/node/ext/call.h +++ b/src/node/ext/call.h @@ -51,76 +51,56 @@ namespace node { using std::unique_ptr; using std::shared_ptr; -/** - * Helper function for throwing errors with a grpc_call_error value. - * Modified from the answer by Gus Goose to - * http://stackoverflow.com/questions/31794200. - */ -inline v8::Local<v8::Value> nanErrorWithCode(const char *msg, - grpc_call_error code) { - NanEscapableScope(); - v8::Local<v8::Object> err = NanError(msg).As<v8::Object>(); - err->Set(NanNew("code"), NanNew<v8::Uint32>(code)); - return NanEscapeScope(err); -} - -v8::Handle<v8::Value> ParseMetadata(const grpc_metadata_array *metadata_array); - -class PersistentHolder { - public: - explicit PersistentHolder(v8::Persistent<v8::Value> *persist) : - persist(persist) { - } +typedef Nan::Persistent<v8::Value, Nan::CopyablePersistentTraits<v8::Value>> PersistentValue; - ~PersistentHolder() { - NanDisposePersistent(*persist); - delete persist; - } +v8::Local<v8::Value> nanErrorWithCode(const char *msg, grpc_call_error code); - private: - v8::Persistent<v8::Value> *persist; -}; +v8::Local<v8::Value> ParseMetadata(const grpc_metadata_array *metadata_array); struct Resources { - std::vector<unique_ptr<NanUtf8String> > strings; - std::vector<unique_ptr<PersistentHolder> > handles; + std::vector<unique_ptr<Nan::Utf8String> > strings; + std::vector<unique_ptr<PersistentValue> > handles; }; +bool CreateMetadataArray(v8::Local<v8::Object> metadata, + grpc_metadata_array *array, + shared_ptr<Resources> resources); + class Op { public: - virtual v8::Handle<v8::Value> GetNodeValue() const = 0; - virtual bool ParseOp(v8::Handle<v8::Value> value, grpc_op *out, + virtual v8::Local<v8::Value> GetNodeValue() const = 0; + virtual bool ParseOp(v8::Local<v8::Value> value, grpc_op *out, shared_ptr<Resources> resources) = 0; - v8::Handle<v8::Value> GetOpType() const; + virtual ~Op(); + v8::Local<v8::Value> GetOpType() const; protected: virtual std::string GetTypeString() const = 0; }; typedef std::vector<unique_ptr<Op>> OpVec; - struct tag { - tag(NanCallback *callback, OpVec *ops, + tag(Nan::Callback *callback, OpVec *ops, shared_ptr<Resources> resources); ~tag(); - NanCallback *callback; + Nan::Callback *callback; OpVec *ops; shared_ptr<Resources> resources; }; -v8::Handle<v8::Value> GetTagNodeValue(void *tag); +v8::Local<v8::Value> GetTagNodeValue(void *tag); -NanCallback *GetTagCallback(void *tag); +Nan::Callback *GetTagCallback(void *tag); void DestroyTag(void *tag); /* Wrapper class for grpc_call structs. */ -class Call : public ::node::ObjectWrap { +class Call : public Nan::ObjectWrap { public: - static void Init(v8::Handle<v8::Object> exports); - static bool HasInstance(v8::Handle<v8::Value> val); + static void Init(v8::Local<v8::Object> exports); + static bool HasInstance(v8::Local<v8::Value> val); /* Wrap a grpc_call struct in a javascript object */ - static v8::Handle<v8::Value> WrapStruct(grpc_call *call); + static v8::Local<v8::Value> WrapStruct(grpc_call *call); private: explicit Call(grpc_call *call); @@ -135,9 +115,10 @@ class Call : public ::node::ObjectWrap { static NAN_METHOD(Cancel); static NAN_METHOD(CancelWithStatus); static NAN_METHOD(GetPeer); - static NanCallback *constructor; + static NAN_METHOD(SetCredentials); + static Nan::Callback *constructor; // Used for typechecking instances of this javascript class - static v8::Persistent<v8::FunctionTemplate> fun_tpl; + static Nan::Persistent<v8::FunctionTemplate> fun_tpl; grpc_call *wrapped_call; }; diff --git a/src/node/ext/call_credentials.cc b/src/node/ext/call_credentials.cc new file mode 100644 index 0000000000..ff16a1f122 --- /dev/null +++ b/src/node/ext/call_credentials.cc @@ -0,0 +1,252 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <node.h> + +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "grpc/support/log.h" +#include "call_credentials.h" +#include "call.h" + +namespace grpc { +namespace node { + +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + +using v8::Exception; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Integer; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::Value; + +Nan::Callback *CallCredentials::constructor; +Persistent<FunctionTemplate> CallCredentials::fun_tpl; + +CallCredentials::CallCredentials(grpc_credentials *credentials) + : wrapped_credentials(credentials) {} + +CallCredentials::~CallCredentials() { + grpc_credentials_release(wrapped_credentials); +} + +void CallCredentials::Init(Local<Object> exports) { + HandleScope scope; + Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); + tpl->SetClassName(Nan::New("CallCredentials").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + Nan::SetPrototypeMethod(tpl, "compose", Compose); + fun_tpl.Reset(tpl); + Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(ctr, Nan::New("createFromPlugin").ToLocalChecked(), + Nan::GetFunction( + Nan::New<FunctionTemplate>(CreateFromPlugin)).ToLocalChecked()); + Nan::Set(exports, Nan::New("CallCredentials").ToLocalChecked(), ctr); + constructor = new Nan::Callback(ctr); +} + +bool CallCredentials::HasInstance(Local<Value> val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); +} + +Local<Value> CallCredentials::WrapStruct(grpc_credentials *credentials) { + EscapableHandleScope scope; + const int argc = 1; + if (credentials == NULL) { + return scope.Escape(Nan::Null()); + } + Local<Value> argv[argc] = { + Nan::New<External>(reinterpret_cast<void *>(credentials))}; + MaybeLocal<Object> maybe_instance = Nan::NewInstance( + constructor->GetFunction(), argc, argv); + if (maybe_instance.IsEmpty()) { + return scope.Escape(Nan::Null()); + } else { + return scope.Escape(maybe_instance.ToLocalChecked()); + } +} + +grpc_credentials *CallCredentials::GetWrappedCredentials() { + return wrapped_credentials; +} + +NAN_METHOD(CallCredentials::New) { + if (info.IsConstructCall()) { + if (!info[0]->IsExternal()) { + return Nan::ThrowTypeError( + "CallCredentials can only be created with the provided functions"); + } + Local<External> ext = info[0].As<External>(); + grpc_credentials *creds_value = + reinterpret_cast<grpc_credentials *>(ext->Value()); + CallCredentials *credentials = new CallCredentials(creds_value); + credentials->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + return; + } else { + // This should never be called directly + return Nan::ThrowTypeError( + "CallCredentials can only be created with the provided functions"); + } +} + +NAN_METHOD(CallCredentials::Compose) { + if (!CallCredentials::HasInstance(info.This())) { + return Nan::ThrowTypeError( + "compose can only be called on CallCredentials objects"); + } + if (!CallCredentials::HasInstance(info[0])) { + return Nan::ThrowTypeError( + "compose's first argument must be a CallCredentials object"); + } + CallCredentials *self = ObjectWrap::Unwrap<CallCredentials>(info.This()); + CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>( + Nan::To<Object>(info[0]).ToLocalChecked()); + grpc_credentials *creds = grpc_composite_credentials_create( + self->wrapped_credentials, other->wrapped_credentials, NULL); + info.GetReturnValue().Set(WrapStruct(creds)); +} + + + +NAN_METHOD(CallCredentials::CreateFromPlugin) { + if (!info[0]->IsFunction()) { + return Nan::ThrowTypeError( + "createFromPlugin's argument must be a function"); + } + grpc_metadata_credentials_plugin plugin; + plugin_state *state = new plugin_state; + state->callback = new Nan::Callback(info[0].As<Function>()); + plugin.get_metadata = plugin_get_metadata; + plugin.destroy = plugin_destroy_state; + plugin.state = reinterpret_cast<void*>(state); + grpc_credentials *creds = grpc_metadata_credentials_create_from_plugin(plugin, + NULL); + info.GetReturnValue().Set(WrapStruct(creds)); +} + +NAN_METHOD(PluginCallback) { + // Arguments: status code, error details, metadata + if (!info[0]->IsUint32()) { + return Nan::ThrowTypeError( + "The callback's first argument must be a status code"); + } + if (!info[1]->IsString()) { + return Nan::ThrowTypeError( + "The callback's second argument must be a string"); + } + if (!info[2]->IsObject()) { + return Nan::ThrowTypeError( + "The callback's third argument must be an object"); + } + shared_ptr<Resources> resources(new Resources); + grpc_status_code code = static_cast<grpc_status_code>( + Nan::To<uint32_t>(info[0]).FromJust()); + char *details = *Utf8String(info[1]); + grpc_metadata_array array; + if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(), + &array, resources)){ + return Nan::ThrowError("Failed to parse metadata"); + } + grpc_credentials_plugin_metadata_cb cb = + reinterpret_cast<grpc_credentials_plugin_metadata_cb>( + Nan::Get(info.Callee(), + Nan::New("cb").ToLocalChecked() + ).ToLocalChecked().As<External>()->Value()); + void *user_data = + Nan::Get(info.Callee(), + Nan::New("user_data").ToLocalChecked() + ).ToLocalChecked().As<External>()->Value(); + cb(user_data, array.metadata, array.count, code, details); +} + +NAUV_WORK_CB(SendPluginCallback) { + Nan::HandleScope scope; + plugin_callback_data *data = reinterpret_cast<plugin_callback_data*>( + async->data); + // Attach cb and user_data to plugin_callback so that it can access them later + v8::Local<v8::Function> plugin_callback = Nan::GetFunction( + Nan::New<v8::FunctionTemplate>(PluginCallback)).ToLocalChecked(); + Nan::Set(plugin_callback, Nan::New("cb").ToLocalChecked(), + Nan::New<v8::External>(reinterpret_cast<void*>(data->cb))); + Nan::Set(plugin_callback, Nan::New("user_data").ToLocalChecked(), + Nan::New<v8::External>(data->user_data)); + const int argc = 2; + v8::Local<v8::Value> argv[argc] = { + Nan::New(data->service_url).ToLocalChecked(), + plugin_callback + }; + Nan::Callback *callback = data->state->callback; + callback->Call(argc, argv); + delete data; + uv_unref((uv_handle_t *)async); + uv_close((uv_handle_t *)async, (uv_close_cb)free); +} + +void plugin_get_metadata(void *state, const char *service_url, + grpc_credentials_plugin_metadata_cb cb, + void *user_data) { + uv_async_t *async = static_cast<uv_async_t*>(malloc(sizeof(uv_async_t))); + uv_async_init(uv_default_loop(), + async, + SendPluginCallback); + plugin_callback_data *data = new plugin_callback_data; + data->state = reinterpret_cast<plugin_state*>(state); + data->service_url = service_url; + data->cb = cb; + data->user_data = user_data; + async->data = data; + /* libuv says that it will coalesce calls to uv_async_send. If there is ever a + * problem with a callback not getting called, that is probably the reason */ + uv_async_send(async); +} + +void plugin_destroy_state(void *ptr) { + plugin_state *state = reinterpret_cast<plugin_state *>(ptr); + delete state->callback; +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/call_credentials.h b/src/node/ext/call_credentials.h new file mode 100644 index 0000000000..618292d19e --- /dev/null +++ b/src/node/ext/call_credentials.h @@ -0,0 +1,100 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_NODE_CALL_CREDENTIALS_H_ +#define GRPC_NODE_CALL_CREDENTIALS_H_ + +#include <node.h> +#include <nan.h> +#include "grpc/grpc_security.h" + +namespace grpc { +namespace node { + +class CallCredentials : public Nan::ObjectWrap { + public: + static void Init(v8::Local<v8::Object> exports); + static bool HasInstance(v8::Local<v8::Value> val); + /* Wrap a grpc_credentials struct in a javascript object */ + static v8::Local<v8::Value> WrapStruct(grpc_credentials *credentials); + + /* Returns the grpc_credentials struct that this object wraps */ + grpc_credentials *GetWrappedCredentials(); + + private: + explicit CallCredentials(grpc_credentials *credentials); + ~CallCredentials(); + + // Prevent copying + CallCredentials(const CallCredentials &); + CallCredentials &operator=(const CallCredentials &); + + static NAN_METHOD(New); + static NAN_METHOD(CreateSsl); + static NAN_METHOD(CreateFromPlugin); + + static NAN_METHOD(Compose); + static Nan::Callback *constructor; + // Used for typechecking instances of this javascript class + static Nan::Persistent<v8::FunctionTemplate> fun_tpl; + + grpc_credentials *wrapped_credentials; +}; + +/* Auth metadata plugin functionality */ + +typedef struct plugin_state { + Nan::Callback *callback; +} plugin_state; + +typedef struct plugin_callback_data { + plugin_state *state; + const char *service_url; + grpc_credentials_plugin_metadata_cb cb; + void *user_data; +} plugin_callback_data; + +void plugin_get_metadata(void *state, const char *service_url, + grpc_credentials_plugin_metadata_cb cb, + void *user_data); + +void plugin_destroy_state(void *state); + +NAN_METHOD(PluginCallback); + +NAUV_WORK_CB(SendPluginCallback); + +} // namespace node +} // namepsace grpc + +#endif // GRPC_NODE_CALL_CREDENTIALS_H_ diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc index 9aed96bbf5..584a0cf8ab 100644 --- a/src/node/ext/channel.cc +++ b/src/node/ext/channel.cc @@ -42,29 +42,101 @@ #include "call.h" #include "channel.h" #include "completion_queue_async_worker.h" -#include "credentials.h" +#include "channel_credentials.h" #include "timeval.h" namespace grpc { namespace node { +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + using v8::Array; using v8::Exception; using v8::Function; using v8::FunctionTemplate; -using v8::Handle; -using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Number; using v8::Object; -using v8::Persistent; using v8::String; using v8::Value; -NanCallback *Channel::constructor; +Callback *Channel::constructor; Persistent<FunctionTemplate> Channel::fun_tpl; +bool ParseChannelArgs(Local<Value> args_val, + grpc_channel_args **channel_args_ptr) { + if (args_val->IsUndefined() || args_val->IsNull()) { + *channel_args_ptr = NULL; + return true; + } + if (!args_val->IsObject()) { + *channel_args_ptr = NULL; + return false; + } + grpc_channel_args *channel_args = reinterpret_cast<grpc_channel_args*>( + malloc(sizeof(channel_args))); + *channel_args_ptr = channel_args; + Local<Object> args_hash = Nan::To<Object>(args_val).ToLocalChecked(); + Local<Array> keys = Nan::GetOwnPropertyNames(args_hash).ToLocalChecked(); + channel_args->num_args = keys->Length(); + channel_args->args = reinterpret_cast<grpc_arg *>( + calloc(channel_args->num_args, sizeof(grpc_arg))); + for (unsigned int i = 0; i < channel_args->num_args; i++) { + Local<Value> key = Nan::Get(keys, i).ToLocalChecked(); + Utf8String key_str(key); + if (*key_str == NULL) { + // Key string onversion failed + return false; + } + Local<Value> value = Nan::Get(args_hash, key).ToLocalChecked(); + if (value->IsInt32()) { + channel_args->args[i].type = GRPC_ARG_INTEGER; + channel_args->args[i].value.integer = Nan::To<int32_t>(value).FromJust(); + } else if (value->IsString()) { + Utf8String val_str(value); + channel_args->args[i].type = GRPC_ARG_STRING; + channel_args->args[i].value.string = reinterpret_cast<char*>( + calloc(val_str.length() + 1,sizeof(char))); + memcpy(channel_args->args[i].value.string, + *val_str, val_str.length() + 1); + } else { + // The value does not match either of the accepted types + return false; + } + channel_args->args[i].key = reinterpret_cast<char*>( + calloc(key_str.length() + 1, sizeof(char))); + memcpy(channel_args->args[i].key, *key_str, key_str.length() + 1); + } + return true; +} + +void DeallocateChannelArgs(grpc_channel_args *channel_args) { + if (channel_args == NULL) { + return; + } + for (size_t i = 0; i < channel_args->num_args; i++) { + if (channel_args->args[i].key == NULL) { + /* NULL key implies that this argument and all subsequent arguments failed + * to parse */ + break; + } + free(channel_args->args[i].key); + if (channel_args->args[i].type == GRPC_ARG_STRING) { + free(channel_args->args[i].value.string); + } + } + free(channel_args->args); + free(channel_args); +} + Channel::Channel(grpc_channel *channel) : wrapped_channel(channel) {} Channel::~Channel() { @@ -73,88 +145,51 @@ Channel::~Channel() { } } -void Channel::Init(Handle<Object> exports) { - NanScope(); - Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New); - tpl->SetClassName(NanNew("Channel")); +void Channel::Init(Local<Object> exports) { + Nan::HandleScope scope; + Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); + tpl->SetClassName(Nan::New("Channel").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); - NanSetPrototypeTemplate(tpl, "close", - NanNew<FunctionTemplate>(Close)->GetFunction()); - NanSetPrototypeTemplate(tpl, "getTarget", - NanNew<FunctionTemplate>(GetTarget)->GetFunction()); - NanSetPrototypeTemplate( - tpl, "getConnectivityState", - NanNew<FunctionTemplate>(GetConnectivityState)->GetFunction()); - NanSetPrototypeTemplate( - tpl, "watchConnectivityState", - NanNew<FunctionTemplate>(WatchConnectivityState)->GetFunction()); - NanAssignPersistent(fun_tpl, tpl); - Handle<Function> ctr = tpl->GetFunction(); - constructor = new NanCallback(ctr); - exports->Set(NanNew("Channel"), ctr); + Nan::SetPrototypeMethod(tpl, "close", Close); + Nan::SetPrototypeMethod(tpl, "getTarget", GetTarget); + Nan::SetPrototypeMethod(tpl, "getConnectivityState", GetConnectivityState); + Nan::SetPrototypeMethod(tpl, "watchConnectivityState", + WatchConnectivityState); + fun_tpl.Reset(tpl); + Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(exports, Nan::New("Channel").ToLocalChecked(), ctr); + constructor = new Callback(ctr); } -bool Channel::HasInstance(Handle<Value> val) { - NanScope(); - return NanHasInstance(fun_tpl, val); +bool Channel::HasInstance(Local<Value> val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); } grpc_channel *Channel::GetWrappedChannel() { return this->wrapped_channel; } NAN_METHOD(Channel::New) { - NanScope(); - - if (args.IsConstructCall()) { - if (!args[0]->IsString()) { - return NanThrowTypeError( + if (info.IsConstructCall()) { + if (!info[0]->IsString()) { + return Nan::ThrowTypeError( "Channel expects a string, a credential and an object"); } grpc_channel *wrapped_channel; // Owned by the Channel object - NanUtf8String host(args[0]); + Utf8String host(info[0]); grpc_credentials *creds; - if (!Credentials::HasInstance(args[1])) { - return NanThrowTypeError( - "Channel's second argument must be a credential"); + if (!ChannelCredentials::HasInstance(info[1])) { + return Nan::ThrowTypeError( + "Channel's second argument must be a ChannelCredentials"); } - Credentials *creds_object = ObjectWrap::Unwrap<Credentials>( - args[1]->ToObject()); + ChannelCredentials *creds_object = ObjectWrap::Unwrap<ChannelCredentials>( + Nan::To<Object>(info[1]).ToLocalChecked()); creds = creds_object->GetWrappedCredentials(); - grpc_channel_args *channel_args_ptr; - if (args[2]->IsUndefined()) { - channel_args_ptr = NULL; - wrapped_channel = grpc_insecure_channel_create(*host, NULL, NULL); - } else if (args[2]->IsObject()) { - Handle<Object> args_hash(args[2]->ToObject()->Clone()); - Handle<Array> keys(args_hash->GetOwnPropertyNames()); - grpc_channel_args channel_args; - channel_args.num_args = keys->Length(); - channel_args.args = reinterpret_cast<grpc_arg *>( - calloc(channel_args.num_args, sizeof(grpc_arg))); - /* These are used to keep all strings until then end of the block, then - destroy them */ - std::vector<NanUtf8String *> key_strings(keys->Length()); - std::vector<NanUtf8String *> value_strings(keys->Length()); - for (unsigned int i = 0; i < channel_args.num_args; i++) { - Handle<String> current_key(keys->Get(i)->ToString()); - Handle<Value> current_value(args_hash->Get(current_key)); - key_strings[i] = new NanUtf8String(current_key); - channel_args.args[i].key = **key_strings[i]; - if (current_value->IsInt32()) { - channel_args.args[i].type = GRPC_ARG_INTEGER; - channel_args.args[i].value.integer = current_value->Int32Value(); - } else if (current_value->IsString()) { - channel_args.args[i].type = GRPC_ARG_STRING; - value_strings[i] = new NanUtf8String(current_value); - channel_args.args[i].value.string = **value_strings[i]; - } else { - free(channel_args.args); - return NanThrowTypeError("Arg values must be strings"); - } - } - channel_args_ptr = &channel_args; - } else { - return NanThrowTypeError("Channel expects a string and an object"); + grpc_channel_args *channel_args_ptr = NULL; + if (!ParseChannelArgs(info[2], &channel_args_ptr)) { + DeallocateChannelArgs(channel_args_ptr); + return Nan::ThrowTypeError("Channel options must be an object with " + "string keys and integer or string values"); } if (creds == NULL) { wrapped_channel = grpc_insecure_channel_create(*host, channel_args_ptr, @@ -163,77 +198,81 @@ NAN_METHOD(Channel::New) { wrapped_channel = grpc_secure_channel_create(creds, *host, channel_args_ptr, NULL); } - if (channel_args_ptr != NULL) { - free(channel_args_ptr->args); - } + DeallocateChannelArgs(channel_args_ptr); Channel *channel = new Channel(wrapped_channel); - channel->Wrap(args.This()); - NanReturnValue(args.This()); + channel->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + return; } else { const int argc = 3; - Local<Value> argv[argc] = {args[0], args[1], args[2]}; - NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); + Local<Value> argv[argc] = {info[0], info[1], info[2]}; + MaybeLocal<Object> maybe_instance = constructor->GetFunction()->NewInstance( + argc, argv); + if (maybe_instance.IsEmpty()) { + // There's probably a pending exception + return; + } else { + info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); + } } } NAN_METHOD(Channel::Close) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("close can only be called on Channel objects"); + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("close can only be called on Channel objects"); } - Channel *channel = ObjectWrap::Unwrap<Channel>(args.This()); + Channel *channel = ObjectWrap::Unwrap<Channel>(info.This()); if (channel->wrapped_channel != NULL) { grpc_channel_destroy(channel->wrapped_channel); channel->wrapped_channel = NULL; } - NanReturnUndefined(); } NAN_METHOD(Channel::GetTarget) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("getTarget can only be called on Channel objects"); + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("getTarget can only be called on Channel objects"); } - Channel *channel = ObjectWrap::Unwrap<Channel>(args.This()); - NanReturnValue(NanNew(grpc_channel_get_target(channel->wrapped_channel))); + Channel *channel = ObjectWrap::Unwrap<Channel>(info.This()); + info.GetReturnValue().Set(Nan::New( + grpc_channel_get_target(channel->wrapped_channel)).ToLocalChecked()); } NAN_METHOD(Channel::GetConnectivityState) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError( + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( "getConnectivityState can only be called on Channel objects"); } - Channel *channel = ObjectWrap::Unwrap<Channel>(args.This()); - int try_to_connect = (int)args[0]->Equals(NanTrue()); - NanReturnValue(grpc_channel_check_connectivity_state(channel->wrapped_channel, - try_to_connect)); + Channel *channel = ObjectWrap::Unwrap<Channel>(info.This()); + int try_to_connect = (int)info[0]->Equals(Nan::True()); + info.GetReturnValue().Set( + grpc_channel_check_connectivity_state(channel->wrapped_channel, + try_to_connect)); } NAN_METHOD(Channel::WatchConnectivityState) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError( + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( "watchConnectivityState can only be called on Channel objects"); } - if (!args[0]->IsUint32()) { - return NanThrowTypeError( + if (!info[0]->IsUint32()) { + return Nan::ThrowTypeError( "watchConnectivityState's first argument must be a channel state"); } - if (!(args[1]->IsNumber() || args[1]->IsDate())) { - return NanThrowTypeError( + if (!(info[1]->IsNumber() || info[1]->IsDate())) { + return Nan::ThrowTypeError( "watchConnectivityState's second argument must be a date or a number"); } - if (!args[2]->IsFunction()) { - return NanThrowTypeError( + if (!info[2]->IsFunction()) { + return Nan::ThrowTypeError( "watchConnectivityState's third argument must be a callback"); } grpc_connectivity_state last_state = - static_cast<grpc_connectivity_state>(args[0]->Uint32Value()); - double deadline = args[1]->NumberValue(); - Handle<Function> callback_func = args[2].As<Function>(); - NanCallback *callback = new NanCallback(callback_func); - Channel *channel = ObjectWrap::Unwrap<Channel>(args.This()); + static_cast<grpc_connectivity_state>( + Nan::To<uint32_t>(info[0]).FromJust()); + double deadline = Nan::To<double>(info[1]).FromJust(); + Local<Function> callback_func = info[2].As<Function>(); + Nan::Callback *callback = new Callback(callback_func); + Channel *channel = ObjectWrap::Unwrap<Channel>(info.This()); unique_ptr<OpVec> ops(new OpVec()); grpc_channel_watch_connectivity_state( channel->wrapped_channel, last_state, MillisecondsToTimespec(deadline), @@ -242,7 +281,6 @@ NAN_METHOD(Channel::WatchConnectivityState) { ops.release(), shared_ptr<Resources>(nullptr))); CompletionQueueAsyncWorker::Next(); - NanReturnUndefined(); } } // namespace node diff --git a/src/node/ext/channel.h b/src/node/ext/channel.h index 458f71d093..9ec28e15af 100644 --- a/src/node/ext/channel.h +++ b/src/node/ext/channel.h @@ -41,11 +41,16 @@ namespace grpc { namespace node { +bool ParseChannelArgs(v8::Local<v8::Value> args_val, + grpc_channel_args **channel_args_ptr); + +void DeallocateChannelArgs(grpc_channel_args *channel_args); + /* Wrapper class for grpc_channel structs */ -class Channel : public ::node::ObjectWrap { +class Channel : public Nan::ObjectWrap { public: - static void Init(v8::Handle<v8::Object> exports); - static bool HasInstance(v8::Handle<v8::Value> val); + static void Init(v8::Local<v8::Object> exports); + static bool HasInstance(v8::Local<v8::Value> val); /* This is used to typecheck javascript objects before converting them to this type */ static v8::Persistent<v8::Value> prototype; @@ -66,8 +71,8 @@ class Channel : public ::node::ObjectWrap { static NAN_METHOD(GetTarget); static NAN_METHOD(GetConnectivityState); static NAN_METHOD(WatchConnectivityState); - static NanCallback *constructor; - static v8::Persistent<v8::FunctionTemplate> fun_tpl; + static Nan::Callback *constructor; + static Nan::Persistent<v8::FunctionTemplate> fun_tpl; grpc_channel *wrapped_channel; }; diff --git a/src/node/ext/channel_credentials.cc b/src/node/ext/channel_credentials.cc new file mode 100644 index 0000000000..7ca3b9816c --- /dev/null +++ b/src/node/ext/channel_credentials.cc @@ -0,0 +1,197 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <node.h> + +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "grpc/support/log.h" +#include "channel_credentials.h" +#include "call_credentials.h" +#include "call.h" + +namespace grpc { +namespace node { + +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + +using v8::Exception; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Integer; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::Value; + +Nan::Callback *ChannelCredentials::constructor; +Persistent<FunctionTemplate> ChannelCredentials::fun_tpl; + +ChannelCredentials::ChannelCredentials(grpc_credentials *credentials) + : wrapped_credentials(credentials) {} + +ChannelCredentials::~ChannelCredentials() { + grpc_credentials_release(wrapped_credentials); +} + +void ChannelCredentials::Init(Local<Object> exports) { + HandleScope scope; + Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); + tpl->SetClassName(Nan::New("ChannelCredentials").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + Nan::SetPrototypeMethod(tpl, "compose", Compose); + fun_tpl.Reset(tpl); + Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(), + Nan::GetFunction( + Nan::New<FunctionTemplate>(CreateSsl)).ToLocalChecked()); + Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(), + Nan::GetFunction( + Nan::New<FunctionTemplate>(CreateInsecure)).ToLocalChecked()); + Nan::Set(exports, Nan::New("ChannelCredentials").ToLocalChecked(), ctr); + constructor = new Nan::Callback(ctr); +} + +bool ChannelCredentials::HasInstance(Local<Value> val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); +} + +Local<Value> ChannelCredentials::WrapStruct(grpc_credentials *credentials) { + EscapableHandleScope scope; + const int argc = 1; + Local<Value> argv[argc] = { + Nan::New<External>(reinterpret_cast<void *>(credentials))}; + MaybeLocal<Object> maybe_instance = Nan::NewInstance( + constructor->GetFunction(), argc, argv); + if (maybe_instance.IsEmpty()) { + return scope.Escape(Nan::Null()); + } else { + return scope.Escape(maybe_instance.ToLocalChecked()); + } +} + +grpc_credentials *ChannelCredentials::GetWrappedCredentials() { + return wrapped_credentials; +} + +NAN_METHOD(ChannelCredentials::New) { + if (info.IsConstructCall()) { + if (!info[0]->IsExternal()) { + return Nan::ThrowTypeError( + "ChannelCredentials can only be created with the provided functions"); + } + Local<External> ext = info[0].As<External>(); + grpc_credentials *creds_value = + reinterpret_cast<grpc_credentials *>(ext->Value()); + ChannelCredentials *credentials = new ChannelCredentials(creds_value); + credentials->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + return; + } else { + // This should never be called directly + return Nan::ThrowTypeError( + "ChannelCredentials can only be created with the provided functions"); + } +} + +NAN_METHOD(ChannelCredentials::CreateSsl) { + char *root_certs = NULL; + grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL}; + if (::node::Buffer::HasInstance(info[0])) { + root_certs = ::node::Buffer::Data(info[0]); + } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) { + return Nan::ThrowTypeError("createSsl's first argument must be a Buffer"); + } + if (::node::Buffer::HasInstance(info[1])) { + key_cert_pair.private_key = ::node::Buffer::Data(info[1]); + } else if (!(info[1]->IsNull() || info[1]->IsUndefined())) { + return Nan::ThrowTypeError( + "createSSl's second argument must be a Buffer if provided"); + } + if (::node::Buffer::HasInstance(info[2])) { + key_cert_pair.cert_chain = ::node::Buffer::Data(info[2]); + } else if (!(info[2]->IsNull() || info[2]->IsUndefined())) { + return Nan::ThrowTypeError( + "createSSl's third argument must be a Buffer if provided"); + } + grpc_credentials *creds = grpc_ssl_credentials_create( + root_certs, key_cert_pair.private_key == NULL ? NULL : &key_cert_pair, + NULL); + if (creds == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); + } +} + +NAN_METHOD(ChannelCredentials::Compose) { + if (!ChannelCredentials::HasInstance(info.This())) { + return Nan::ThrowTypeError( + "compose can only be called on ChannelCredentials objects"); + } + if (!CallCredentials::HasInstance(info[0])) { + return Nan::ThrowTypeError( + "compose's first argument must be a CallCredentials object"); + } + ChannelCredentials *self = ObjectWrap::Unwrap<ChannelCredentials>( + info.This()); + if (self->wrapped_credentials == NULL) { + return Nan::ThrowTypeError( + "Cannot compose insecure credential"); + } + CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>( + Nan::To<Object>(info[0]).ToLocalChecked()); + grpc_credentials *creds = grpc_composite_credentials_create( + self->wrapped_credentials, other->GetWrappedCredentials(), NULL); + if (creds == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); + } +} + +NAN_METHOD(ChannelCredentials::CreateInsecure) { + info.GetReturnValue().Set(WrapStruct(NULL)); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/credentials.h b/src/node/ext/channel_credentials.h index 62957e61c3..31ea0987bc 100644 --- a/src/node/ext/credentials.h +++ b/src/node/ext/channel_credentials.h @@ -31,8 +31,8 @@ * */ -#ifndef NET_GRPC_NODE_CREDENTIALS_H_ -#define NET_GRPC_NODE_CREDENTIALS_H_ +#ifndef NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_ +#define NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_ #include <node.h> #include <nan.h> @@ -43,35 +43,32 @@ namespace grpc { namespace node { /* Wrapper class for grpc_credentials structs */ -class Credentials : public ::node::ObjectWrap { +class ChannelCredentials : public Nan::ObjectWrap { public: - static void Init(v8::Handle<v8::Object> exports); - static bool HasInstance(v8::Handle<v8::Value> val); + static void Init(v8::Local<v8::Object> exports); + static bool HasInstance(v8::Local<v8::Value> val); /* Wrap a grpc_credentials struct in a javascript object */ - static v8::Handle<v8::Value> WrapStruct(grpc_credentials *credentials); + static v8::Local<v8::Value> WrapStruct(grpc_credentials *credentials); /* Returns the grpc_credentials struct that this object wraps */ grpc_credentials *GetWrappedCredentials(); private: - explicit Credentials(grpc_credentials *credentials); - ~Credentials(); + explicit ChannelCredentials(grpc_credentials *credentials); + ~ChannelCredentials(); // Prevent copying - Credentials(const Credentials &); - Credentials &operator=(const Credentials &); + ChannelCredentials(const ChannelCredentials &); + ChannelCredentials &operator=(const ChannelCredentials &); static NAN_METHOD(New); - static NAN_METHOD(CreateDefault); static NAN_METHOD(CreateSsl); - static NAN_METHOD(CreateComposite); - static NAN_METHOD(CreateGce); - static NAN_METHOD(CreateFake); - static NAN_METHOD(CreateIam); static NAN_METHOD(CreateInsecure); - static NanCallback *constructor; + + static NAN_METHOD(Compose); + static Nan::Callback *constructor; // Used for typechecking instances of this javascript class - static v8::Persistent<v8::FunctionTemplate> fun_tpl; + static Nan::Persistent<v8::FunctionTemplate> fun_tpl; grpc_credentials *wrapped_credentials; }; @@ -79,4 +76,4 @@ class Credentials : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_CREDENTIALS_H_ +#endif // NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_ diff --git a/src/node/ext/completion_queue_async_worker.cc b/src/node/ext/completion_queue_async_worker.cc index bf2cd946a5..619ea41515 100644 --- a/src/node/ext/completion_queue_async_worker.cc +++ b/src/node/ext/completion_queue_async_worker.cc @@ -46,18 +46,20 @@ namespace node { const int max_queue_threads = 2; using v8::Function; -using v8::Handle; +using v8::Local; using v8::Object; -using v8::Persistent; using v8::Value; grpc_completion_queue *CompletionQueueAsyncWorker::queue; +// Invariants: current_threads <= max_queue_threads +// (current_threads == max_queue_threads) || (waiting_next_calls == 0) + int CompletionQueueAsyncWorker::current_threads; int CompletionQueueAsyncWorker::waiting_next_calls; CompletionQueueAsyncWorker::CompletionQueueAsyncWorker() - : NanAsyncWorker(NULL) {} + : Nan::AsyncWorker(NULL) {} CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {} @@ -72,42 +74,61 @@ void CompletionQueueAsyncWorker::Execute() { grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; } void CompletionQueueAsyncWorker::Next() { - NanScope(); + Nan::HandleScope scope; if (current_threads < max_queue_threads) { + current_threads += 1; CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker(); - NanAsyncQueueWorker(worker); + Nan::AsyncQueueWorker(worker); } else { waiting_next_calls += 1; } + GPR_ASSERT(current_threads <= max_queue_threads); + GPR_ASSERT((current_threads == max_queue_threads) || + (waiting_next_calls == 0)); } -void CompletionQueueAsyncWorker::Init(Handle<Object> exports) { - NanScope(); +void CompletionQueueAsyncWorker::Init(Local<Object> exports) { + Nan::HandleScope scope; current_threads = 0; waiting_next_calls = 0; queue = grpc_completion_queue_create(NULL); } void CompletionQueueAsyncWorker::HandleOKCallback() { - NanScope(); + Nan::HandleScope scope; if (waiting_next_calls > 0) { waiting_next_calls -= 1; + // Old worker removed, new worker added. current_threads += 0 CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker(); - NanAsyncQueueWorker(worker); + Nan::AsyncQueueWorker(worker); } else { current_threads -= 1; } - NanCallback *callback = GetTagCallback(result.tag); - Handle<Value> argv[] = {NanNull(), GetTagNodeValue(result.tag)}; + GPR_ASSERT(current_threads <= max_queue_threads); + GPR_ASSERT((current_threads == max_queue_threads) || + (waiting_next_calls == 0)); + Nan::Callback *callback = GetTagCallback(result.tag); + Local<Value> argv[] = {Nan::Null(), GetTagNodeValue(result.tag)}; callback->Call(2, argv); DestroyTag(result.tag); } void CompletionQueueAsyncWorker::HandleErrorCallback() { - NanScope(); - NanCallback *callback = GetTagCallback(result.tag); - Handle<Value> argv[] = {NanError(ErrorMessage())}; + if (waiting_next_calls > 0) { + waiting_next_calls -= 1; + // Old worker removed, new worker added. current_threads += 0 + CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker(); + Nan::AsyncQueueWorker(worker); + } else { + current_threads -= 1; + } + GPR_ASSERT(current_threads <= max_queue_threads); + GPR_ASSERT((current_threads == max_queue_threads) || + (waiting_next_calls == 0)); + Nan::HandleScope scope; + Nan::Callback *callback = GetTagCallback(result.tag); + Local<Value> argv[] = {Nan::Error(ErrorMessage())}; callback->Call(1, argv); diff --git a/src/node/ext/completion_queue_async_worker.h b/src/node/ext/completion_queue_async_worker.h index 27fedf2fce..6e54116765 100644 --- a/src/node/ext/completion_queue_async_worker.h +++ b/src/node/ext/completion_queue_async_worker.h @@ -42,7 +42,7 @@ namespace node { /* A worker that asynchronously calls completion_queue_next, and queues onto the node event loop a call to the function stored in the event's tag. */ -class CompletionQueueAsyncWorker : public NanAsyncWorker { +class CompletionQueueAsyncWorker : public Nan::AsyncWorker { public: CompletionQueueAsyncWorker(); @@ -59,7 +59,7 @@ class CompletionQueueAsyncWorker : public NanAsyncWorker { static void Next(); /* Initialize the CompletionQueueAsyncWorker class */ - static void Init(v8::Handle<v8::Object> exports); + static void Init(v8::Local<v8::Object> exports); protected: /* Called when Execute has succeeded (completed without setting an error diff --git a/src/node/ext/credentials.cc b/src/node/ext/credentials.cc deleted file mode 100644 index c3b04dcea7..0000000000 --- a/src/node/ext/credentials.cc +++ /dev/null @@ -1,220 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include <node.h> - -#include "grpc/grpc.h" -#include "grpc/grpc_security.h" -#include "grpc/support/log.h" -#include "credentials.h" - -namespace grpc { -namespace node { - -using v8::Exception; -using v8::External; -using v8::Function; -using v8::FunctionTemplate; -using v8::Handle; -using v8::HandleScope; -using v8::Integer; -using v8::Local; -using v8::Object; -using v8::ObjectTemplate; -using v8::Persistent; -using v8::Value; - -NanCallback *Credentials::constructor; -Persistent<FunctionTemplate> Credentials::fun_tpl; - -Credentials::Credentials(grpc_credentials *credentials) - : wrapped_credentials(credentials) {} - -Credentials::~Credentials() { - grpc_credentials_release(wrapped_credentials); -} - -void Credentials::Init(Handle<Object> exports) { - NanScope(); - Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New); - tpl->SetClassName(NanNew("Credentials")); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - NanAssignPersistent(fun_tpl, tpl); - Handle<Function> ctr = tpl->GetFunction(); - ctr->Set(NanNew("createDefault"), - NanNew<FunctionTemplate>(CreateDefault)->GetFunction()); - ctr->Set(NanNew("createSsl"), - NanNew<FunctionTemplate>(CreateSsl)->GetFunction()); - ctr->Set(NanNew("createComposite"), - NanNew<FunctionTemplate>(CreateComposite)->GetFunction()); - ctr->Set(NanNew("createGce"), - NanNew<FunctionTemplate>(CreateGce)->GetFunction()); - ctr->Set(NanNew("createIam"), - NanNew<FunctionTemplate>(CreateIam)->GetFunction()); - ctr->Set(NanNew("createInsecure"), - NanNew<FunctionTemplate>(CreateInsecure)->GetFunction()); - constructor = new NanCallback(ctr); - exports->Set(NanNew("Credentials"), ctr); -} - -bool Credentials::HasInstance(Handle<Value> val) { - NanScope(); - return NanHasInstance(fun_tpl, val); -} - -Handle<Value> Credentials::WrapStruct(grpc_credentials *credentials) { - NanEscapableScope(); - const int argc = 1; - Handle<Value> argv[argc] = { - NanNew<External>(reinterpret_cast<void *>(credentials))}; - return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv)); -} - -grpc_credentials *Credentials::GetWrappedCredentials() { - return wrapped_credentials; -} - -NAN_METHOD(Credentials::New) { - NanScope(); - - if (args.IsConstructCall()) { - if (!args[0]->IsExternal()) { - return NanThrowTypeError( - "Credentials can only be created with the provided functions"); - } - Handle<External> ext = args[0].As<External>(); - grpc_credentials *creds_value = - reinterpret_cast<grpc_credentials *>(ext->Value()); - Credentials *credentials = new Credentials(creds_value); - credentials->Wrap(args.This()); - NanReturnValue(args.This()); - } else { - const int argc = 1; - Local<Value> argv[argc] = {args[0]}; - NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); - } -} - -NAN_METHOD(Credentials::CreateDefault) { - NanScope(); - grpc_credentials *creds = grpc_google_default_credentials_create(); - if (creds == NULL) { - NanReturnNull(); - } - NanReturnValue(WrapStruct(creds)); -} - -NAN_METHOD(Credentials::CreateSsl) { - NanScope(); - char *root_certs = NULL; - grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL}; - if (::node::Buffer::HasInstance(args[0])) { - root_certs = ::node::Buffer::Data(args[0]); - } else if (!(args[0]->IsNull() || args[0]->IsUndefined())) { - return NanThrowTypeError("createSsl's first argument must be a Buffer"); - } - if (::node::Buffer::HasInstance(args[1])) { - key_cert_pair.private_key = ::node::Buffer::Data(args[1]); - } else if (!(args[1]->IsNull() || args[1]->IsUndefined())) { - return NanThrowTypeError( - "createSSl's second argument must be a Buffer if provided"); - } - if (::node::Buffer::HasInstance(args[2])) { - key_cert_pair.cert_chain = ::node::Buffer::Data(args[2]); - } else if (!(args[2]->IsNull() || args[2]->IsUndefined())) { - return NanThrowTypeError( - "createSSl's third argument must be a Buffer if provided"); - } - grpc_credentials *creds = grpc_ssl_credentials_create( - root_certs, key_cert_pair.private_key == NULL ? NULL : &key_cert_pair, - NULL); - if (creds == NULL) { - NanReturnNull(); - } - NanReturnValue(WrapStruct(creds)); -} - -NAN_METHOD(Credentials::CreateComposite) { - NanScope(); - if (!HasInstance(args[0])) { - return NanThrowTypeError( - "createComposite's first argument must be a Credentials object"); - } - if (!HasInstance(args[1])) { - return NanThrowTypeError( - "createComposite's second argument must be a Credentials object"); - } - Credentials *creds1 = ObjectWrap::Unwrap<Credentials>(args[0]->ToObject()); - Credentials *creds2 = ObjectWrap::Unwrap<Credentials>(args[1]->ToObject()); - grpc_credentials *creds = grpc_composite_credentials_create( - creds1->wrapped_credentials, creds2->wrapped_credentials, NULL); - if (creds == NULL) { - NanReturnNull(); - } - NanReturnValue(WrapStruct(creds)); -} - -NAN_METHOD(Credentials::CreateGce) { - NanScope(); - grpc_credentials *creds = grpc_google_compute_engine_credentials_create(NULL); - if (creds == NULL) { - NanReturnNull(); - } - NanReturnValue(WrapStruct(creds)); -} - -NAN_METHOD(Credentials::CreateIam) { - NanScope(); - if (!args[0]->IsString()) { - return NanThrowTypeError("createIam's first argument must be a string"); - } - if (!args[1]->IsString()) { - return NanThrowTypeError("createIam's second argument must be a string"); - } - NanUtf8String auth_token(args[0]); - NanUtf8String auth_selector(args[1]); - grpc_credentials *creds = - grpc_google_iam_credentials_create(*auth_token, *auth_selector, NULL); - if (creds == NULL) { - NanReturnNull(); - } - NanReturnValue(WrapStruct(creds)); -} - -NAN_METHOD(Credentials::CreateInsecure) { - NanScope(); - NanReturnValue(WrapStruct(NULL)); -} - -} // namespace node -} // namespace grpc diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc index 0cf30da922..5b5f3c1c5b 100644 --- a/src/node/ext/node_grpc.cc +++ b/src/node/ext/node_grpc.cc @@ -37,177 +37,201 @@ #include "grpc/grpc.h" #include "call.h" +#include "call_credentials.h" #include "channel.h" +#include "channel_credentials.h" #include "server.h" #include "completion_queue_async_worker.h" -#include "credentials.h" #include "server_credentials.h" -using v8::Handle; +using v8::Local; using v8::Value; using v8::Object; using v8::Uint32; using v8::String; -void InitStatusConstants(Handle<Object> exports) { - NanScope(); - Handle<Object> status = NanNew<Object>(); - exports->Set(NanNew("status"), status); - Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_STATUS_OK)); - status->Set(NanNew("OK"), OK); - Handle<Value> CANCELLED(NanNew<Uint32, uint32_t>(GRPC_STATUS_CANCELLED)); - status->Set(NanNew("CANCELLED"), CANCELLED); - Handle<Value> UNKNOWN(NanNew<Uint32, uint32_t>(GRPC_STATUS_UNKNOWN)); - status->Set(NanNew("UNKNOWN"), UNKNOWN); - Handle<Value> INVALID_ARGUMENT( - NanNew<Uint32, uint32_t>(GRPC_STATUS_INVALID_ARGUMENT)); - status->Set(NanNew("INVALID_ARGUMENT"), INVALID_ARGUMENT); - Handle<Value> DEADLINE_EXCEEDED( - NanNew<Uint32, uint32_t>(GRPC_STATUS_DEADLINE_EXCEEDED)); - status->Set(NanNew("DEADLINE_EXCEEDED"), DEADLINE_EXCEEDED); - Handle<Value> NOT_FOUND(NanNew<Uint32, uint32_t>(GRPC_STATUS_NOT_FOUND)); - status->Set(NanNew("NOT_FOUND"), NOT_FOUND); - Handle<Value> ALREADY_EXISTS( - NanNew<Uint32, uint32_t>(GRPC_STATUS_ALREADY_EXISTS)); - status->Set(NanNew("ALREADY_EXISTS"), ALREADY_EXISTS); - Handle<Value> PERMISSION_DENIED( - NanNew<Uint32, uint32_t>(GRPC_STATUS_PERMISSION_DENIED)); - status->Set(NanNew("PERMISSION_DENIED"), PERMISSION_DENIED); - Handle<Value> UNAUTHENTICATED( - NanNew<Uint32, uint32_t>(GRPC_STATUS_UNAUTHENTICATED)); - status->Set(NanNew("UNAUTHENTICATED"), UNAUTHENTICATED); - Handle<Value> RESOURCE_EXHAUSTED( - NanNew<Uint32, uint32_t>(GRPC_STATUS_RESOURCE_EXHAUSTED)); - status->Set(NanNew("RESOURCE_EXHAUSTED"), RESOURCE_EXHAUSTED); - Handle<Value> FAILED_PRECONDITION( - NanNew<Uint32, uint32_t>(GRPC_STATUS_FAILED_PRECONDITION)); - status->Set(NanNew("FAILED_PRECONDITION"), FAILED_PRECONDITION); - Handle<Value> ABORTED(NanNew<Uint32, uint32_t>(GRPC_STATUS_ABORTED)); - status->Set(NanNew("ABORTED"), ABORTED); - Handle<Value> OUT_OF_RANGE( - NanNew<Uint32, uint32_t>(GRPC_STATUS_OUT_OF_RANGE)); - status->Set(NanNew("OUT_OF_RANGE"), OUT_OF_RANGE); - Handle<Value> UNIMPLEMENTED( - NanNew<Uint32, uint32_t>(GRPC_STATUS_UNIMPLEMENTED)); - status->Set(NanNew("UNIMPLEMENTED"), UNIMPLEMENTED); - Handle<Value> INTERNAL(NanNew<Uint32, uint32_t>(GRPC_STATUS_INTERNAL)); - status->Set(NanNew("INTERNAL"), INTERNAL); - Handle<Value> UNAVAILABLE(NanNew<Uint32, uint32_t>(GRPC_STATUS_UNAVAILABLE)); - status->Set(NanNew("UNAVAILABLE"), UNAVAILABLE); - Handle<Value> DATA_LOSS(NanNew<Uint32, uint32_t>(GRPC_STATUS_DATA_LOSS)); - status->Set(NanNew("DATA_LOSS"), DATA_LOSS); +void InitStatusConstants(Local<Object> exports) { + Nan::HandleScope scope; + Local<Object> status = Nan::New<Object>(); + Nan::Set(exports, Nan::New("status").ToLocalChecked(), status); + Local<Value> OK(Nan::New<Uint32, uint32_t>(GRPC_STATUS_OK)); + Nan::Set(status, Nan::New("OK").ToLocalChecked(), OK); + Local<Value> CANCELLED(Nan::New<Uint32, uint32_t>(GRPC_STATUS_CANCELLED)); + Nan::Set(status, Nan::New("CANCELLED").ToLocalChecked(), CANCELLED); + Local<Value> UNKNOWN(Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNKNOWN)); + Nan::Set(status, Nan::New("UNKNOWN").ToLocalChecked(), UNKNOWN); + Local<Value> INVALID_ARGUMENT( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_INVALID_ARGUMENT)); + Nan::Set(status, Nan::New("INVALID_ARGUMENT").ToLocalChecked(), + INVALID_ARGUMENT); + Local<Value> DEADLINE_EXCEEDED( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_DEADLINE_EXCEEDED)); + Nan::Set(status, Nan::New("DEADLINE_EXCEEDED").ToLocalChecked(), + DEADLINE_EXCEEDED); + Local<Value> NOT_FOUND(Nan::New<Uint32, uint32_t>(GRPC_STATUS_NOT_FOUND)); + Nan::Set(status, Nan::New("NOT_FOUND").ToLocalChecked(), NOT_FOUND); + Local<Value> ALREADY_EXISTS( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_ALREADY_EXISTS)); + Nan::Set(status, Nan::New("ALREADY_EXISTS").ToLocalChecked(), ALREADY_EXISTS); + Local<Value> PERMISSION_DENIED( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_PERMISSION_DENIED)); + Nan::Set(status, Nan::New("PERMISSION_DENIED").ToLocalChecked(), + PERMISSION_DENIED); + Local<Value> UNAUTHENTICATED( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNAUTHENTICATED)); + Nan::Set(status, Nan::New("UNAUTHENTICATED").ToLocalChecked(), + UNAUTHENTICATED); + Local<Value> RESOURCE_EXHAUSTED( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_RESOURCE_EXHAUSTED)); + Nan::Set(status, Nan::New("RESOURCE_EXHAUSTED").ToLocalChecked(), + RESOURCE_EXHAUSTED); + Local<Value> FAILED_PRECONDITION( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_FAILED_PRECONDITION)); + Nan::Set(status, Nan::New("FAILED_PRECONDITION").ToLocalChecked(), + FAILED_PRECONDITION); + Local<Value> ABORTED(Nan::New<Uint32, uint32_t>(GRPC_STATUS_ABORTED)); + Nan::Set(status, Nan::New("ABORTED").ToLocalChecked(), ABORTED); + Local<Value> OUT_OF_RANGE( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_OUT_OF_RANGE)); + Nan::Set(status, Nan::New("OUT_OF_RANGE").ToLocalChecked(), OUT_OF_RANGE); + Local<Value> UNIMPLEMENTED( + Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNIMPLEMENTED)); + Nan::Set(status, Nan::New("UNIMPLEMENTED").ToLocalChecked(), UNIMPLEMENTED); + Local<Value> INTERNAL(Nan::New<Uint32, uint32_t>(GRPC_STATUS_INTERNAL)); + Nan::Set(status, Nan::New("INTERNAL").ToLocalChecked(), INTERNAL); + Local<Value> UNAVAILABLE(Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNAVAILABLE)); + Nan::Set(status, Nan::New("UNAVAILABLE").ToLocalChecked(), UNAVAILABLE); + Local<Value> DATA_LOSS(Nan::New<Uint32, uint32_t>(GRPC_STATUS_DATA_LOSS)); + Nan::Set(status, Nan::New("DATA_LOSS").ToLocalChecked(), DATA_LOSS); } -void InitCallErrorConstants(Handle<Object> exports) { - NanScope(); - Handle<Object> call_error = NanNew<Object>(); - exports->Set(NanNew("callError"), call_error); - Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_CALL_OK)); - call_error->Set(NanNew("OK"), OK); - Handle<Value> ERROR(NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR)); - call_error->Set(NanNew("ERROR"), ERROR); - Handle<Value> NOT_ON_SERVER( - NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_SERVER)); - call_error->Set(NanNew("NOT_ON_SERVER"), NOT_ON_SERVER); - Handle<Value> NOT_ON_CLIENT( - NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_CLIENT)); - call_error->Set(NanNew("NOT_ON_CLIENT"), NOT_ON_CLIENT); - Handle<Value> ALREADY_INVOKED( - NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_INVOKED)); - call_error->Set(NanNew("ALREADY_INVOKED"), ALREADY_INVOKED); - Handle<Value> NOT_INVOKED( - NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_INVOKED)); - call_error->Set(NanNew("NOT_INVOKED"), NOT_INVOKED); - Handle<Value> ALREADY_FINISHED( - NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_FINISHED)); - call_error->Set(NanNew("ALREADY_FINISHED"), ALREADY_FINISHED); - Handle<Value> TOO_MANY_OPERATIONS( - NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS)); - call_error->Set(NanNew("TOO_MANY_OPERATIONS"), TOO_MANY_OPERATIONS); - Handle<Value> INVALID_FLAGS( - NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_INVALID_FLAGS)); - call_error->Set(NanNew("INVALID_FLAGS"), INVALID_FLAGS); +void InitCallErrorConstants(Local<Object> exports) { + Nan::HandleScope scope; + Local<Object> call_error = Nan::New<Object>(); + Nan::Set(exports, Nan::New("callError").ToLocalChecked(), call_error); + Local<Value> OK(Nan::New<Uint32, uint32_t>(GRPC_CALL_OK)); + Nan::Set(call_error, Nan::New("OK").ToLocalChecked(), OK); + Local<Value> ERROR(Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR)); + Nan::Set(call_error, Nan::New("ERROR").ToLocalChecked(), ERROR); + Local<Value> NOT_ON_SERVER( + Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_SERVER)); + Nan::Set(call_error, Nan::New("NOT_ON_SERVER").ToLocalChecked(), + NOT_ON_SERVER); + Local<Value> NOT_ON_CLIENT( + Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_CLIENT)); + Nan::Set(call_error, Nan::New("NOT_ON_CLIENT").ToLocalChecked(), + NOT_ON_CLIENT); + Local<Value> ALREADY_INVOKED( + Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_INVOKED)); + Nan::Set(call_error, Nan::New("ALREADY_INVOKED").ToLocalChecked(), + ALREADY_INVOKED); + Local<Value> NOT_INVOKED( + Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_INVOKED)); + Nan::Set(call_error, Nan::New("NOT_INVOKED").ToLocalChecked(), NOT_INVOKED); + Local<Value> ALREADY_FINISHED( + Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_FINISHED)); + Nan::Set(call_error, Nan::New("ALREADY_FINISHED").ToLocalChecked(), + ALREADY_FINISHED); + Local<Value> TOO_MANY_OPERATIONS( + Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS)); + Nan::Set(call_error, Nan::New("TOO_MANY_OPERATIONS").ToLocalChecked(), + TOO_MANY_OPERATIONS); + Local<Value> INVALID_FLAGS( + Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_INVALID_FLAGS)); + Nan::Set(call_error, Nan::New("INVALID_FLAGS").ToLocalChecked(), + INVALID_FLAGS); } -void InitOpTypeConstants(Handle<Object> exports) { - NanScope(); - Handle<Object> op_type = NanNew<Object>(); - exports->Set(NanNew("opType"), op_type); - Handle<Value> SEND_INITIAL_METADATA( - NanNew<Uint32, uint32_t>(GRPC_OP_SEND_INITIAL_METADATA)); - op_type->Set(NanNew("SEND_INITIAL_METADATA"), SEND_INITIAL_METADATA); - Handle<Value> SEND_MESSAGE( - NanNew<Uint32, uint32_t>(GRPC_OP_SEND_MESSAGE)); - op_type->Set(NanNew("SEND_MESSAGE"), SEND_MESSAGE); - Handle<Value> SEND_CLOSE_FROM_CLIENT( - NanNew<Uint32, uint32_t>(GRPC_OP_SEND_CLOSE_FROM_CLIENT)); - op_type->Set(NanNew("SEND_CLOSE_FROM_CLIENT"), SEND_CLOSE_FROM_CLIENT); - Handle<Value> SEND_STATUS_FROM_SERVER( - NanNew<Uint32, uint32_t>(GRPC_OP_SEND_STATUS_FROM_SERVER)); - op_type->Set(NanNew("SEND_STATUS_FROM_SERVER"), SEND_STATUS_FROM_SERVER); - Handle<Value> RECV_INITIAL_METADATA( - NanNew<Uint32, uint32_t>(GRPC_OP_RECV_INITIAL_METADATA)); - op_type->Set(NanNew("RECV_INITIAL_METADATA"), RECV_INITIAL_METADATA); - Handle<Value> RECV_MESSAGE( - NanNew<Uint32, uint32_t>(GRPC_OP_RECV_MESSAGE)); - op_type->Set(NanNew("RECV_MESSAGE"), RECV_MESSAGE); - Handle<Value> RECV_STATUS_ON_CLIENT( - NanNew<Uint32, uint32_t>(GRPC_OP_RECV_STATUS_ON_CLIENT)); - op_type->Set(NanNew("RECV_STATUS_ON_CLIENT"), RECV_STATUS_ON_CLIENT); - Handle<Value> RECV_CLOSE_ON_SERVER( - NanNew<Uint32, uint32_t>(GRPC_OP_RECV_CLOSE_ON_SERVER)); - op_type->Set(NanNew("RECV_CLOSE_ON_SERVER"), RECV_CLOSE_ON_SERVER); +void InitOpTypeConstants(Local<Object> exports) { + Nan::HandleScope scope; + Local<Object> op_type = Nan::New<Object>(); + Nan::Set(exports, Nan::New("opType").ToLocalChecked(), op_type); + Local<Value> SEND_INITIAL_METADATA( + Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_INITIAL_METADATA)); + Nan::Set(op_type, Nan::New("SEND_INITIAL_METADATA").ToLocalChecked(), + SEND_INITIAL_METADATA); + Local<Value> SEND_MESSAGE( + Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_MESSAGE)); + Nan::Set(op_type, Nan::New("SEND_MESSAGE").ToLocalChecked(), SEND_MESSAGE); + Local<Value> SEND_CLOSE_FROM_CLIENT( + Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_CLOSE_FROM_CLIENT)); + Nan::Set(op_type, Nan::New("SEND_CLOSE_FROM_CLIENT").ToLocalChecked(), + SEND_CLOSE_FROM_CLIENT); + Local<Value> SEND_STATUS_FROM_SERVER( + Nan::New<Uint32, uint32_t>(GRPC_OP_SEND_STATUS_FROM_SERVER)); + Nan::Set(op_type, Nan::New("SEND_STATUS_FROM_SERVER").ToLocalChecked(), + SEND_STATUS_FROM_SERVER); + Local<Value> RECV_INITIAL_METADATA( + Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_INITIAL_METADATA)); + Nan::Set(op_type, Nan::New("RECV_INITIAL_METADATA").ToLocalChecked(), + RECV_INITIAL_METADATA); + Local<Value> RECV_MESSAGE( + Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_MESSAGE)); + Nan::Set(op_type, Nan::New("RECV_MESSAGE").ToLocalChecked(), RECV_MESSAGE); + Local<Value> RECV_STATUS_ON_CLIENT( + Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_STATUS_ON_CLIENT)); + Nan::Set(op_type, Nan::New("RECV_STATUS_ON_CLIENT").ToLocalChecked(), + RECV_STATUS_ON_CLIENT); + Local<Value> RECV_CLOSE_ON_SERVER( + Nan::New<Uint32, uint32_t>(GRPC_OP_RECV_CLOSE_ON_SERVER)); + Nan::Set(op_type, Nan::New("RECV_CLOSE_ON_SERVER").ToLocalChecked(), + RECV_CLOSE_ON_SERVER); } -void InitPropagateConstants(Handle<Object> exports) { - NanScope(); - Handle<Object> propagate = NanNew<Object>(); - exports->Set(NanNew("propagate"), propagate); - Handle<Value> DEADLINE(NanNew<Uint32, uint32_t>(GRPC_PROPAGATE_DEADLINE)); - propagate->Set(NanNew("DEADLINE"), DEADLINE); - Handle<Value> CENSUS_STATS_CONTEXT( - NanNew<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)); - propagate->Set(NanNew("CENSUS_STATS_CONTEXT"), CENSUS_STATS_CONTEXT); - Handle<Value> CENSUS_TRACING_CONTEXT( - NanNew<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT)); - propagate->Set(NanNew("CENSUS_TRACING_CONTEXT"), CENSUS_TRACING_CONTEXT); - Handle<Value> CANCELLATION( - NanNew<Uint32, uint32_t>(GRPC_PROPAGATE_CANCELLATION)); - propagate->Set(NanNew("CANCELLATION"), CANCELLATION); - Handle<Value> DEFAULTS(NanNew<Uint32, uint32_t>(GRPC_PROPAGATE_DEFAULTS)); - propagate->Set(NanNew("DEFAULTS"), DEFAULTS); +void InitPropagateConstants(Local<Object> exports) { + Nan::HandleScope scope; + Local<Object> propagate = Nan::New<Object>(); + Nan::Set(exports, Nan::New("propagate").ToLocalChecked(), propagate); + Local<Value> DEADLINE(Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_DEADLINE)); + Nan::Set(propagate, Nan::New("DEADLINE").ToLocalChecked(), DEADLINE); + Local<Value> CENSUS_STATS_CONTEXT( + Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)); + Nan::Set(propagate, Nan::New("CENSUS_STATS_CONTEXT").ToLocalChecked(), + CENSUS_STATS_CONTEXT); + Local<Value> CENSUS_TRACING_CONTEXT( + Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT)); + Nan::Set(propagate, Nan::New("CENSUS_TRACING_CONTEXT").ToLocalChecked(), + CENSUS_TRACING_CONTEXT); + Local<Value> CANCELLATION( + Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CANCELLATION)); + Nan::Set(propagate, Nan::New("CANCELLATION").ToLocalChecked(), CANCELLATION); + Local<Value> DEFAULTS(Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_DEFAULTS)); + Nan::Set(propagate, Nan::New("DEFAULTS").ToLocalChecked(), DEFAULTS); } -void InitConnectivityStateConstants(Handle<Object> exports) { - NanScope(); - Handle<Object> channel_state = NanNew<Object>(); - exports->Set(NanNew("connectivityState"), channel_state); - Handle<Value> IDLE(NanNew<Uint32, uint32_t>(GRPC_CHANNEL_IDLE)); - channel_state->Set(NanNew("IDLE"), IDLE); - Handle<Value> CONNECTING(NanNew<Uint32, uint32_t>(GRPC_CHANNEL_CONNECTING)); - channel_state->Set(NanNew("CONNECTING"), CONNECTING); - Handle<Value> READY(NanNew<Uint32, uint32_t>(GRPC_CHANNEL_READY)); - channel_state->Set(NanNew("READY"), READY); - Handle<Value> TRANSIENT_FAILURE( - NanNew<Uint32, uint32_t>(GRPC_CHANNEL_TRANSIENT_FAILURE)); - channel_state->Set(NanNew("TRANSIENT_FAILURE"), TRANSIENT_FAILURE); - Handle<Value> FATAL_FAILURE( - NanNew<Uint32, uint32_t>(GRPC_CHANNEL_FATAL_FAILURE)); - channel_state->Set(NanNew("FATAL_FAILURE"), FATAL_FAILURE); +void InitConnectivityStateConstants(Local<Object> exports) { + Nan::HandleScope scope; + Local<Object> channel_state = Nan::New<Object>(); + Nan::Set(exports, Nan::New("connectivityState").ToLocalChecked(), + channel_state); + Local<Value> IDLE(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_IDLE)); + Nan::Set(channel_state, Nan::New("IDLE").ToLocalChecked(), IDLE); + Local<Value> CONNECTING(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_CONNECTING)); + Nan::Set(channel_state, Nan::New("CONNECTING").ToLocalChecked(), CONNECTING); + Local<Value> READY(Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_READY)); + Nan::Set(channel_state, Nan::New("READY").ToLocalChecked(), READY); + Local<Value> TRANSIENT_FAILURE( + Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_TRANSIENT_FAILURE)); + Nan::Set(channel_state, Nan::New("TRANSIENT_FAILURE").ToLocalChecked(), + TRANSIENT_FAILURE); + Local<Value> FATAL_FAILURE( + Nan::New<Uint32, uint32_t>(GRPC_CHANNEL_FATAL_FAILURE)); + Nan::Set(channel_state, Nan::New("FATAL_FAILURE").ToLocalChecked(), + FATAL_FAILURE); } -void InitWriteFlags(Handle<Object> exports) { - NanScope(); - Handle<Object> write_flags = NanNew<Object>(); - exports->Set(NanNew("writeFlags"), write_flags); - Handle<Value> BUFFER_HINT(NanNew<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT)); - write_flags->Set(NanNew("BUFFER_HINT"), BUFFER_HINT); - Handle<Value> NO_COMPRESS(NanNew<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS)); - write_flags->Set(NanNew("NO_COMPRESS"), NO_COMPRESS); +void InitWriteFlags(Local<Object> exports) { + Nan::HandleScope scope; + Local<Object> write_flags = Nan::New<Object>(); + Nan::Set(exports, Nan::New("writeFlags").ToLocalChecked(), write_flags); + Local<Value> BUFFER_HINT(Nan::New<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT)); + Nan::Set(write_flags, Nan::New("BUFFER_HINT").ToLocalChecked(), BUFFER_HINT); + Local<Value> NO_COMPRESS(Nan::New<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS)); + Nan::Set(write_flags, Nan::New("NO_COMPRESS").ToLocalChecked(), NO_COMPRESS); } -void init(Handle<Object> exports) { - NanScope(); +void init(Local<Object> exports) { + Nan::HandleScope scope; grpc_init(); InitStatusConstants(exports); InitCallErrorConstants(exports); @@ -217,11 +241,12 @@ void init(Handle<Object> exports) { InitWriteFlags(exports); grpc::node::Call::Init(exports); + grpc::node::CallCredentials::Init(exports); grpc::node::Channel::Init(exports); + grpc::node::ChannelCredentials::Init(exports); grpc::node::Server::Init(exports); grpc::node::CompletionQueueAsyncWorker::Init(exports); - grpc::node::Credentials::Init(exports); grpc::node::ServerCredentials::Init(exports); } -NODE_MODULE(grpc, init) +NODE_MODULE(grpc_node, init) diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc index 32a8ff55b1..b9e1fe9160 100644 --- a/src/node/ext/server.cc +++ b/src/node/ext/server.cc @@ -50,6 +50,15 @@ namespace grpc { namespace node { +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + using std::unique_ptr; using v8::Array; using v8::Boolean; @@ -57,16 +66,13 @@ using v8::Date; using v8::Exception; using v8::Function; using v8::FunctionTemplate; -using v8::Handle; -using v8::HandleScope; using v8::Local; using v8::Number; using v8::Object; -using v8::Persistent; using v8::String; using v8::Value; -NanCallback *Server::constructor; +Nan::Callback *Server::constructor; Persistent<FunctionTemplate> Server::fun_tpl; class NewCallOp : public Op { @@ -82,22 +88,26 @@ class NewCallOp : public Op { grpc_metadata_array_destroy(&request_metadata); } - Handle<Value> GetNodeValue() const { - NanEscapableScope(); + Local<Value> GetNodeValue() const { + Nan::EscapableHandleScope scope; if (call == NULL) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); } - Handle<Object> obj = NanNew<Object>(); - obj->Set(NanNew("call"), Call::WrapStruct(call)); - obj->Set(NanNew("method"), NanNew(details.method)); - obj->Set(NanNew("host"), NanNew(details.host)); - obj->Set(NanNew("deadline"), - NanNew<Date>(TimespecToMilliseconds(details.deadline))); - obj->Set(NanNew("metadata"), ParseMetadata(&request_metadata)); - return NanEscapeScope(obj); + Local<Object> obj = Nan::New<Object>(); + Nan::Set(obj, Nan::New("call").ToLocalChecked(), Call::WrapStruct(call)); + Nan::Set(obj, Nan::New("method").ToLocalChecked(), + Nan::New(details.method).ToLocalChecked()); + Nan::Set(obj, Nan::New("host").ToLocalChecked(), + Nan::New(details.host).ToLocalChecked()); + Nan::Set(obj, Nan::New("deadline").ToLocalChecked(), + Nan::New<Date>( + TimespecToMilliseconds(details.deadline)).ToLocalChecked()); + Nan::Set(obj, Nan::New("metadata").ToLocalChecked(), + ParseMetadata(&request_metadata)); + return scope.Escape(obj); } - bool ParseOp(Handle<Value> value, grpc_op *out, + bool ParseOp(Local<Value> value, grpc_op *out, shared_ptr<Resources> resources) { return true; } @@ -124,35 +134,25 @@ Server::~Server() { grpc_completion_queue_destroy(this->shutdown_queue); } -void Server::Init(Handle<Object> exports) { - NanScope(); - Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New); - tpl->SetClassName(NanNew("Server")); +void Server::Init(Local<Object> exports) { + HandleScope scope; + Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); + tpl->SetClassName(Nan::New("Server").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); - NanSetPrototypeTemplate(tpl, "requestCall", - NanNew<FunctionTemplate>(RequestCall)->GetFunction()); - - NanSetPrototypeTemplate( - tpl, "addHttp2Port", - NanNew<FunctionTemplate>(AddHttp2Port)->GetFunction()); - - NanSetPrototypeTemplate(tpl, "start", - NanNew<FunctionTemplate>(Start)->GetFunction()); - - NanSetPrototypeTemplate(tpl, "tryShutdown", - NanNew<FunctionTemplate>(TryShutdown)->GetFunction()); - NanSetPrototypeTemplate( - tpl, "forceShutdown", - NanNew<FunctionTemplate>(ForceShutdown)->GetFunction()); - - NanAssignPersistent(fun_tpl, tpl); - Handle<Function> ctr = tpl->GetFunction(); - constructor = new NanCallback(ctr); - exports->Set(NanNew("Server"), ctr); + Nan::SetPrototypeMethod(tpl, "requestCall", RequestCall); + Nan::SetPrototypeMethod(tpl, "addHttp2Port", AddHttp2Port); + Nan::SetPrototypeMethod(tpl, "start", Start); + Nan::SetPrototypeMethod(tpl, "tryShutdown", TryShutdown); + Nan::SetPrototypeMethod(tpl, "forceShutdown", ForceShutdown); + fun_tpl.Reset(tpl); + Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(exports, Nan::New("Server").ToLocalChecked(), ctr); + constructor = new Callback(ctr); } -bool Server::HasInstance(Handle<Value> val) { - return NanHasInstance(fun_tpl, val); +bool Server::HasInstance(Local<Value> val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); } void Server::ShutdownServer() { @@ -165,64 +165,42 @@ void Server::ShutdownServer() { } NAN_METHOD(Server::New) { - NanScope(); - /* If this is not a constructor call, make a constructor call and return the result */ - if (!args.IsConstructCall()) { + if (!info.IsConstructCall()) { const int argc = 1; - Local<Value> argv[argc] = {args[0]}; - NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); + Local<Value> argv[argc] = {info[0]}; + MaybeLocal<Object> maybe_instance = constructor->GetFunction()->NewInstance( + argc, argv); + if (maybe_instance.IsEmpty()) { + // There's probably a pending exception + return; + } else { + info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); + return; + } } grpc_server *wrapped_server; grpc_completion_queue *queue = CompletionQueueAsyncWorker::GetQueue(); - if (args[0]->IsUndefined()) { - wrapped_server = grpc_server_create(NULL, NULL); - } else if (args[0]->IsObject()) { - Handle<Object> args_hash(args[0]->ToObject()); - Handle<Array> keys(args_hash->GetOwnPropertyNames()); - grpc_channel_args channel_args; - channel_args.num_args = keys->Length(); - channel_args.args = reinterpret_cast<grpc_arg *>( - calloc(channel_args.num_args, sizeof(grpc_arg))); - /* These are used to keep all strings until then end of the block, then - destroy them */ - std::vector<NanUtf8String *> key_strings(keys->Length()); - std::vector<NanUtf8String *> value_strings(keys->Length()); - for (unsigned int i = 0; i < channel_args.num_args; i++) { - Handle<String> current_key(keys->Get(i)->ToString()); - Handle<Value> current_value(args_hash->Get(current_key)); - key_strings[i] = new NanUtf8String(current_key); - channel_args.args[i].key = **key_strings[i]; - if (current_value->IsInt32()) { - channel_args.args[i].type = GRPC_ARG_INTEGER; - channel_args.args[i].value.integer = current_value->Int32Value(); - } else if (current_value->IsString()) { - channel_args.args[i].type = GRPC_ARG_STRING; - value_strings[i] = new NanUtf8String(current_value); - channel_args.args[i].value.string = **value_strings[i]; - } else { - free(channel_args.args); - return NanThrowTypeError("Arg values must be strings"); - } - } - wrapped_server = grpc_server_create(&channel_args, NULL); - free(channel_args.args); - } else { - return NanThrowTypeError("Server expects an object"); + grpc_channel_args *channel_args; + if (!ParseChannelArgs(info[0], &channel_args)) { + DeallocateChannelArgs(channel_args); + return Nan::ThrowTypeError("Server options must be an object with " + "string keys and integer or string values"); } + wrapped_server = grpc_server_create(channel_args, NULL); + DeallocateChannelArgs(channel_args); grpc_server_register_completion_queue(wrapped_server, queue, NULL); Server *server = new Server(wrapped_server); - server->Wrap(args.This()); - NanReturnValue(args.This()); + server->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); } NAN_METHOD(Server::RequestCall) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("requestCall can only be called on a Server"); + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("requestCall can only be called on a Server"); } - Server *server = ObjectWrap::Unwrap<Server>(args.This()); + Server *server = ObjectWrap::Unwrap<Server>(info.This()); NewCallOp *op = new NewCallOp(); unique_ptr<OpVec> ops(new OpVec()); ops->push_back(unique_ptr<Op>(op)); @@ -230,79 +208,74 @@ NAN_METHOD(Server::RequestCall) { server->wrapped_server, &op->call, &op->details, &op->request_metadata, CompletionQueueAsyncWorker::GetQueue(), CompletionQueueAsyncWorker::GetQueue(), - new struct tag(new NanCallback(args[0].As<Function>()), ops.release(), + new struct tag(new Callback(info[0].As<Function>()), ops.release(), shared_ptr<Resources>(nullptr))); if (error != GRPC_CALL_OK) { - return NanThrowError(nanErrorWithCode("requestCall failed", error)); + return Nan::ThrowError(nanErrorWithCode("requestCall failed", error)); } CompletionQueueAsyncWorker::Next(); - NanReturnUndefined(); } NAN_METHOD(Server::AddHttp2Port) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError( + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( "addHttp2Port can only be called on a Server"); } - if (!args[0]->IsString()) { - return NanThrowTypeError( + if (!info[0]->IsString()) { + return Nan::ThrowTypeError( "addHttp2Port's first argument must be a String"); } - if (!ServerCredentials::HasInstance(args[1])) { - return NanThrowTypeError( + if (!ServerCredentials::HasInstance(info[1])) { + return Nan::ThrowTypeError( "addHttp2Port's second argument must be ServerCredentials"); } - Server *server = ObjectWrap::Unwrap<Server>(args.This()); + Server *server = ObjectWrap::Unwrap<Server>(info.This()); ServerCredentials *creds_object = ObjectWrap::Unwrap<ServerCredentials>( - args[1]->ToObject()); + Nan::To<Object>(info[1]).ToLocalChecked()); grpc_server_credentials *creds = creds_object->GetWrappedServerCredentials(); int port; if (creds == NULL) { port = grpc_server_add_insecure_http2_port(server->wrapped_server, - *NanUtf8String(args[0])); + *Utf8String(info[0])); } else { port = grpc_server_add_secure_http2_port(server->wrapped_server, - *NanUtf8String(args[0]), + *Utf8String(info[0]), creds); } - NanReturnValue(NanNew<Number>(port)); + info.GetReturnValue().Set(Nan::New<Number>(port)); } NAN_METHOD(Server::Start) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("start can only be called on a Server"); + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("start can only be called on a Server"); } - Server *server = ObjectWrap::Unwrap<Server>(args.This()); + Server *server = ObjectWrap::Unwrap<Server>(info.This()); grpc_server_start(server->wrapped_server); - NanReturnUndefined(); } NAN_METHOD(Server::TryShutdown) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("tryShutdown can only be called on a Server"); + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("tryShutdown can only be called on a Server"); } - Server *server = ObjectWrap::Unwrap<Server>(args.This()); + Server *server = ObjectWrap::Unwrap<Server>(info.This()); unique_ptr<OpVec> ops(new OpVec()); grpc_server_shutdown_and_notify( server->wrapped_server, CompletionQueueAsyncWorker::GetQueue(), - new struct tag(new NanCallback(args[0].As<Function>()), ops.release(), + new struct tag(new Nan::Callback(info[0].As<Function>()), ops.release(), shared_ptr<Resources>(nullptr))); CompletionQueueAsyncWorker::Next(); - NanReturnUndefined(); } NAN_METHOD(Server::ForceShutdown) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("forceShutdown can only be called on a Server"); + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("forceShutdown can only be called on a Server"); } - Server *server = ObjectWrap::Unwrap<Server>(args.This()); + Server *server = ObjectWrap::Unwrap<Server>(info.This()); server->ShutdownServer(); - NanReturnUndefined(); } } // namespace node diff --git a/src/node/ext/server.h b/src/node/ext/server.h index e7d5c3fb11..ab5fc210e8 100644 --- a/src/node/ext/server.h +++ b/src/node/ext/server.h @@ -44,14 +44,14 @@ namespace node { /* Wraps grpc_server as a JavaScript object. Provides a constructor and wrapper methods for grpc_server_create, grpc_server_request_call, grpc_server_add_http2_port, and grpc_server_start. */ -class Server : public ::node::ObjectWrap { +class Server : public Nan::ObjectWrap { public: /* Initializes the Server class and exposes the constructor and wrapper methods to JavaScript */ - static void Init(v8::Handle<v8::Object> exports); + static void Init(v8::Local<v8::Object> exports); /* Tests whether the given value was constructed by this class's JavaScript constructor */ - static bool HasInstance(v8::Handle<v8::Value> val); + static bool HasInstance(v8::Local<v8::Value> val); private: explicit Server(grpc_server *server); @@ -69,8 +69,8 @@ class Server : public ::node::ObjectWrap { static NAN_METHOD(Start); static NAN_METHOD(TryShutdown); static NAN_METHOD(ForceShutdown); - static NanCallback *constructor; - static v8::Persistent<v8::FunctionTemplate> fun_tpl; + static Nan::Callback *constructor; + static Nan::Persistent<v8::FunctionTemplate> fun_tpl; grpc_server *wrapped_server; grpc_completion_queue *shutdown_queue; diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc index b1201eb664..e6c55e263c 100644 --- a/src/node/ext/server_credentials.cc +++ b/src/node/ext/server_credentials.cc @@ -41,22 +41,28 @@ namespace grpc { namespace node { +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + using v8::Array; using v8::Exception; using v8::External; using v8::Function; using v8::FunctionTemplate; -using v8::Handle; -using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Object; using v8::ObjectTemplate; -using v8::Persistent; using v8::String; using v8::Value; -NanCallback *ServerCredentials::constructor; +Nan::Callback *ServerCredentials::constructor; Persistent<FunctionTemplate> ServerCredentials::fun_tpl; ServerCredentials::ServerCredentials(grpc_server_credentials *credentials) @@ -66,33 +72,41 @@ ServerCredentials::~ServerCredentials() { grpc_server_credentials_release(wrapped_credentials); } -void ServerCredentials::Init(Handle<Object> exports) { - NanScope(); - Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New); - tpl->SetClassName(NanNew("ServerCredentials")); +void ServerCredentials::Init(Local<Object> exports) { + Nan::HandleScope scope; + Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); + tpl->SetClassName(Nan::New("ServerCredentials").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); - NanAssignPersistent(fun_tpl, tpl); - Handle<Function> ctr = tpl->GetFunction(); - ctr->Set(NanNew("createSsl"), - NanNew<FunctionTemplate>(CreateSsl)->GetFunction()); - ctr->Set(NanNew("createInsecure"), - NanNew<FunctionTemplate>(CreateInsecure)->GetFunction()); - constructor = new NanCallback(ctr); - exports->Set(NanNew("ServerCredentials"), ctr); + Local<Function> ctr = tpl->GetFunction(); + Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(), + Nan::GetFunction( + Nan::New<FunctionTemplate>(CreateSsl)).ToLocalChecked()); + Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(), + Nan::GetFunction( + Nan::New<FunctionTemplate>(CreateInsecure)).ToLocalChecked()); + fun_tpl.Reset(tpl); + constructor = new Nan::Callback(ctr); + Nan::Set(exports, Nan::New("ServerCredentials").ToLocalChecked(), ctr); } -bool ServerCredentials::HasInstance(Handle<Value> val) { - NanScope(); - return NanHasInstance(fun_tpl, val); +bool ServerCredentials::HasInstance(Local<Value> val) { + Nan::HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); } -Handle<Value> ServerCredentials::WrapStruct( +Local<Value> ServerCredentials::WrapStruct( grpc_server_credentials *credentials) { - NanEscapableScope(); + Nan::EscapableHandleScope scope; const int argc = 1; - Handle<Value> argv[argc] = { - NanNew<External>(reinterpret_cast<void *>(credentials))}; - return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv)); + Local<Value> argv[argc] = { + Nan::New<External>(reinterpret_cast<void *>(credentials))}; + MaybeLocal<Object> maybe_instance = Nan::NewInstance( + constructor->GetFunction(), argc, argv); + if (maybe_instance.IsEmpty()) { + return scope.Escape(Nan::Null()); + } else { + return scope.Escape(maybe_instance.ToLocalChecked()); + } } grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() { @@ -100,96 +114,96 @@ grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() { } NAN_METHOD(ServerCredentials::New) { - NanScope(); - - if (args.IsConstructCall()) { - if (!args[0]->IsExternal()) { - return NanThrowTypeError( - "ServerCredentials can only be created with the provide functions"); + if (info.IsConstructCall()) { + if (!info[0]->IsExternal()) { + return Nan::ThrowTypeError( + "ServerCredentials can only be created with the provided functions"); } - Handle<External> ext = args[0].As<External>(); + Local<External> ext = info[0].As<External>(); grpc_server_credentials *creds_value = reinterpret_cast<grpc_server_credentials *>(ext->Value()); ServerCredentials *credentials = new ServerCredentials(creds_value); - credentials->Wrap(args.This()); - NanReturnValue(args.This()); + credentials->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); } else { - const int argc = 1; - Local<Value> argv[argc] = {args[0]}; - NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); + // This should never be called directly + return Nan::ThrowTypeError( + "ServerCredentials can only be created with the provided functions"); } } NAN_METHOD(ServerCredentials::CreateSsl) { - // TODO: have the node API support multiple key/cert pairs. - NanScope(); + Nan::HandleScope scope; char *root_certs = NULL; - if (::node::Buffer::HasInstance(args[0])) { - root_certs = ::node::Buffer::Data(args[0]); - } else if (!(args[0]->IsNull() || args[0]->IsUndefined())) { - return NanThrowTypeError( + if (::node::Buffer::HasInstance(info[0])) { + root_certs = ::node::Buffer::Data(info[0]); + } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) { + return Nan::ThrowTypeError( "createSSl's first argument must be a Buffer if provided"); } - if (!args[1]->IsArray()) { - return NanThrowTypeError( + if (!info[1]->IsArray()) { + return Nan::ThrowTypeError( "createSsl's second argument must be a list of objects"); } int force_client_auth = 0; - if (args[2]->IsBoolean()) { - force_client_auth = (int)args[2]->BooleanValue(); - } else if (!(args[2]->IsUndefined() || args[2]->IsNull())) { - return NanThrowTypeError( + if (info[2]->IsBoolean()) { + force_client_auth = (int)Nan::To<bool>(info[2]).FromJust(); + } else if (!(info[2]->IsUndefined() || info[2]->IsNull())) { + return Nan::ThrowTypeError( "createSsl's third argument must be a boolean if provided"); } - Handle<Array> pair_list = Local<Array>::Cast(args[1]); + Local<Array> pair_list = Local<Array>::Cast(info[1]); uint32_t key_cert_pair_count = pair_list->Length(); grpc_ssl_pem_key_cert_pair *key_cert_pairs = new grpc_ssl_pem_key_cert_pair[ key_cert_pair_count]; - Handle<String> key_key = NanNew("private_key"); - Handle<String> cert_key = NanNew("cert_chain"); + Local<String> key_key = Nan::New("private_key").ToLocalChecked(); + Local<String> cert_key = Nan::New("cert_chain").ToLocalChecked(); for(uint32_t i = 0; i < key_cert_pair_count; i++) { - if (!pair_list->Get(i)->IsObject()) { + Local<Value> pair_val = Nan::Get(pair_list, i).ToLocalChecked(); + if (!pair_val->IsObject()) { delete key_cert_pairs; - return NanThrowTypeError("Key/cert pairs must be objects"); + return Nan::ThrowTypeError("Key/cert pairs must be objects"); } - Handle<Object> pair_obj = pair_list->Get(i)->ToObject(); - if (!pair_obj->HasOwnProperty(key_key)) { + Local<Object> pair_obj = Nan::To<Object>(pair_val).ToLocalChecked(); + MaybeLocal<Value> maybe_key = Nan::Get(pair_obj, key_key); + if (maybe_key.IsEmpty()) { delete key_cert_pairs; - return NanThrowTypeError( + return Nan::ThrowTypeError( "Key/cert pairs must have a private_key and a cert_chain"); } - if (!pair_obj->HasOwnProperty(cert_key)) { + MaybeLocal<Value> maybe_cert = Nan::Get(pair_obj, cert_key); + if (maybe_cert.IsEmpty()) { delete key_cert_pairs; - return NanThrowTypeError( + return Nan::ThrowTypeError( "Key/cert pairs must have a private_key and a cert_chain"); } - if (!::node::Buffer::HasInstance(pair_obj->Get(key_key))) { + if (!::node::Buffer::HasInstance(maybe_key.ToLocalChecked())) { delete key_cert_pairs; - return NanThrowTypeError("private_key must be a Buffer"); + return Nan::ThrowTypeError("private_key must be a Buffer"); } - if (!::node::Buffer::HasInstance(pair_obj->Get(cert_key))) { + if (!::node::Buffer::HasInstance(maybe_cert.ToLocalChecked())) { delete key_cert_pairs; - return NanThrowTypeError("cert_chain must be a Buffer"); + return Nan::ThrowTypeError("cert_chain must be a Buffer"); } key_cert_pairs[i].private_key = ::node::Buffer::Data( - pair_obj->Get(key_key)); + maybe_key.ToLocalChecked()); key_cert_pairs[i].cert_chain = ::node::Buffer::Data( - pair_obj->Get(cert_key)); + maybe_cert.ToLocalChecked()); } grpc_server_credentials *creds = grpc_ssl_server_credentials_create( root_certs, key_cert_pairs, key_cert_pair_count, force_client_auth, NULL); delete key_cert_pairs; if (creds == NULL) { - NanReturnNull(); + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); } - NanReturnValue(WrapStruct(creds)); } NAN_METHOD(ServerCredentials::CreateInsecure) { - NanScope(); - NanReturnValue(WrapStruct(NULL)); + info.GetReturnValue().Set(WrapStruct(NULL)); } } // namespace node diff --git a/src/node/ext/server_credentials.h b/src/node/ext/server_credentials.h index 63903f663c..bf279e481c 100644 --- a/src/node/ext/server_credentials.h +++ b/src/node/ext/server_credentials.h @@ -43,12 +43,12 @@ namespace grpc { namespace node { /* Wrapper class for grpc_server_credentials structs */ -class ServerCredentials : public ::node::ObjectWrap { +class ServerCredentials : public Nan::ObjectWrap { public: - static void Init(v8::Handle<v8::Object> exports); - static bool HasInstance(v8::Handle<v8::Value> val); + static void Init(v8::Local<v8::Object> exports); + static bool HasInstance(v8::Local<v8::Value> val); /* Wrap a grpc_server_credentials struct in a javascript object */ - static v8::Handle<v8::Value> WrapStruct(grpc_server_credentials *credentials); + static v8::Local<v8::Value> WrapStruct(grpc_server_credentials *credentials); /* Returns the grpc_server_credentials struct that this object wraps */ grpc_server_credentials *GetWrappedServerCredentials(); @@ -64,9 +64,9 @@ class ServerCredentials : public ::node::ObjectWrap { static NAN_METHOD(New); static NAN_METHOD(CreateSsl); static NAN_METHOD(CreateInsecure); - static NanCallback *constructor; + static Nan::Callback *constructor; // Used for typechecking instances of this javascript class - static v8::Persistent<v8::FunctionTemplate> fun_tpl; + static Nan::Persistent<v8::FunctionTemplate> fun_tpl; grpc_server_credentials *wrapped_credentials; }; diff --git a/src/node/index.js b/src/node/index.js index 02b73f66ee..0d1a7fd887 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -33,6 +33,14 @@ 'use strict'; +var path = require('path'); + +var SSL_ROOTS_PATH = path.resolve(__dirname, '..', '..', 'etc', 'roots.pem'); + +if (!process.env.GRPC_DEFAULT_SSL_ROOTS_FILE_PATH) { + process.env.GRPC_DEFAULT_SSL_ROOTS_FILE_PATH = SSL_ROOTS_PATH; +} + var _ = require('lodash'); var ProtoBuf = require('protobufjs'); @@ -43,7 +51,7 @@ var server = require('./src/server.js'); var Metadata = require('./src/metadata.js'); -var grpc = require('bindings')('grpc'); +var grpc = require('bindings')('grpc_node'); /** * Load a gRPC object from an existing ProtoBuf.Reflect object. @@ -90,37 +98,10 @@ exports.load = function load(filename, format) { default: throw new Error('Unrecognized format "' + format + '"'); } - return loadObject(builder.ns); }; /** - * Get a function that a client can use to update metadata with authentication - * information from a Google Auth credential object, which comes from the - * google-auth-library. - * @param {Object} credential The credential object to use - * @return {function(Object, callback)} Metadata updater function - */ -exports.getGoogleAuthDelegate = function getGoogleAuthDelegate(credential) { - /** - * Update a metadata object with authentication information. - * @param {string} authURI The uri to authenticate to - * @param {Object} metadata Metadata object - * @param {function(Error, Object)} callback - */ - return function updateMetadata(authURI, metadata, callback) { - credential.getRequestMetadata(authURI, function(err, header) { - if (err) { - callback(err); - return; - } - metadata.add('authorization', header.Authorization); - callback(null, metadata); - }); - }; -}; - -/** * @see module:src/server.Server */ exports.Server = server.Server; @@ -153,7 +134,7 @@ exports.writeFlags = grpc.writeFlags; /** * Credentials factories */ -exports.Credentials = grpc.Credentials; +exports.credentials = require('./src/credentials.js'); /** * ServerCredentials factories diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js index 6a8d2633ca..53ffa385bd 100644 --- a/src/node/interop/interop_client.js +++ b/src/node/interop/interop_client.js @@ -35,19 +35,25 @@ var fs = require('fs'); var path = require('path'); -var _ = require('lodash'); var grpc = require('..'); -var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; +var testProto = grpc.load({ + root: __dirname + '/../../..', + file: 'test/proto/test.proto'}).grpc.testing; var GoogleAuth = require('google-auth-library'); var assert = require('assert'); -var AUTH_SCOPE = 'https://www.googleapis.com/auth/xapi.zoo'; -var AUTH_SCOPE_RESPONSE = 'xapi.zoo'; -var AUTH_USER = ('155450119199-3psnrh1sdr3d8cpj1v46naggf81mhdnk' + - '@developer.gserviceaccount.com'); -var COMPUTE_ENGINE_USER = ('155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel' + - '@developer.gserviceaccount.com'); +var SERVICE_ACCOUNT_EMAIL; +try { + SERVICE_ACCOUNT_EMAIL = require( + process.env.GOOGLE_APPLICATION_CREDENTIALS).client_email; +} catch (e) { + // This will cause the tests to fail if they need that string + SERVICE_ACCOUNT_EMAIL = null; +} + +var ECHO_INITIAL_KEY = 'x-grpc-test-echo-initial'; +var ECHO_TRAILING_KEY = 'x-grpc-test-echo-trailing-bin'; /** * Create a buffer filled with size zeroes @@ -61,6 +67,27 @@ function zeroBuffer(size) { } /** + * This is used for testing functions with multiple asynchronous calls that + * can happen in different orders. This should be passed the number of async + * function invocations that can occur last, and each of those should call this + * function's return value + * @param {function()} done The function that should be called when a test is + * complete. + * @param {number} count The number of calls to the resulting function if the + * test passes. + * @return {function()} The function that should be called at the end of each + * sequence of asynchronous functions. + */ +function multiDone(done, count) { + return function() { + count -= 1; + if (count <= 0) { + done(); + } + }; +} + +/** * Run the empty_unary test * @param {Client} client The client to test against * @param {function} done Callback to call when the test is completed. Included @@ -271,6 +298,89 @@ function timeoutOnSleepingServer(client, done) { }); } +function customMetadata(client, done) { + done = multiDone(done, 5); + var metadata = new grpc.Metadata(); + metadata.set(ECHO_INITIAL_KEY, 'test_initial_metadata_value'); + metadata.set(ECHO_TRAILING_KEY, new Buffer('ababab', 'hex')); + var arg = { + response_type: 'COMPRESSABLE', + response_size: 314159, + payload: { + body: zeroBuffer(271828) + } + }; + var streaming_arg = { + payload: { + body: zeroBuffer(271828) + } + }; + var unary = client.unaryCall(arg, function(err, resp) { + assert.ifError(err); + done(); + }, metadata); + unary.on('metadata', function(metadata) { + assert.deepEqual(metadata.get(ECHO_INITIAL_KEY), + ['test_initial_metadata_value']); + done(); + }); + unary.on('status', function(status) { + var echo_trailer = status.metadata.get(ECHO_TRAILING_KEY); + assert(echo_trailer.length > 0); + assert.strictEqual(echo_trailer[0].toString('hex'), 'ababab'); + done(); + }); + var stream = client.fullDuplexCall(metadata); + stream.on('metadata', function(metadata) { + assert.deepEqual(metadata.get(ECHO_INITIAL_KEY), + ['test_initial_metadata_value']); + done(); + }); + stream.on('status', function(status) { + var echo_trailer = status.metadata.get(ECHO_TRAILING_KEY); + assert(echo_trailer.length > 0); + assert.strictEqual(echo_trailer[0].toString('hex'), 'ababab'); + done(); + }); + stream.write(streaming_arg); + stream.end(); +} + +function statusCodeAndMessage(client, done) { + done = multiDone(done, 2); + var arg = { + response_status: { + code: 2, + message: 'test status message' + } + }; + client.unaryCall(arg, function(err, resp) { + assert(err); + assert.strictEqual(err.code, 2); + assert.strictEqual(err.message, 'test status message'); + done(); + }); + var duplex = client.fullDuplexCall(); + duplex.on('status', function(status) { + assert(status); + assert.strictEqual(status.code, 2); + assert.strictEqual(status.details, 'test status message'); + done(); + }); + duplex.on('error', function(){}); + duplex.write(arg); + duplex.end(); +} + +function unimplementedMethod(client, done) { + client.unimplementedCall({}, function(err, resp) { + assert(err); + assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED); + assert(!err.message); + done(); + }); +} + /** * Run one of the authentication tests. * @param {string} expected_user The expected username in the response @@ -280,67 +390,110 @@ function timeoutOnSleepingServer(client, done) { * primarily for use with mocha */ function authTest(expected_user, scope, client, done) { - (new GoogleAuth()).getApplicationDefault(function(err, credential) { + var arg = { + response_type: 'COMPRESSABLE', + response_size: 314159, + payload: { + body: zeroBuffer(271828) + }, + fill_username: true, + fill_oauth_scope: true + }; + client.unaryCall(arg, function(err, resp) { assert.ifError(err); - if (credential.createScopedRequired() && scope) { - credential = credential.createScoped(scope); + assert.strictEqual(resp.payload.type, 'COMPRESSABLE'); + assert.strictEqual(resp.payload.body.length, 314159); + assert.strictEqual(resp.username, expected_user); + if (scope) { + assert(scope.indexOf(resp.oauth_scope) > -1); + } + if (done) { + done(); } - client.$updateMetadata = grpc.getGoogleAuthDelegate(credential); + }); +} + +function computeEngineCreds(client, done, extra) { + authTest(extra.service_account, null, client, done); +} + +function serviceAccountCreds(client, done, extra) { + authTest(SERVICE_ACCOUNT_EMAIL, extra.oauth_scope, client, done); +} + +function jwtTokenCreds(client, done, extra) { + authTest(SERVICE_ACCOUNT_EMAIL, null, client, done); +} + +function oauth2Test(client, done, extra) { + var arg = { + fill_username: true, + fill_oauth_scope: true + }; + client.unaryCall(arg, function(err, resp) { + assert.ifError(err); + assert.strictEqual(resp.username, SERVICE_ACCOUNT_EMAIL); + assert(extra.oauth_scope.indexOf(resp.oauth_scope) > -1); + if (done) { + done(); + } + }); +} + +function perRpcAuthTest(client, done, extra) { + (new GoogleAuth()).getApplicationDefault(function(err, credential) { + assert.ifError(err); var arg = { - response_type: 'COMPRESSABLE', - response_size: 314159, - payload: { - body: zeroBuffer(271828) - }, fill_username: true, fill_oauth_scope: true }; + var scope = extra.oauth_scope; + if (credential.createScopedRequired() && scope) { + credential = credential.createScoped(scope); + } + var creds = grpc.credentials.createFromGoogleCredential(credential); client.unaryCall(arg, function(err, resp) { assert.ifError(err); - assert.strictEqual(resp.payload.type, 'COMPRESSABLE'); - assert.strictEqual(resp.payload.body.length, 314159); - assert.strictEqual(resp.username, expected_user); - if (scope) { - assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE); - } + assert.strictEqual(resp.username, SERVICE_ACCOUNT_EMAIL); + assert(extra.oauth_scope.indexOf(resp.oauth_scope) > -1); if (done) { done(); } - }); + }, null, {credentials: creds}); }); } -function oauth2Test(expected_user, scope, per_rpc, client, done) { +function getApplicationCreds(scope, callback) { (new GoogleAuth()).getApplicationDefault(function(err, credential) { - assert.ifError(err); - var arg = { - fill_username: true, - fill_oauth_scope: true - }; + if (err) { + callback(err); + return; + } + if (credential.createScopedRequired() && scope) { + credential = credential.createScoped(scope); + } + callback(null, grpc.credentials.createFromGoogleCredential(credential)); + }); +} + +function getOauth2Creds(scope, callback) { + (new GoogleAuth()).getApplicationDefault(function(err, credential) { + if (err) { + callback(err); + return; + } credential = credential.createScoped(scope); credential.getAccessToken(function(err, token) { - assert.ifError(err); - var updateMetadata = function(authURI, metadata, callback) { - metadata.Add('authorization', 'Bearer ' + token); + if (err) { + callback(err); + return; + } + var updateMd = function(service_url, callback) { + var metadata = new grpc.Metadata(); + metadata.add('authorization', 'Bearer ' + token); callback(null, metadata); }; - var makeTestCall = function(error, client_metadata) { - assert.ifError(error); - client.unaryCall(arg, function(err, resp) { - assert.ifError(err); - assert.strictEqual(resp.username, expected_user); - assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE); - if (done) { - done(); - } - }, client_metadata); - }; - if (per_rpc) { - updateMetadata('', {}, makeTestCall); - } else { - client.$updateMetadata = updateMetadata; - makeTestCall(null, {}); - } + callback(null, grpc.credentials.createFromMetadataGenerator(updateMd)); }); }); } @@ -349,20 +502,44 @@ function oauth2Test(expected_user, scope, per_rpc, client, done) { * Map from test case names to test functions */ var test_cases = { - empty_unary: emptyUnary, - large_unary: largeUnary, - client_streaming: clientStreaming, - server_streaming: serverStreaming, - ping_pong: pingPong, - empty_stream: emptyStream, - cancel_after_begin: cancelAfterBegin, - cancel_after_first_response: cancelAfterFirstResponse, - timeout_on_sleeping_server: timeoutOnSleepingServer, - compute_engine_creds: _.partial(authTest, COMPUTE_ENGINE_USER, null), - service_account_creds: _.partial(authTest, AUTH_USER, AUTH_SCOPE), - jwt_token_creds: _.partial(authTest, AUTH_USER, null), - oauth2_auth_token: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, false), - per_rpc_creds: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, true) + empty_unary: {run: emptyUnary, + Client: testProto.TestService}, + large_unary: {run: largeUnary, + Client: testProto.TestService}, + client_streaming: {run: clientStreaming, + Client: testProto.TestService}, + server_streaming: {run: serverStreaming, + Client: testProto.TestService}, + ping_pong: {run: pingPong, + Client: testProto.TestService}, + empty_stream: {run: emptyStream, + Client: testProto.TestService}, + cancel_after_begin: {run: cancelAfterBegin, + Client: testProto.TestService}, + cancel_after_first_response: {run: cancelAfterFirstResponse, + Client: testProto.TestService}, + timeout_on_sleeping_server: {run: timeoutOnSleepingServer, + Client: testProto.TestService}, + custom_metadata: {run: customMetadata, + Client: testProto.TestService}, + status_code_and_message: {run: statusCodeAndMessage, + Client: testProto.TestService}, + unimplemented_method: {run: unimplementedMethod, + Client: testProto.UnimplementedService}, + compute_engine_creds: {run: computeEngineCreds, + Client: testProto.TestService, + getCreds: getApplicationCreds}, + service_account_creds: {run: serviceAccountCreds, + Client: testProto.TestService, + getCreds: getApplicationCreds}, + jwt_token_creds: {run: jwtTokenCreds, + Client: testProto.TestService, + getCreds: getApplicationCreds}, + oauth2_auth_token: {run: oauth2Test, + Client: testProto.TestService, + getCreds: getOauth2Creds}, + per_rpc_creds: {run: perRpcAuthTest, + Client: testProto.TestService} }; /** @@ -375,8 +552,9 @@ var test_cases = { * @param {bool} tls Indicates that a secure channel should be used * @param {function} done Callback to call when the test is completed. Included * primarily for use with mocha + * @param {object=} extra Extra options for some tests */ -function runTest(address, host_override, test_case, tls, test_ca, done) { +function runTest(address, host_override, test_case, tls, test_ca, done, extra) { // TODO(mlumish): enable TLS functionality var options = {}; var creds; @@ -384,34 +562,53 @@ function runTest(address, host_override, test_case, tls, test_ca, done) { var ca_path; if (test_ca) { ca_path = path.join(__dirname, '../test/data/ca.pem'); + var ca_data = fs.readFileSync(ca_path); + creds = grpc.credentials.createSsl(ca_data); } else { - ca_path = process.env.SSL_CERT_FILE; + creds = grpc.credentials.createSsl(); } - var ca_data = fs.readFileSync(ca_path); - creds = grpc.Credentials.createSsl(ca_data); if (host_override) { options['grpc.ssl_target_name_override'] = host_override; options['grpc.default_authority'] = host_override; } } else { - creds = grpc.Credentials.createInsecure(); + creds = grpc.credentials.createInsecure(); } - var client = new testProto.TestService(address, creds, options); + var test = test_cases[test_case]; + + var execute = function(err, creds) { + assert.ifError(err); + var client = new test.Client(address, creds, options); + test.run(client, done, extra); + }; - test_cases[test_case](client, done); + if (test.getCreds) { + test.getCreds(extra.oauth_scope, function(err, new_creds) { + assert.ifError(err); + execute(err, grpc.credentials.combineChannelCredentials( + creds, new_creds)); + }); + } else { + execute(null, creds); + } } if (require.main === module) { var parseArgs = require('minimist'); var argv = parseArgs(process.argv, { string: ['server_host', 'server_host_override', 'server_port', 'test_case', - 'use_tls', 'use_test_ca'] + 'use_tls', 'use_test_ca', 'default_service_account', 'oauth_scope', + 'service_account_key_file'] }); + var extra_args = { + service_account: argv.default_service_account, + oauth_scope: argv.oauth_scope + }; runTest(argv.server_host + ':' + argv.server_port, argv.server_host_override, argv.test_case, argv.use_tls === 'true', argv.use_test_ca === 'true', function () { console.log('OK:', argv.test_case); - }); + }, extra_args); } /** diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js index 99155e9958..5321005c86 100644 --- a/src/node/interop/interop_server.js +++ b/src/node/interop/interop_server.js @@ -37,7 +37,15 @@ var fs = require('fs'); var path = require('path'); var _ = require('lodash'); var grpc = require('..'); -var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; +var testProto = grpc.load({ + root: __dirname + '/../../..', + file: 'test/proto/test.proto'}).grpc.testing; + +var ECHO_INITIAL_KEY = 'x-grpc-test-echo-initial'; +var ECHO_TRAILING_KEY = 'x-grpc-test-echo-trailing-bin'; + +var incompressible_data = fs.readFileSync( + __dirname + '/../../../test/cpp/interop/rnd.dat'); /** * Create a buffer filled with size zeroes @@ -51,6 +59,47 @@ function zeroBuffer(size) { } /** + * Echos a header metadata item as specified in the interop spec. + * @param {Call} call The call to echo metadata on + */ +function echoHeader(call) { + var echo_initial = call.metadata.get(ECHO_INITIAL_KEY); + if (echo_initial.length > 0) { + var response_metadata = new grpc.Metadata(); + response_metadata.set(ECHO_INITIAL_KEY, echo_initial[0]); + call.sendMetadata(response_metadata); + } +} + +/** + * Gets the trailer metadata that should be echoed when the call is done, + * as specified in the interop spec. + * @param {Call} call The call to get metadata from + * @return {grpc.Metadata} The metadata to send as a trailer + */ +function getEchoTrailer(call) { + var echo_trailer = call.metadata.get(ECHO_TRAILING_KEY); + var response_trailer = new grpc.Metadata(); + if (echo_trailer.length > 0) { + response_trailer.set(ECHO_TRAILING_KEY, echo_trailer[0]); + } + return response_trailer; +} + +function getPayload(payload_type, size) { + if (payload_type === 'RANDOM') { + payload_type = ['COMPRESSABLE', + 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; + } + var body; + switch (payload_type) { + case 'COMPRESSABLE': body = zeroBuffer(size); break; + case 'UNCOMPRESSABLE': incompressible_data.slice(size); break; + } + return {type: payload_type, body: body}; +} + +/** * Respond to an empty parameter with an empty response. * NOTE: this currently does not work due to issue #137 * @param {Call} call Call to handle @@ -58,7 +107,8 @@ function zeroBuffer(size) { * or error */ function handleEmpty(call, callback) { - callback(null, {}); + echoHeader(call); + callback(null, {}, getEchoTrailer(call)); } /** @@ -68,14 +118,17 @@ function handleEmpty(call, callback) { * error */ function handleUnary(call, callback) { + echoHeader(call); var req = call.request; - var zeros = zeroBuffer(req.response_size); - var payload_type = req.response_type; - if (payload_type === 'RANDOM') { - payload_type = ['COMPRESSABLE', - 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; + if (req.response_status) { + var status = req.response_status; + status.metadata = getEchoTrailer(call); + callback(status); + return; } - callback(null, {payload: {type: payload_type, body: zeros}}); + var payload = getPayload(req.response_type, req.response_size); + callback(null, {payload: payload}, + getEchoTrailer(call)); } /** @@ -85,12 +138,14 @@ function handleUnary(call, callback) { * error */ function handleStreamingInput(call, callback) { + echoHeader(call); var aggregate_size = 0; call.on('data', function(value) { aggregate_size += value.payload.body.length; }); call.on('end', function() { - callback(null, {aggregated_payload_size: aggregate_size}); + callback(null, {aggregated_payload_size: aggregate_size}, + getEchoTrailer(call)); }); } @@ -99,21 +154,18 @@ function handleStreamingInput(call, callback) { * @param {Call} call Call to handle */ function handleStreamingOutput(call) { + echoHeader(call); var req = call.request; - var payload_type = req.response_type; - if (payload_type === 'RANDOM') { - payload_type = ['COMPRESSABLE', - 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; + if (req.response_status) { + var status = req.response_status; + status.metadata = getEchoTrailer(call); + call.emit('error', status); + return; } _.each(req.response_parameters, function(resp_param) { - call.write({ - payload: { - body: zeroBuffer(resp_param.size), - type: payload_type - } - }); + call.write({payload: getPayload(req.response_type, resp_param.size)}); }); - call.end(); + call.end(getEchoTrailer(call)); } /** @@ -122,23 +174,20 @@ function handleStreamingOutput(call) { * @param {Call} call Call to handle */ function handleFullDuplex(call) { + echoHeader(call); call.on('data', function(value) { - var payload_type = value.response_type; - if (payload_type === 'RANDOM') { - payload_type = ['COMPRESSABLE', - 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; + if (value.response_status) { + var status = value.response_status; + status.metadata = getEchoTrailer(call); + call.emit('error', status); + return; } _.each(value.response_parameters, function(resp_param) { - call.write({ - payload: { - body: zeroBuffer(resp_param.size), - type: payload_type - } - }); + call.write({payload: getPayload(value.response_type, resp_param.size)}); }); }); call.on('end', function() { - call.end(); + call.end(getEchoTrailer(call)); }); } @@ -148,7 +197,7 @@ function handleFullDuplex(call) { * @param {Call} call Call to handle */ function handleHalfDuplex(call) { - throw new Error('HalfDuplexCall not yet implemented'); + call.emit('error', Error('HalfDuplexCall not yet implemented')); } /** diff --git a/src/node/package.json b/src/node/package.json deleted file mode 100644 index bb8987cc0d..0000000000 --- a/src/node/package.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "grpc", - "version": "0.11.0", - "author": "Google Inc.", - "description": "gRPC Library for Node", - "homepage": "http://www.grpc.io/", - "repository": { - "type": "git", - "url": "https://github.com/grpc/grpc.git" - }, - "bugs": "https://github.com/grpc/grpc/issues", - "contributors": [ - { - "name": "Michael Lumish", - "email": "mlumish@google.com" - } - ], - "directories": { - "lib": "src", - "example": "examples" - }, - "scripts": { - "lint": "node ./node_modules/jshint/bin/jshint src test examples interop index.js", - "test": "node ./node_modules/mocha/bin/mocha && npm run-script lint", - "gen_docs": "./node_modules/.bin/jsdoc -c jsdoc_conf.json" - }, - "dependencies": { - "bindings": "^1.2.0", - "lodash": "^3.9.3", - "nan": "^1.5.0", - "protobufjs": "^4.0.0" - }, - "devDependencies": { - "async": "^0.9.0", - "google-auth-library": "^0.9.2", - "jsdoc": "^3.3.2", - "jshint": "^2.5.0", - "minimist": "^1.1.0", - "mocha": "~1.21.0", - "mustache": "^2.0.0", - "strftime": "^0.8.2" - }, - "engines": { - "node": ">=0.10.13" - }, - "files": [ - "LICENSE", - "README.md", - "index.js", - "binding.gyp", - "bin", - "cli", - "examples", - "ext", - "interop", - "src", - "test" - ], - "main": "index.js", - "license": "BSD-3-Clause" -} diff --git a/src/node/examples/perf_test.js b/src/node/performance/perf_test.js index ba8fbf88d2..fe51e4a603 100644 --- a/src/node/examples/perf_test.js +++ b/src/node/performance/perf_test.js @@ -42,7 +42,7 @@ function runTest(iterations, callback) { var testServer = interop_server.getServer(0, false); testServer.server.start(); var client = new testProto.TestService('localhost:' + testServer.port, - grpc.Credentials.createInsecure()); + grpc.credentials.createInsecure()); function runIterations(finish) { var start = process.hrtime(); diff --git a/src/node/examples/qps_test.js b/src/node/performance/qps_test.js index ec968b8540..491f47364c 100644 --- a/src/node/examples/qps_test.js +++ b/src/node/performance/qps_test.js @@ -62,7 +62,7 @@ function runTest(concurrent_calls, seconds, callback) { var testServer = interop_server.getServer(0, false); testServer.server.start(); var client = new testProto.TestService('localhost:' + testServer.port, - grpc.Credentials.createInsecure()); + grpc.credentials.createInsecure()); var warmup_num = 100; diff --git a/src/node/src/client.js b/src/node/src/client.js index b427297a8a..3cdd550752 100644 --- a/src/node/src/client.js +++ b/src/node/src/client.js @@ -33,6 +33,17 @@ /** * Client module + * + * This module contains the factory method for creating Client classes, and the + * method calling code for all types of methods. + * + * For example, to create a client and call a method on it: + * + * var proto_obj = grpc.load(proto_file_path); + * var Client = proto_obj.package.subpackage.ServiceName; + * var client = new Client(server_address, client_credentials); + * var call = client.unaryMethod(arguments, callback); + * * @module */ @@ -40,7 +51,7 @@ var _ = require('lodash'); -var grpc = require('bindings')('grpc.node'); +var grpc = require('bindings')('grpc_node'); var common = require('./common'); @@ -54,7 +65,7 @@ var Readable = stream.Readable; var Writable = stream.Writable; var Duplex = stream.Duplex; var util = require('util'); -var version = require('../package.json').version; +var version = require('../../../package.json').version; util.inherits(ClientWritableStream, Writable); @@ -233,17 +244,23 @@ function getCall(channel, method, options) { var host; var parent; var propagate_flags; + var credentials; if (options) { deadline = options.deadline; host = options.host; parent = _.get(options, 'parent.call'); propagate_flags = options.propagate_flags; + credentials = options.credentials; } if (deadline === undefined) { deadline = Infinity; } - return new grpc.Call(channel, method, deadline, host, - parent, propagate_flags); + var call = new grpc.Call(channel, method, deadline, host, + parent, propagate_flags); + if (credentials) { + call.setCredentials(credentials); + } + return call; } /** @@ -282,60 +299,53 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { emitter.getPeer = function getPeer() { return call.getPeer(); }; - this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) { - if (error) { - call.cancel(); - callback(error); - return; - } - var client_batch = {}; - var message = serialize(argument); - if (options) { - message.grpcWriteFlags = options.flags; - } - client_batch[grpc.opType.SEND_INITIAL_METADATA] = - metadata._getCoreRepresentation(); - client_batch[grpc.opType.SEND_MESSAGE] = message; - client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; - client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; - client_batch[grpc.opType.RECV_MESSAGE] = true; - client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; - call.startBatch(client_batch, function(err, response) { - response.status.metadata = Metadata._fromCoreRepresentation( - response.status.metadata); - var status = response.status; - var error; - var deserialized; - if (status.code === grpc.status.OK) { - if (err) { - // Got a batch error, but OK status. Something went wrong - callback(err); - return; - } else { - try { - deserialized = deserialize(response.read); - } catch (e) { - /* Change status to indicate bad server response. This will result - * in passing an error to the callback */ - status = { - code: grpc.status.INTERNAL, - details: 'Failed to parse server response' - }; - } - } - } - if (status.code !== grpc.status.OK) { - error = new Error(response.status.details); - error.code = status.code; - error.metadata = status.metadata; - callback(error); + var client_batch = {}; + var message = serialize(argument); + if (options) { + message.grpcWriteFlags = options.flags; + } + client_batch[grpc.opType.SEND_INITIAL_METADATA] = + metadata._getCoreRepresentation(); + client_batch[grpc.opType.SEND_MESSAGE] = message; + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + response.status.metadata = Metadata._fromCoreRepresentation( + response.status.metadata); + var status = response.status; + var error; + var deserialized; + if (status.code === grpc.status.OK) { + if (err) { + // Got a batch error, but OK status. Something went wrong + callback(err); + return; } else { - callback(null, deserialized); + try { + deserialized = deserialize(response.read); + } catch (e) { + /* Change status to indicate bad server response. This will result + * in passing an error to the callback */ + status = { + code: grpc.status.INTERNAL, + details: 'Failed to parse server response' + }; + } } - emitter.emit('status', status); - emitter.emit('metadata', Metadata._fromCoreRepresentation( - response.metadata)); - }); + } + if (status.code !== grpc.status.OK) { + error = new Error(response.status.details); + error.code = status.code; + error.metadata = status.metadata; + callback(error); + } else { + callback(null, deserialized); + } + emitter.emit('status', status); + emitter.emit('metadata', Metadata._fromCoreRepresentation( + response.metadata)); }); return emitter; } @@ -371,62 +381,55 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { metadata = metadata.clone(); } var stream = new ClientWritableStream(call, serialize); - this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) { - if (error) { - call.cancel(); - callback(error); + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = + metadata._getCoreRepresentation(); + metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(metadata_batch, function(err, response) { + if (err) { + // The call has stopped for some reason. A non-OK status will arrive + // in the other batch. return; } - var metadata_batch = {}; - metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = - metadata._getCoreRepresentation(); - metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; - call.startBatch(metadata_batch, function(err, response) { + stream.emit('metadata', Metadata._fromCoreRepresentation( + response.metadata)); + }); + var client_batch = {}; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + response.status.metadata = Metadata._fromCoreRepresentation( + response.status.metadata); + var status = response.status; + var error; + var deserialized; + if (status.code === grpc.status.OK) { if (err) { - // The call has stopped for some reason. A non-OK status will arrive - // in the other batch. + // Got a batch error, but OK status. Something went wrong + callback(err); return; - } - stream.emit('metadata', Metadata._fromCoreRepresentation( - response.metadata)); - }); - var client_batch = {}; - client_batch[grpc.opType.RECV_MESSAGE] = true; - client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; - call.startBatch(client_batch, function(err, response) { - response.status.metadata = Metadata._fromCoreRepresentation( - response.status.metadata); - var status = response.status; - var error; - var deserialized; - if (status.code === grpc.status.OK) { - if (err) { - // Got a batch error, but OK status. Something went wrong - callback(err); - return; - } else { - try { - deserialized = deserialize(response.read); - } catch (e) { - /* Change status to indicate bad server response. This will result - * in passing an error to the callback */ - status = { - code: grpc.status.INTERNAL, - details: 'Failed to parse server response' - }; - } - } - } - if (status.code !== grpc.status.OK) { - error = new Error(response.status.details); - error.code = status.code; - error.metadata = status.metadata; - callback(error); } else { - callback(null, deserialized); + try { + deserialized = deserialize(response.read); + } catch (e) { + /* Change status to indicate bad server response. This will result + * in passing an error to the callback */ + status = { + code: grpc.status.INTERNAL, + details: 'Failed to parse server response' + }; + } } - stream.emit('status', status); - }); + } + if (status.code !== grpc.status.OK) { + error = new Error(response.status.details); + error.code = status.code; + error.metadata = status.metadata; + callback(error); + } else { + callback(null, deserialized); + } + stream.emit('status', status); }); return stream; } @@ -462,51 +465,44 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) { metadata = metadata.clone(); } var stream = new ClientReadableStream(call, deserialize); - this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) { - if (error) { - call.cancel(); - stream.emit('error', error); + var start_batch = {}; + var message = serialize(argument); + if (options) { + message.grpcWriteFlags = options.flags; + } + start_batch[grpc.opType.SEND_INITIAL_METADATA] = + metadata._getCoreRepresentation(); + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + start_batch[grpc.opType.SEND_MESSAGE] = message; + start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + // The call has stopped for some reason. A non-OK status will arrive + // in the other batch. return; } - var start_batch = {}; - var message = serialize(argument); - if (options) { - message.grpcWriteFlags = options.flags; - } - start_batch[grpc.opType.SEND_INITIAL_METADATA] = - metadata._getCoreRepresentation(); - start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; - start_batch[grpc.opType.SEND_MESSAGE] = message; - start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; - call.startBatch(start_batch, function(err, response) { + stream.emit('metadata', Metadata._fromCoreRepresentation( + response.metadata)); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + response.status.metadata = Metadata._fromCoreRepresentation( + response.status.metadata); + stream.emit('status', response.status); + if (response.status.code !== grpc.status.OK) { + var error = new Error(response.status.details); + error.code = response.status.code; + error.metadata = response.status.metadata; + stream.emit('error', error); + return; + } else { if (err) { - // The call has stopped for some reason. A non-OK status will arrive - // in the other batch. - return; - } - stream.emit('metadata', Metadata._fromCoreRepresentation( - response.metadata)); - }); - var status_batch = {}; - status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; - call.startBatch(status_batch, function(err, response) { - response.status.metadata = Metadata._fromCoreRepresentation( - response.status.metadata); - stream.emit('status', response.status); - if (response.status.code !== grpc.status.OK) { - var error = new Error(response.status.details); - error.code = response.status.code; - error.metadata = response.status.metadata; - stream.emit('error', error); + // Got a batch error, but OK status. Something went wrong + stream.emit('error', err); return; - } else { - if (err) { - // Got a batch error, but OK status. Something went wrong - stream.emit('error', err); - return; - } } - }); + } }); return stream; } @@ -540,45 +536,38 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) { metadata = metadata.clone(); } var stream = new ClientDuplexStream(call, serialize, deserialize); - this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) { - if (error) { - call.cancel(); - stream.emit('error', error); + var start_batch = {}; + start_batch[grpc.opType.SEND_INITIAL_METADATA] = + metadata._getCoreRepresentation(); + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + // The call has stopped for some reason. A non-OK status will arrive + // in the other batch. return; } - var start_batch = {}; - start_batch[grpc.opType.SEND_INITIAL_METADATA] = - metadata._getCoreRepresentation(); - start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; - call.startBatch(start_batch, function(err, response) { + stream.emit('metadata', Metadata._fromCoreRepresentation( + response.metadata)); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + response.status.metadata = Metadata._fromCoreRepresentation( + response.status.metadata); + stream.emit('status', response.status); + if (response.status.code !== grpc.status.OK) { + var error = new Error(response.status.details); + error.code = response.status.code; + error.metadata = response.status.metadata; + stream.emit('error', error); + return; + } else { if (err) { - // The call has stopped for some reason. A non-OK status will arrive - // in the other batch. + // Got a batch error, but OK status. Something went wrong + stream.emit('error', err); return; } - stream.emit('metadata', Metadata._fromCoreRepresentation( - response.metadata)); - }); - var status_batch = {}; - status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; - call.startBatch(status_batch, function(err, response) { - response.status.metadata = Metadata._fromCoreRepresentation( - response.status.metadata); - stream.emit('status', response.status); - if (response.status.code !== grpc.status.OK) { - var error = new Error(response.status.details); - error.code = response.status.code; - error.metadata = response.status.metadata; - stream.emit('error', error); - return; - } else { - if (err) { - // Got a batch error, but OK status. Something went wrong - stream.emit('error', err); - return; - } - } - }); + } }); return stream; } @@ -618,15 +607,8 @@ exports.makeClientConstructor = function(methods, serviceName) { * @param {grpc.Credentials} credentials Credentials to use to connect * to the server * @param {Object} options Options to pass to the underlying channel - * @param {function(string, Object, function)=} updateMetadata function to - * update the metadata for each request */ - function Client(address, credentials, options, updateMetadata) { - if (!updateMetadata) { - updateMetadata = function(uri, metadata, callback) { - callback(null, metadata); - }; - } + function Client(address, credentials, options) { if (!options) { options = {}; } @@ -634,11 +616,6 @@ exports.makeClientConstructor = function(methods, serviceName) { /* Private fields use $ as a prefix instead of _ because it is an invalid * prefix of a method name */ this.$channel = new grpc.Channel(address, credentials, options); - // Remove the optional DNS scheme, trailing port, and trailing backslash - address = address.replace(/^(dns:\/{3})?([^:\/]+)(:\d+)?\/?$/, '$2'); - this.$server_address = address; - this.$auth_uri = 'https://' + this.server_address + '/' + serviceName; - this.$updateMetadata = updateMetadata; } _.each(methods, function(attrs, name) { @@ -695,6 +672,7 @@ exports.waitForClientReady = function(client, deadline, callback) { var checkState = function(err) { if (err) { callback(new Error('Failed to connect before the deadline')); + return; } var new_state = client.$channel.getConnectivityState(true); if (new_state === grpc.connectivityState.READY) { diff --git a/src/node/src/common.js b/src/node/src/common.js index 5551ebeec8..e4fe5a8e03 100644 --- a/src/node/src/common.js +++ b/src/node/src/common.js @@ -32,6 +32,8 @@ */ /** + * This module contains functions that are common to client and server + * code. None of them should be used directly by gRPC users. * @module */ @@ -87,14 +89,9 @@ exports.fullyQualifiedName = function fullyQualifiedName(value) { return ''; } var name = value.name; - if (value.className === 'Service.RPCMethod') { - name = _.capitalize(name); - } - if (value.hasOwnProperty('parent')) { - var parent_name = fullyQualifiedName(value.parent); - if (parent_name !== '') { - name = parent_name + '.' + name; - } + var parent_name = fullyQualifiedName(value.parent); + if (parent_name !== '') { + name = parent_name + '.' + name; } return name; }; diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js new file mode 100644 index 0000000000..ff10a22e6a --- /dev/null +++ b/src/node/src/credentials.js @@ -0,0 +1,166 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * Credentials module + * + * This module contains factory methods for two different credential types: + * CallCredentials and ChannelCredentials. ChannelCredentials are things like + * SSL credentials that can be used to secure a connection, and are used to + * construct a Client object. CallCredentials genrally modify metadata, so they + * can be attached to an individual method call. + * + * CallCredentials can be composed with other CallCredentials to create + * CallCredentials. ChannelCredentials can be composed with CallCredentials + * to create ChannelCredentials. No combined credential can have more than + * one ChannelCredentials. + * + * For example, to create a client secured with SSL that uses Google + * default application credentials to authenticate: + * + * var channel_creds = credentials.createSsl(root_certs); + * (new GoogleAuth()).getApplicationDefault(function(err, credential) { + * var call_creds = credentials.createFromGoogleCredential(credential); + * var combined_creds = credentials.combineChannelCredentials( + * channel_creds, call_creds); + * var client = new Client(address, combined_creds); + * }); + * + * @module + */ + +'use strict'; + +var grpc = require('bindings')('grpc_node.node'); + +var CallCredentials = grpc.CallCredentials; + +var ChannelCredentials = grpc.ChannelCredentials; + +var Metadata = require('./metadata.js'); + +/** + * Create an SSL Credentials object. If using a client-side certificate, both + * the second and third arguments must be passed. + * @param {Buffer} root_certs The root certificate data + * @param {Buffer=} private_key The client certificate private key, if + * applicable + * @param {Buffer=} cert_chain The client certificate cert chain, if applicable + * @return {ChannelCredentials} The SSL Credentials object + */ +exports.createSsl = ChannelCredentials.createSsl; + +/** + * Create a gRPC credentials object from a metadata generation function. This + * function gets the service URL and a callback as parameters. The error + * passed to the callback can optionally have a 'code' value attached to it, + * which corresponds to a status code that this library uses. + * @param {function(String, function(Error, Metadata))} metadata_generator The + * function that generates metadata + * @return {CallCredentials} The credentials object + */ +exports.createFromMetadataGenerator = function(metadata_generator) { + return CallCredentials.createFromPlugin(function(service_url, callback) { + metadata_generator(service_url, function(error, metadata) { + var code = grpc.status.OK; + var message = ''; + if (error) { + message = error.message; + if (error.hasOwnProperty('code')) { + code = error.code; + } + if (!metadata) { + metadata = new Metadata(); + } + } + callback(code, message, metadata._getCoreRepresentation()); + }); + }); +}; + +/** + * Create a gRPC credential from a Google credential object. + * @param {Object} google_credential The Google credential object to use + * @return {CallCredentials} The resulting credentials object + */ +exports.createFromGoogleCredential = function(google_credential) { + return exports.createFromMetadataGenerator(function(service_url, callback) { + google_credential.getRequestMetadata(service_url, function(err, header) { + if (err) { + callback(err); + return; + } + var metadata = new Metadata(); + metadata.add('authorization', header.Authorization); + callback(null, metadata); + }); + }); +}; + +/** + * Combine a ChannelCredentials with any number of CallCredentials into a single + * ChannelCredentials object. + * @param {ChannelCredentials} channel_credential The ChannelCredentials to + * start with + * @param {...CallCredentials} credentials The CallCredentials to compose + * @return ChannelCredentials A credentials object that combines all of the + * input credentials + */ +exports.combineChannelCredentials = function(channel_credential) { + var current = channel_credential; + for (var i = 1; i < arguments.length; i++) { + current = current.compose(arguments[i]); + } + return current; +}; + +/** + * Combine any number of CallCredentials into a single CallCredentials object + * @param {...CallCredentials} credentials the CallCredentials to compose + * @return CallCredentials A credentials object that combines all of the input + * credentials + */ +exports.combineCallCredentials = function() { + var current = arguments[0]; + for (var i = 1; i < arguments.length; i++) { + current = current.compose(arguments[i]); + } + return current; +}; + +/** + * Create an insecure credentials object. This is used to create a channel that + * does not use SSL. This cannot be composed with anything. + * @return {ChannelCredentials} The insecure credentials object + */ +exports.createInsecure = ChannelCredentials.createInsecure; diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js index c1da70b197..0a2f1489b6 100644 --- a/src/node/src/metadata.js +++ b/src/node/src/metadata.js @@ -33,6 +33,15 @@ /** * Metadata module + * + * This module defines the Metadata class, which represents header and trailer + * metadata for gRPC calls. Here is an example of how to use it: + * + * var metadata = new metadata_module.Metadata(); + * metadata.set('key1', 'value1'); + * metadata.add('key1', 'value2'); + * metadata.get('key1') // returns ['value1', 'value2'] + * * @module */ @@ -173,7 +182,9 @@ Metadata.prototype._getCoreRepresentation = function() { Metadata._fromCoreRepresentation = function(metadata) { var newMetadata = new Metadata(); if (metadata) { - newMetadata._internal_repr = _.cloneDeep(metadata); + _.forOwn(metadata, function(value, key) { + newMetadata._internal_repr[key] = _.clone(value); + }); } return newMetadata; }; diff --git a/src/node/src/server.js b/src/node/src/server.js index b6f162adf8..d1fb627e6c 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -33,6 +33,17 @@ /** * Server module + * + * This module contains all the server code for Node gRPC: both the Server + * class itself and the method handler code for all types of methods. + * + * For example, to create a Server, add a service, and start it: + * + * var server = new server_module.Server(); + * server.addProtoService(protobuf_service_descriptor, service_implementation); + * server.bind('address:port', server_credential); + * server.start(); + * * @module */ @@ -40,7 +51,7 @@ var _ = require('lodash'); -var grpc = require('bindings')('grpc.node'); +var grpc = require('bindings')('grpc_node'); var common = require('./common'); @@ -276,6 +287,7 @@ function ServerWritableStream(call, serialize) { function _write(chunk, encoding, callback) { /* jshint validthis: true */ var batch = {}; + var self = this; if (!this.call.metadataSent) { batch[grpc.opType.SEND_INITIAL_METADATA] = (new Metadata())._getCoreRepresentation(); @@ -290,7 +302,7 @@ function _write(chunk, encoding, callback) { batch[grpc.opType.SEND_MESSAGE] = message; this.call.startBatch(batch, function(err, value) { if (err) { - this.emit('error', err); + self.emit('error', err); return; } callback(); @@ -305,6 +317,7 @@ ServerWritableStream.prototype._write = _write; */ function sendMetadata(responseMetadata) { /* jshint validthis: true */ + var self = this; if (!this.call.metadataSent) { this.call.metadataSent = true; var batch = []; @@ -312,7 +325,7 @@ function sendMetadata(responseMetadata) { responseMetadata._getCoreRepresentation(); this.call.startBatch(batch, function(err) { if (err) { - this.emit('error', err); + self.emit('error', err); return; } }); @@ -595,10 +608,6 @@ function Server(options) { throw new Error('Server is already running'); } this.started = true; - console.log('Server starting'); - _.each(handlers, function(handler, handler_name) { - console.log('Serving', handler_name); - }); server.start(); /** * Handles the SERVER_RPC_NEW event. If there is a handler associated with @@ -627,7 +636,7 @@ function Server(options) { (new Metadata())._getCoreRepresentation(); batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { code: grpc.status.UNIMPLEMENTED, - details: 'This method is not available on this server.', + details: '', metadata: {} }; batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; @@ -748,8 +757,8 @@ Server.prototype.addProtoService = function(service, implementation) { * Binds the server to the given port, with SSL enabled if creds is given * @param {string} port The port that the server should bind on, in the format * "address:port" - * @param {boolean=} creds Server credential object to be used for SSL. Pass - * nothing for an insecure port + * @param {ServerCredentials=} creds Server credential object to be used for + * SSL. Pass an insecure credentials object for an insecure port. */ Server.prototype.bind = function(port, creds) { if (this.started) { diff --git a/src/node/test/async_test.js b/src/node/test/async_test.js new file mode 100644 index 0000000000..0af63c379e --- /dev/null +++ b/src/node/test/async_test.js @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +'use strict'; + +var assert = require('assert'); + +var grpc = require('..'); +var math = grpc.load(__dirname + '/math/math.proto').math; + + +/** + * Client to use to make requests to a running server. + */ +var math_client; + +/** + * Server to test against + */ +var getServer = require('./math/math_server.js'); + +var server = getServer(); + +describe('Async functionality', function() { + before(function(done) { + var port_num = server.bind('0.0.0.0:0', + grpc.ServerCredentials.createInsecure()); + server.start(); + math_client = new math.Math('localhost:' + port_num, + grpc.credentials.createInsecure()); + done(); + }); + after(function() { + server.forceShutdown(); + }); + it('should not hang', function(done) { + var chunkCount=0; + var call = math_client.sum(function handleSumResult(err, value) { + assert.ifError(err); + assert.equal(value.num, chunkCount); + }); + + var path = require('path'); + var fs = require('fs'); + var fileToRead = path.join(__dirname, 'numbers.txt'); + var readStream = fs.createReadStream(fileToRead); + + readStream.once('readable', function () { + readStream.on('data', function (chunk) { + call.write({'num': 1}); + chunkCount += 1; + }); + + readStream.on('end', function () { + call.end(); + }); + + readStream.on('error', function (error) { + }); + }); + + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, grpc.status.OK); + done(); + }); + }); +}); diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js index e7f071bcd5..f1f86b35db 100644 --- a/src/node/test/call_test.js +++ b/src/node/test/call_test.js @@ -34,7 +34,7 @@ 'use strict'; var assert = require('assert'); -var grpc = require('bindings')('grpc.node'); +var grpc = require('bindings')('grpc_node'); /** * Helper function to return an absolute deadline given a relative timeout in @@ -48,7 +48,7 @@ function getDeadline(timeout_secs) { return deadline; } -var insecureCreds = grpc.Credentials.createInsecure(); +var insecureCreds = grpc.ChannelCredentials.createInsecure(); describe('call', function() { var channel; @@ -107,6 +107,23 @@ describe('call', function() { new grpc.Call(channel, 'method', 'now'); }, TypeError); }); + it('should succeed without the new keyword', function() { + assert.doesNotThrow(function() { + var call = grpc.Call(channel, 'method', new Date()); + assert(call instanceof grpc.Call); + }); + }); + }); + describe('deadline', function() { + it('should time out immediately with negative deadline', function(done) { + var call = new grpc.Call(channel, 'method', -Infinity); + var batch = {}; + batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(batch, function(err, response) { + assert.strictEqual(response.status.code, grpc.status.DEADLINE_EXCEEDED); + done(); + }); + }); }); describe('startBatch', function() { it('should fail without an object and a function', function() { @@ -192,6 +209,43 @@ describe('call', function() { }); }); }); + describe('cancelWithStatus', function() { + it('should reject anything other than an integer and a string', function() { + assert.doesNotThrow(function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.cancelWithStatus(1, 'details'); + }); + assert.throws(function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.cancelWithStatus(); + }); + assert.throws(function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.cancelWithStatus(''); + }); + assert.throws(function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.cancelWithStatus(5, {}); + }); + }); + it('should reject the OK status code', function() { + assert.throws(function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.cancelWithStatus(0, 'details'); + }); + }); + it('should result in the call ending with a status', function(done) { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + var batch = {}; + batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(batch, function(err, response) { + assert.strictEqual(response.status.code, 5); + assert.strictEqual(response.status.details, 'details'); + done(); + }); + call.cancelWithStatus(5, 'details'); + }); + }); describe('getPeer', function() { it('should return a string', function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); diff --git a/src/node/test/channel_test.js b/src/node/test/channel_test.js index d81df2a36d..7163a5fb5e 100644 --- a/src/node/test/channel_test.js +++ b/src/node/test/channel_test.js @@ -34,7 +34,7 @@ 'use strict'; var assert = require('assert'); -var grpc = require('bindings')('grpc.node'); +var grpc = require('bindings')('grpc_node'); /** * This is used for testing functions with multiple asynchronous calls that @@ -56,7 +56,7 @@ function multiDone(done, count) { } }; } -var insecureCreds = grpc.Credentials.createInsecure(); +var insecureCreds = grpc.ChannelCredentials.createInsecure(); describe('channel', function() { describe('constructor', function() { @@ -104,6 +104,12 @@ describe('channel', function() { new grpc.Channel('hostname', insecureCreds, {'key' : new Date()}); }); }); + it('should succeed without the new keyword', function() { + assert.doesNotThrow(function() { + var channel = grpc.Channel('hostname', insecureCreds); + assert(channel instanceof grpc.Channel); + }); + }); }); describe('close', function() { var channel; diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js index fa06ad4e4d..b17cd339cb 100644 --- a/src/node/test/constant_test.js +++ b/src/node/test/constant_test.js @@ -34,7 +34,7 @@ 'use strict'; var assert = require('assert'); -var grpc = require('bindings')('grpc.node'); +var grpc = require('bindings')('grpc_node'); /** * List of all status names diff --git a/src/node/test/credentials_test.js b/src/node/test/credentials_test.js new file mode 100644 index 0000000000..3e01b62cf4 --- /dev/null +++ b/src/node/test/credentials_test.js @@ -0,0 +1,305 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +'use strict'; + +var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); + +var grpc = require('..'); + +/** + * This is used for testing functions with multiple asynchronous calls that + * can happen in different orders. This should be passed the number of async + * function invocations that can occur last, and each of those should call this + * function's return value + * @param {function()} done The function that should be called when a test is + * complete. + * @param {number} count The number of calls to the resulting function if the + * test passes. + * @return {function()} The function that should be called at the end of each + * sequence of asynchronous functions. + */ +function multiDone(done, count) { + return function() { + count -= 1; + if (count <= 0) { + done(); + } + }; +} + +var fakeSuccessfulGoogleCredentials = { + getRequestMetadata: function(service_url, callback) { + setTimeout(function() { + callback(null, {Authorization: 'success'}); + }, 0); + } +}; + +var fakeFailingGoogleCredentials = { + getRequestMetadata: function(service_url, callback) { + setTimeout(function() { + callback(new Error('Authorization failure')); + }, 0); + } +}; + +describe('client credentials', function() { + var Client; + var server; + var port; + var client_ssl_creds; + var client_options = {}; + before(function() { + var proto = grpc.load(__dirname + '/test_service.proto'); + server = new grpc.Server(); + server.addProtoService(proto.TestService.service, { + unary: function(call, cb) { + call.sendMetadata(call.metadata); + cb(null, {}); + }, + clientStream: function(stream, cb){ + stream.on('data', function(data) {}); + stream.on('end', function() { + stream.sendMetadata(stream.metadata); + cb(null, {}); + }); + }, + serverStream: function(stream) { + stream.sendMetadata(stream.metadata); + stream.end(); + }, + bidiStream: function(stream) { + stream.on('data', function(data) {}); + stream.on('end', function() { + stream.sendMetadata(stream.metadata); + stream.end(); + }); + } + }); + var key_path = path.join(__dirname, './data/server1.key'); + var pem_path = path.join(__dirname, './data/server1.pem'); + var key_data = fs.readFileSync(key_path); + var pem_data = fs.readFileSync(pem_path); + var creds = grpc.ServerCredentials.createSsl(null, + [{private_key: key_data, + cert_chain: pem_data}]); + //creds = grpc.ServerCredentials.createInsecure(); + port = server.bind('localhost:0', creds); + server.start(); + + Client = proto.TestService; + var ca_path = path.join(__dirname, '../test/data/ca.pem'); + var ca_data = fs.readFileSync(ca_path); + client_ssl_creds = grpc.credentials.createSsl(ca_data); + var host_override = 'foo.test.google.fr'; + client_options['grpc.ssl_target_name_override'] = host_override; + client_options['grpc.default_authority'] = host_override; + }); + after(function() { + server.forceShutdown(); + }); + it('Should accept SSL creds for a client', function(done) { + var client = new Client('localhost:' + port, client_ssl_creds, + client_options); + client.unary({}, function(err, data) { + assert.ifError(err); + done(); + }); + }); + it('Should update metadata with SSL creds', function(done) { + var metadataUpdater = function(service_url, callback) { + var metadata = new grpc.Metadata(); + metadata.set('plugin_key', 'plugin_value'); + callback(null, metadata); + }; + var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater); + var combined_creds = grpc.credentials.combineChannelCredentials( + client_ssl_creds, creds); + var client = new Client('localhost:' + port, combined_creds, + client_options); + var call = client.unary({}, function(err, data) { + assert.ifError(err); + }); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + }); + it('Should update metadata for two simultaneous calls', function(done) { + done = multiDone(done, 2); + var metadataUpdater = function(service_url, callback) { + var metadata = new grpc.Metadata(); + metadata.set('plugin_key', 'plugin_value'); + callback(null, metadata); + }; + var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater); + var combined_creds = grpc.credentials.combineChannelCredentials( + client_ssl_creds, creds); + var client = new Client('localhost:' + port, combined_creds, + client_options); + var call = client.unary({}, function(err, data) { + assert.ifError(err); + }); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + var call2 = client.unary({}, function(err, data) { + assert.ifError(err); + }); + call2.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + }); + it.skip('should propagate errors that the updater emits', function(done) { + var metadataUpdater = function(service_url, callback) { + var error = new Error('Authentication error'); + error.code = grpc.status.UNAUTHENTICATED; + callback(error); + }; + var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater); + var combined_creds = grpc.credentials.combineChannelCredentials( + client_ssl_creds, creds); + var client = new Client('localhost:' + port, combined_creds, + client_options); + client.unary({}, function(err, data) { + assert(err); + assert.strictEqual(err.message, 'Authentication error'); + assert.strictEqual(err.code, grpc.status.UNAUTHENTICATED); + done(); + }); + }); + it('should successfully wrap a Google credential', function(done) { + var creds = grpc.credentials.createFromGoogleCredential( + fakeSuccessfulGoogleCredentials); + var combined_creds = grpc.credentials.combineChannelCredentials( + client_ssl_creds, creds); + var client = new Client('localhost:' + port, combined_creds, + client_options); + var call = client.unary({}, function(err, data) { + assert.ifError(err); + }); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('authorization'), ['success']); + done(); + }); + }); + it.skip('should get an error from a Google credential', function(done) { + var creds = grpc.credentials.createFromGoogleCredential( + fakeFailingGoogleCredentials); + var combined_creds = grpc.credentials.combineChannelCredentials( + client_ssl_creds, creds); + var client = new Client('localhost:' + port, combined_creds, + client_options); + client.unary({}, function(err, data) { + assert(err); + assert.strictEqual(err.message, 'Authorization failure'); + done(); + }); + }); + describe('Per-rpc creds', function() { + var client; + var updater_creds; + before(function() { + client = new Client('localhost:' + port, client_ssl_creds, + client_options); + var metadataUpdater = function(service_url, callback) { + var metadata = new grpc.Metadata(); + metadata.set('plugin_key', 'plugin_value'); + callback(null, metadata); + }; + updater_creds = grpc.credentials.createFromMetadataGenerator( + metadataUpdater); + }); + it('Should update metadata on a unary call', function(done) { + var call = client.unary({}, function(err, data) { + assert.ifError(err); + }, null, {credentials: updater_creds}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + }); + it('should update metadata on a client streaming call', function(done) { + var call = client.clientStream(function(err, data) { + assert.ifError(err); + }, null, {credentials: updater_creds}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + call.end(); + }); + it('should update metadata on a server streaming call', function(done) { + var call = client.serverStream({}, null, {credentials: updater_creds}); + call.on('data', function() {}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + }); + it('should update metadata on a bidi streaming call', function(done) { + var call = client.bidiStream(null, {credentials: updater_creds}); + call.on('data', function() {}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + call.end(); + }); + it('should be able to use multiple plugin credentials', function(done) { + var altMetadataUpdater = function(service_url, callback) { + var metadata = new grpc.Metadata(); + metadata.set('other_plugin_key', 'other_plugin_value'); + callback(null, metadata); + }; + var alt_updater_creds = grpc.credentials.createFromMetadataGenerator( + altMetadataUpdater); + var combined_updater = grpc.credentials.combineCallCredentials( + updater_creds, alt_updater_creds); + var call = client.unary({}, function(err, data) { + assert.ifError(err); + }, null, {credentials: combined_updater}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + assert.deepEqual(metadata.get('other_plugin_key'), + ['other_plugin_value']); + done(); + }); + }); + }); +}); diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js index 4b8da3bfb1..0f6c5941c4 100644 --- a/src/node/test/end_to_end_test.js +++ b/src/node/test/end_to_end_test.js @@ -34,7 +34,7 @@ 'use strict'; var assert = require('assert'); -var grpc = require('bindings')('grpc.node'); +var grpc = require('bindings')('grpc_node'); /** * This is used for testing functions with multiple asynchronous calls that @@ -57,7 +57,7 @@ function multiDone(done, count) { }; } -var insecureCreds = grpc.Credentials.createInsecure(); +var insecureCreds = grpc.ChannelCredentials.createInsecure(); describe('end-to-end', function() { var server; diff --git a/src/node/test/health_test.js b/src/node/test/health_test.js index 9267bff7eb..c93b528d42 100644 --- a/src/node/test/health_test.js +++ b/src/node/test/health_test.js @@ -45,16 +45,18 @@ describe('Health Checking', function() { 'grpc.test.TestServiceNotServing': 'NOT_SERVING', 'grpc.test.TestServiceServing': 'SERVING' }; - var healthServer = new grpc.Server(); - healthServer.addProtoService(health.service, - new health.Implementation(statusMap)); + var healthServer; + var healthImpl; var healthClient; before(function() { + healthServer = new grpc.Server(); + healthImpl = new health.Implementation(statusMap); + healthServer.addProtoService(health.service, healthImpl); var port_num = healthServer.bind('0.0.0.0:0', grpc.ServerCredentials.createInsecure()); healthServer.start(); healthClient = new health.Client('localhost:' + port_num, - grpc.Credentials.createInsecure()); + grpc.credentials.createInsecure()); }); after(function() { healthServer.forceShutdown(); @@ -89,4 +91,16 @@ describe('Health Checking', function() { done(); }); }); + it('should get a different response if the status changes', function(done) { + healthClient.check({service: 'transient'}, function(err, response) { + assert(err); + assert.strictEqual(err.code, grpc.status.NOT_FOUND); + healthImpl.setStatus('transient', 'SERVING'); + healthClient.check({service: 'transient'}, function(err, response) { + assert.ifError(err); + assert.strictEqual(response.status, 'SERVING'); + done(); + }); + }); + }); }); diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index 2ca07c1d50..f008a87585 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -90,4 +90,16 @@ describe('Interop tests', function() { interop_client.runTest(port, name_override, 'timeout_on_sleeping_server', true, true, done); }); + it('should pass custom_metadata', function(done) { + interop_client.runTest(port, name_override, 'custom_metadata', + true, true, done); + }); + it('should pass status_code_and_message', function(done) { + interop_client.runTest(port, name_override, 'status_code_and_message', + true, true, done); + }); + it('should pass unimplemented_method', function(done) { + interop_client.runTest(port, name_override, 'unimplemented_method', + true, true, done); + }); }); diff --git a/src/node/examples/math.proto b/src/node/test/math/math.proto index 311e148c02..311e148c02 100644 --- a/src/node/examples/math.proto +++ b/src/node/test/math/math.proto diff --git a/src/node/examples/math_server.js b/src/node/test/math/math_server.js index 31892c65df..9d06596f3d 100644 --- a/src/node/examples/math_server.js +++ b/src/node/test/math/math_server.js @@ -33,7 +33,7 @@ 'use strict'; -var grpc = require('..'); +var grpc = require('../..'); var math = grpc.load(__dirname + '/math.proto').math; /** @@ -106,15 +106,20 @@ function mathDivMany(stream) { stream.end(); }); } -var server = new grpc.Server(); -server.addProtoService(math.Math.service, { - div: mathDiv, - fib: mathFib, - sum: mathSum, - divMany: mathDivMany -}); + +function getMathServer() { + var server = new grpc.Server(); + server.addProtoService(math.Math.service, { + div: mathDiv, + fib: mathFib, + sum: mathSum, + divMany: mathDivMany + }); + return server; +} if (require.main === module) { + var server = getMathServer(); server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); server.start(); } @@ -122,4 +127,4 @@ if (require.main === module) { /** * See docs for server */ -module.exports = server; +module.exports = getMathServer; diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js index 80b0c5ff2a..6361d97857 100644 --- a/src/node/test/math_client_test.js +++ b/src/node/test/math_client_test.js @@ -36,7 +36,7 @@ var assert = require('assert'); var grpc = require('..'); -var math = grpc.load(__dirname + '/../examples/math.proto').math; +var math = grpc.load(__dirname + '/math/math.proto').math; /** * Client to use to make requests to a running server. @@ -46,8 +46,9 @@ var math_client; /** * Server to test against */ -var server = require('../examples/math_server.js'); +var getServer = require('./math/math_server.js'); +var server = getServer(); describe('Math client', function() { before(function(done) { @@ -55,7 +56,7 @@ describe('Math client', function() { grpc.ServerCredentials.createInsecure()); server.start(); math_client = new math.Math('localhost:' + port_num, - grpc.Credentials.createInsecure()); + grpc.credentials.createInsecure()); done(); }); after(function() { diff --git a/src/node/test/numbers.txt b/src/node/test/numbers.txt new file mode 100644 index 0000000000..4972919b4e --- /dev/null +++ b/src/node/test/numbers.txt @@ -0,0 +1,496 @@ +1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
+1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js index 1e69d52e58..999a183b3c 100644 --- a/src/node/test/server_test.js +++ b/src/node/test/server_test.js @@ -36,7 +36,7 @@ var assert = require('assert'); var fs = require('fs'); var path = require('path'); -var grpc = require('bindings')('grpc.node'); +var grpc = require('bindings')('grpc_node'); describe('server', function() { describe('constructor', function() { diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js index d917c7a171..39673e4e05 100644 --- a/src/node/test/surface_test.js +++ b/src/node/test/surface_test.js @@ -41,7 +41,7 @@ var ProtoBuf = require('protobufjs'); var grpc = require('..'); -var math_proto = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto'); +var math_proto = ProtoBuf.loadProtoFile(__dirname + '/math/math.proto'); var mathService = math_proto.lookup('math.Math'); @@ -92,6 +92,31 @@ describe('File loader', function() { }); }); }); +describe('surface Server', function() { + var server; + beforeEach(function() { + server = new grpc.Server(); + }); + afterEach(function() { + server.forceShutdown(); + }); + it('should error if started twice', function() { + server.start(); + assert.throws(function() { + server.start(); + }); + }); + it('should error if a port is bound after the server starts', function() { + server.start(); + assert.throws(function() { + server.bind('localhost:0', grpc.ServerCredentials.createInsecure()); + }); + }); + it('should successfully shutdown if tryShutdown is called', function(done) { + server.start(); + server.tryShutdown(done); + }); +}); describe('Server.prototype.addProtoService', function() { var server; var dummyImpls = { @@ -163,7 +188,7 @@ describe('waitForClientReady', function() { Client = surface_client.makeProtobufClientConstructor(mathService); }); beforeEach(function() { - client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + client = new Client('localhost:' + port, grpc.credentials.createInsecure()); }); after(function() { server.forceShutdown(); @@ -202,6 +227,16 @@ describe('waitForClientReady', function() { }); }); }); + it('should time out if the server does not exist', function(done) { + var bad_client = new Client('nonexistent_hostname', + grpc.credentials.createInsecure()); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + grpc.waitForClientReady(bad_client, deadline, function(error) { + assert(error); + done(); + }); + }); }); describe('Echo service', function() { var server; @@ -217,7 +252,7 @@ describe('Echo service', function() { }); var port = server.bind('localhost:0', server_insecure_creds); var Client = surface_client.makeProtobufClientConstructor(echo_service); - client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + client = new Client('localhost:' + port, grpc.credentials.createInsecure()); server.start(); }); after(function() { @@ -263,7 +298,7 @@ describe('Generic client and server', function() { server.start(); var Client = grpc.makeGenericClientConstructor(string_service_attrs); client = new Client('localhost:' + port, - grpc.Credentials.createInsecure()); + grpc.credentials.createInsecure()); }); after(function() { server.forceShutdown(); @@ -311,7 +346,7 @@ describe('Echo metadata', function() { }); var port = server.bind('localhost:0', server_insecure_creds); var Client = surface_client.makeProtobufClientConstructor(test_service); - client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + client = new Client('localhost:' + port, grpc.credentials.createInsecure()); server.start(); metadata = new grpc.Metadata(); metadata.set('key', 'value'); @@ -356,7 +391,7 @@ describe('Echo metadata', function() { call.end(); }); it('shows the correct user-agent string', function(done) { - var version = require('../package.json').version; + var version = require('../../../package.json').version; var call = client.unary({}, function(err, data) { assert.ifError(err); }, metadata); call.on('metadata', function(metadata) { @@ -365,6 +400,123 @@ describe('Echo metadata', function() { done(); }); }); + it('properly handles duplicate values', function(done) { + var dup_metadata = metadata.clone(); + dup_metadata.add('key', 'value2'); + var call = client.unary({}, function(err, data) {assert.ifError(err); }, + dup_metadata); + call.on('metadata', function(resp_metadata) { + // Two arrays are equal iff their symmetric difference is empty + assert.deepEqual(_.xor(dup_metadata.get('key'), resp_metadata.get('key')), + []); + done(); + }); + }); +}); +describe('Client malformed response handling', function() { + var server; + var client; + var badArg = new Buffer([0xFF]); + before(function() { + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); + var test_service = test_proto.lookup('TestService'); + var malformed_test_service = { + unary: { + path: '/TestService/Unary', + requestStream: false, + responseStream: false, + requestDeserialize: _.identity, + responseSerialize: _.identity + }, + clientStream: { + path: '/TestService/ClientStream', + requestStream: true, + responseStream: false, + requestDeserialize: _.identity, + responseSerialize: _.identity + }, + serverStream: { + path: '/TestService/ServerStream', + requestStream: false, + responseStream: true, + requestDeserialize: _.identity, + responseSerialize: _.identity + }, + bidiStream: { + path: '/TestService/BidiStream', + requestStream: true, + responseStream: true, + requestDeserialize: _.identity, + responseSerialize: _.identity + } + }; + server = new grpc.Server(); + server.addService(malformed_test_service, { + unary: function(call, cb) { + cb(null, badArg); + }, + clientStream: function(stream, cb) { + stream.on('data', function() {/* Ignore requests */}); + stream.on('end', function() { + cb(null, badArg); + }); + }, + serverStream: function(stream) { + stream.write(badArg); + stream.end(); + }, + bidiStream: function(stream) { + stream.on('data', function() { + // Ignore requests + stream.write(badArg); + }); + stream.on('end', function() { + stream.end(); + }); + } + }); + var port = server.bind('localhost:0', server_insecure_creds); + var Client = surface_client.makeProtobufClientConstructor(test_service); + client = new Client('localhost:' + port, grpc.credentials.createInsecure()); + server.start(); + }); + after(function() { + server.forceShutdown(); + }); + it('should get an INTERNAL status with a unary call', function(done) { + client.unary({}, function(err, data) { + assert(err); + assert.strictEqual(err.code, grpc.status.INTERNAL); + done(); + }); + }); + it('should get an INTERNAL status with a client stream call', function(done) { + var call = client.clientStream(function(err, data) { + assert(err); + assert.strictEqual(err.code, grpc.status.INTERNAL); + done(); + }); + call.write({}); + call.end(); + }); + it('should get an INTERNAL status with a server stream call', function(done) { + var call = client.serverStream({}); + call.on('data', function(){}); + call.on('error', function(err) { + assert.strictEqual(err.code, grpc.status.INTERNAL); + done(); + }); + }); + it('should get an INTERNAL status with a bidi stream call', function(done) { + var call = client.bidiStream(); + call.on('data', function(){}); + call.on('error', function(err) { + assert.strictEqual(err.code, grpc.status.INTERNAL); + done(); + }); + call.write({}); + call.end(); + }); }); describe('Other conditions', function() { var test_service; @@ -382,7 +534,8 @@ describe('Other conditions', function() { unary: function(call, cb) { var req = call.request; if (req.error) { - cb(new Error('Requested error'), null, trailer_metadata); + cb({code: grpc.status.UNKNOWN, + details: 'Requested error'}, null, trailer_metadata); } else { cb(null, {count: 1}, trailer_metadata); } @@ -407,7 +560,8 @@ describe('Other conditions', function() { serverStream: function(stream) { var req = stream.request; if (req.error) { - var err = new Error('Requested error'); + var err = {code: grpc.status.UNKNOWN, + details: 'Requested error'}; err.metadata = trailer_metadata; stream.emit('error', err); } else { @@ -437,7 +591,7 @@ describe('Other conditions', function() { }); port = server.bind('localhost:0', server_insecure_creds); Client = surface_client.makeProtobufClientConstructor(test_service); - client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + client = new Client('localhost:' + port, grpc.credentials.createInsecure()); server.start(); }); after(function() { @@ -447,6 +601,23 @@ describe('Other conditions', function() { assert.strictEqual(typeof grpc.getClientChannel(client).getTarget(), 'string'); }); + it('client should be able to pause and resume a stream', function(done) { + var call = client.bidiStream(); + call.on('data', function(data) { + assert(data.count < 3); + call.pause(); + setTimeout(function() { + call.resume(); + }, 10); + }); + call.on('end', function() { + done(); + }); + call.write({}); + call.write({}); + call.write({}); + call.end(); + }); describe('Server recieving bad input', function() { var misbehavingClient; var badArg = new Buffer([0xFF]); @@ -484,7 +655,7 @@ describe('Other conditions', function() { var Client = surface_client.makeClientConstructor(test_service_attrs, 'TestService'); misbehavingClient = new Client('localhost:' + port, - grpc.Credentials.createInsecure()); + grpc.credentials.createInsecure()); }); it('should respond correctly to a unary call', function(done) { misbehavingClient.unary(badArg, function(err, data) { @@ -690,165 +861,187 @@ describe('Other conditions', function() { }); }); }); - describe('Call propagation', function() { - var proxy; - var proxy_impl; - beforeEach(function() { - proxy = new grpc.Server(); - proxy_impl = { - unary: function(call) {}, - clientStream: function(stream) {}, - serverStream: function(stream) {}, - bidiStream: function(stream) {} - }; +}); +describe('Call propagation', function() { + var proxy; + var proxy_impl; + + var test_service; + var Client; + var client; + var server; + before(function() { + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); + test_service = test_proto.lookup('TestService'); + server = new grpc.Server(); + server.addProtoService(test_service, { + unary: function(call) {}, + clientStream: function(stream) {}, + serverStream: function(stream) {}, + bidiStream: function(stream) {} }); - afterEach(function() { - console.log('Shutting down server'); - proxy.forceShutdown(); - }); - describe('Cancellation', function() { - it('With a unary call', function(done) { - done = multiDone(done, 2); - proxy_impl.unary = function(parent, callback) { - client.unary(parent.request, function(err, value) { - try { - assert(err); - assert.strictEqual(err.code, grpc.status.CANCELLED); - } finally { - callback(err, value); - done(); - } - }, null, {parent: parent}); - call.cancel(); - }; - proxy.addProtoService(test_service, proxy_impl); - var proxy_port = proxy.bind('localhost:0', server_insecure_creds); - proxy.start(); - var proxy_client = new Client('localhost:' + proxy_port, - grpc.Credentials.createInsecure()); - var call = proxy_client.unary({}, function(err, value) { - done(); - }); - }); - it('With a client stream call', function(done) { - done = multiDone(done, 2); - proxy_impl.clientStream = function(parent, callback) { - client.clientStream(function(err, value) { - try { - assert(err); - assert.strictEqual(err.code, grpc.status.CANCELLED); - } finally { - callback(err, value); - done(); - } - }, null, {parent: parent}); - call.cancel(); - }; - proxy.addProtoService(test_service, proxy_impl); - var proxy_port = proxy.bind('localhost:0', server_insecure_creds); - proxy.start(); - var proxy_client = new Client('localhost:' + proxy_port, - grpc.Credentials.createInsecure()); - var call = proxy_client.clientStream(function(err, value) { - done(); - }); - }); - it('With a server stream call', function(done) { - done = multiDone(done, 2); - proxy_impl.serverStream = function(parent) { - var child = client.serverStream(parent.request, null, - {parent: parent}); - child.on('error', function(err) { + var port = server.bind('localhost:0', server_insecure_creds); + Client = surface_client.makeProtobufClientConstructor(test_service); + client = new Client('localhost:' + port, grpc.credentials.createInsecure()); + server.start(); + }); + after(function() { + server.forceShutdown(); + }); + beforeEach(function() { + proxy = new grpc.Server(); + proxy_impl = { + unary: function(call) {}, + clientStream: function(stream) {}, + serverStream: function(stream) {}, + bidiStream: function(stream) {} + }; + }); + afterEach(function() { + proxy.forceShutdown(); + }); + describe('Cancellation', function() { + it('With a unary call', function(done) { + done = multiDone(done, 2); + proxy_impl.unary = function(parent, callback) { + client.unary(parent.request, function(err, value) { + try { assert(err); assert.strictEqual(err.code, grpc.status.CANCELLED); + } finally { + callback(err, value); done(); - }); - call.cancel(); - }; - proxy.addProtoService(test_service, proxy_impl); - var proxy_port = proxy.bind('localhost:0', server_insecure_creds); - proxy.start(); - var proxy_client = new Client('localhost:' + proxy_port, - grpc.Credentials.createInsecure()); - var call = proxy_client.serverStream({}); - call.on('error', function(err) { - done(); - }); + } + }, null, {parent: parent}); + call.cancel(); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.credentials.createInsecure()); + var call = proxy_client.unary({}, function(err, value) { + done(); }); - it('With a bidi stream call', function(done) { - done = multiDone(done, 2); - proxy_impl.bidiStream = function(parent) { - var child = client.bidiStream(null, {parent: parent}); - child.on('error', function(err) { + }); + it('With a client stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.clientStream = function(parent, callback) { + client.clientStream(function(err, value) { + try { assert(err); assert.strictEqual(err.code, grpc.status.CANCELLED); + } finally { + callback(err, value); done(); - }); - call.cancel(); - }; - proxy.addProtoService(test_service, proxy_impl); - var proxy_port = proxy.bind('localhost:0', server_insecure_creds); - proxy.start(); - var proxy_client = new Client('localhost:' + proxy_port, - grpc.Credentials.createInsecure()); - var call = proxy_client.bidiStream(); - call.on('error', function(err) { + } + }, null, {parent: parent}); + call.cancel(); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.credentials.createInsecure()); + var call = proxy_client.clientStream(function(err, value) { + done(); + }); + }); + it('With a server stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.serverStream = function(parent) { + var child = client.serverStream(parent.request, null, + {parent: parent}); + child.on('error', function(err) { + assert(err); + assert.strictEqual(err.code, grpc.status.CANCELLED); done(); }); + call.cancel(); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.credentials.createInsecure()); + var call = proxy_client.serverStream({}); + call.on('error', function(err) { + done(); }); }); - describe('Deadline', function() { - /* jshint bitwise:false */ - var deadline_flags = (grpc.propagate.DEFAULTS & - ~grpc.propagate.CANCELLATION); - it('With a client stream call', function(done) { - done = multiDone(done, 2); - proxy_impl.clientStream = function(parent, callback) { - client.clientStream(function(err, value) { - try { - assert(err); - assert(err.code === grpc.status.DEADLINE_EXCEEDED || - err.code === grpc.status.INTERNAL); - } finally { - callback(err, value); - done(); - } - }, null, {parent: parent, propagate_flags: deadline_flags}); - }; - proxy.addProtoService(test_service, proxy_impl); - var proxy_port = proxy.bind('localhost:0', server_insecure_creds); - proxy.start(); - var proxy_client = new Client('localhost:' + proxy_port, - grpc.Credentials.createInsecure()); - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + 1); - proxy_client.clientStream(function(err, value) { + it('With a bidi stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.bidiStream = function(parent) { + var child = client.bidiStream(null, {parent: parent}); + child.on('error', function(err) { + assert(err); + assert.strictEqual(err.code, grpc.status.CANCELLED); done(); - }, null, {deadline: deadline}); - }); - it('With a bidi stream call', function(done) { - done = multiDone(done, 2); - proxy_impl.bidiStream = function(parent) { - var child = client.bidiStream( - null, {parent: parent, propagate_flags: deadline_flags}); - child.on('error', function(err) { + }); + call.cancel(); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.credentials.createInsecure()); + var call = proxy_client.bidiStream(); + call.on('error', function(err) { + done(); + }); + }); + }); + describe('Deadline', function() { + /* jshint bitwise:false */ + var deadline_flags = (grpc.propagate.DEFAULTS & + ~grpc.propagate.CANCELLATION); + it('With a client stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.clientStream = function(parent, callback) { + client.clientStream(function(err, value) { + try { assert(err); assert(err.code === grpc.status.DEADLINE_EXCEEDED || err.code === grpc.status.INTERNAL); + } finally { + callback(err, value); done(); - }); - }; - proxy.addProtoService(test_service, proxy_impl); - var proxy_port = proxy.bind('localhost:0', server_insecure_creds); - proxy.start(); - var proxy_client = new Client('localhost:' + proxy_port, - grpc.Credentials.createInsecure()); - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + 1); - var call = proxy_client.bidiStream(null, {deadline: deadline}); - call.on('error', function(err) { + } + }, null, {parent: parent, propagate_flags: deadline_flags}); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.credentials.createInsecure()); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + proxy_client.clientStream(function(err, value) { + done(); + }, null, {deadline: deadline}); + }); + it('With a bidi stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.bidiStream = function(parent) { + var child = client.bidiStream( + null, {parent: parent, propagate_flags: deadline_flags}); + child.on('error', function(err) { + assert(err); + assert(err.code === grpc.status.DEADLINE_EXCEEDED || + err.code === grpc.status.INTERNAL); done(); }); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.credentials.createInsecure()); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + var call = proxy_client.bidiStream(null, {deadline: deadline}); + call.on('error', function(err) { + done(); }); }); }); @@ -866,7 +1059,7 @@ describe('Cancelling surface client', function() { }); var port = server.bind('localhost:0', server_insecure_creds); var Client = surface_client.makeProtobufClientConstructor(mathService); - client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + client = new Client('localhost:' + port, grpc.credentials.createInsecure()); server.start(); }); after(function() { diff --git a/src/objective-c/.gitignore b/src/objective-c/.gitignore deleted file mode 100644 index 1511066800..0000000000 --- a/src/objective-c/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -# Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate -*.DS_Store
\ No newline at end of file diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h index 2e379a7157..6b443877e9 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h @@ -33,17 +33,19 @@ #import "GRPCCall.h" -// Helpers for setting and reading headers compatible with OAuth2. +/** Helpers for setting and reading headers compatible with OAuth2. */ @interface GRPCCall (OAuth2) -// Setting this property is equivalent to setting "Bearer <passed token>" as the value of the -// request header with key "authorization" (the authorization header). Setting it to nil removes the -// authorization header from the request. -// The value obtained by getting the property is the OAuth2 bearer token if the authorization header -// of the request has the form "Bearer <token>", or nil otherwise. +/** + * Setting this property is equivalent to setting "Bearer <passed token>" as the value of the + * request header with key "authorization" (the authorization header). Setting it to nil removes the + * authorization header from the request. + * The value obtained by getting the property is the OAuth2 bearer token if the authorization header + * of the request has the form "Bearer <token>", or nil otherwise. + */ @property(atomic, copy) NSString *oauth2AccessToken; -// Returns the value (if any) of the "www-authenticate" response header (the challenge header). +/** Returns the value (if any) of the "www-authenticate" response header (the challenge header). */ @property(atomic, readonly) NSString *oauth2ChallengeHeader; @end diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h index cca1614606..ccc5723ec7 100644 --- a/src/objective-c/GRPCClient/GRPCCall+Tests.h +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.h @@ -33,22 +33,28 @@ #import "GRPCCall.h" -// Methods to let tune down the security of gRPC connections for specific hosts. These shouldn't be -// used in releases, but are sometimes needed for testing. +/** + * Methods to let tune down the security of gRPC connections for specific hosts. These shouldn't be + * used in releases, but are sometimes needed for testing. + */ @interface GRPCCall (Tests) -// Establish all SSL connections to the provided host using the passed SSL target name and the root -// certificates found in the file at |certsPath|. -// -// Must be called before any gRPC call to that host is made. It's illegal to pass the same host to -// more than one invocation of the methods of this category. +/** + * Establish all SSL connections to the provided host using the passed SSL target name and the root + * certificates found in the file at |certsPath|. + * + * Must be called before any gRPC call to that host is made. It's illegal to pass the same host to + * more than one invocation of the methods of this category. + */ + (void)useTestCertsPath:(NSString *)certsPath testName:(NSString *)testName forHost:(NSString *)host; -// Establish all connections to the provided host using cleartext instead of SSL. -// -// Must be called before any gRPC call to that host is made. It's illegal to pass the same host to -// more than one invocation of the methods of this category. +/** + * Establish all connections to the provided host using cleartext instead of SSL. + * + * Must be called before any gRPC call to that host is made. It's illegal to pass the same host to + * more than one invocation of the methods of this category. + */ + (void)useInsecureConnectionsForHost:(NSString *)host; @end diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.m b/src/objective-c/GRPCClient/GRPCCall+Tests.m index bade0b2920..c8e8133703 100644 --- a/src/objective-c/GRPCClient/GRPCCall+Tests.m +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.m @@ -40,6 +40,9 @@ + (void)useTestCertsPath:(NSString *)certsPath testName:(NSString *)testName forHost:(NSString *)host { + if (!host || !certsPath || !testName) { + [NSException raise:NSInvalidArgumentException format:@"host, path and name must be provided."]; + } GRPCHost *hostConfig = [GRPCHost hostWithAddress:host]; hostConfig.pathToCertificates = certsPath; hostConfig.hostNameOverride = testName; diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 4eda499b1a..5918f8857a 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -31,77 +31,214 @@ * */ -// The gRPC protocol is an RPC protocol on top of HTTP2. -// -// While the most common type of RPC receives only one request message and returns only one response -// message, the protocol also supports RPCs that return multiple individual messages in a streaming -// fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and -// responses. -// -// Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of -// the "non-streaming type" sending only one message in the corresponding direction (the protocol -// doesn't make any distinction). -// -// Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed -// transparently on the same TCP connection. +/** + * The gRPC protocol is an RPC protocol on top of HTTP2. + * + * While the most common type of RPC receives only one request message and returns only one response + * message, the protocol also supports RPCs that return multiple individual messages in a streaming + * fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and + * responses. + * + * Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of + * the "non-streaming type" sending only one message in the corresponding direction (the protocol + * doesn't make any distinction). + * + * Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed + * transparently on the same TCP connection. + */ #import <Foundation/Foundation.h> #import <RxLibrary/GRXWriter.h> -// Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by -// the server. +#pragma mark gRPC errors + +/** Domain of NSError objects produced by gRPC. */ +extern NSString *const kGRPCErrorDomain; + +/** + * gRPC error codes. + * Note that a few of these are never produced by the gRPC libraries, but are of general utility for + * server applications to produce. + */ +typedef NS_ENUM(NSUInteger, GRPCErrorCode) { + /** The operation was cancelled (typically by the caller). */ + GRPCErrorCodeCancelled = 1, + + /** + * Unknown error. Errors raised by APIs that do not return enough error information may be + * converted to this error. + */ + GRPCErrorCodeUnknown = 2, + + /** + * The client specified an invalid argument. Note that this differs from FAILED_PRECONDITION. + * INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the + * server (e.g., a malformed file name). + */ + GRPCErrorCodeInvalidArgument = 3, + + /** + * Deadline expired before operation could complete. For operations that change the state of the + * server, this error may be returned even if the operation has completed successfully. For + * example, a successful response from the server could have been delayed long enough for the + * deadline to expire. + */ + GRPCErrorCodeDeadlineExceeded = 4, + + /** Some requested entity (e.g., file or directory) was not found. */ + GRPCErrorCodeNotFound = 5, + + /** Some entity that we attempted to create (e.g., file or directory) already exists. */ + GRPCErrorCodeAlreadyExists = 6, + + /** + * The caller does not have permission to execute the specified operation. PERMISSION_DENIED isn't + * used for rejections caused by exhausting some resource (RESOURCE_EXHAUSTED is used instead for + * those errors). PERMISSION_DENIED doesn't indicate a failure to identify the caller + * (UNAUTHENTICATED is used instead for those errors). + */ + GRPCErrorCodePermissionDenied = 7, + + /** + * The request does not have valid authentication credentials for the operation (e.g. the caller's + * identity can't be verified). + */ + GRPCErrorCodeUnauthenticated = 16, + + /** Some resource has been exhausted, perhaps a per-user quota. */ + GRPCErrorCodeResourceExhausted = 8, + + /** + * The RPC was rejected because the server is not in a state required for the procedure's + * execution. For example, a directory to be deleted may be non-empty, etc. + * The client should not retry until the server state has been explicitly fixed (e.g. by + * performing another RPC). The details depend on the service being called, and should be found in + * the NSError's userInfo. + */ + GRPCErrorCodeFailedPrecondition = 9, + + /** + * The RPC was aborted, typically due to a concurrency issue like sequencer check failures, + * transaction aborts, etc. The client should retry at a higher-level (e.g., restarting a read- + * modify-write sequence). + */ + GRPCErrorCodeAborted = 10, + + /** + * The RPC was attempted past the valid range. E.g., enumerating past the end of a list. + * Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system state + * changes. For example, an RPC to get elements of a list will generate INVALID_ARGUMENT if asked + * to return the element at a negative index, but it will generate OUT_OF_RANGE if asked to return + * the element at an index past the current size of the list. + */ + GRPCErrorCodeOutOfRange = 11, + + /** The procedure is not implemented or not supported/enabled in this server. */ + GRPCErrorCodeUnimplemented = 12, + + /** + * Internal error. Means some invariant expected by the server application or the gRPC library has + * been broken. + */ + GRPCErrorCodeInternal = 13, + + /** + * The server is currently unavailable. This is most likely a transient condition and may be + * corrected by retrying with a backoff. + */ + GRPCErrorCodeUnavailable = 14, + + /** Unrecoverable data loss or corruption. */ + GRPCErrorCodeDataLoss = 15, +}; + +/** + * Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by + * the server. + */ extern id const kGRPCHeadersKey; extern id const kGRPCTrailersKey; -// Represents a single gRPC remote call. +#pragma mark GRPCCall + +/** + * The container of the request headers of an RPC conforms to this protocol, which is a subset of + * NSMutableDictionary's interface. It will become a NSMutableDictionary later on. + * The keys of this container are the header names, which per the HTTP standard are case- + * insensitive. They are stored in lowercase (which is how HTTP/2 mandates them on the wire), and + * can only consist of ASCII characters. + * A header value is a NSString object (with only ASCII characters), unless the header name has the + * suffix "-bin", in which case the value has to be a NSData object. + */ +@protocol GRPCRequestHeaders <NSObject> + +@property(nonatomic, readonly) NSUInteger count; + +- (id)objectForKeyedSubscript:(NSString *)key; +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(NSString *)key; + +@end + +/** Represents a single gRPC remote call. */ @interface GRPCCall : GRXWriter -// These HTTP headers will be passed to the server as part of this call. Each HTTP header is a -// name-value pair with string names and either string or binary values. -// -// The passed dictionary has to use NSString keys, corresponding to the header names. The value -// associated to each can be a NSString object or a NSData object. E.g.: -// -// call.requestHeaders = @{@"authorization": @"Bearer ..."}; -// -// call.requestHeaders[@"my-header-bin"] = someData; -// -// After the call is started, trying to modify this property is an error. -// -// For convenience, the property is initialized to an empty NSMutableDictionary, and the setter -// accepts (and copies) both mutable and immutable dictionaries. -- (NSMutableDictionary *)requestHeaders; // nonatomic -- (void)setRequestHeaders:(NSDictionary *)requestHeaders; // nonatomic, copy - -// This dictionary is populated with the HTTP headers received from the server. This happens before -// any response message is received from the server. It has the same structure as the request -// headers dictionary: Keys are NSString header names; names ending with the suffix "-bin" have a -// NSData value; the others have a NSString value. -// -// The value of this property is nil until all response headers are received, and will change before -// any of -writeValue: or -writesFinishedWithError: are sent to the writeable. +/** + * These HTTP headers will be passed to the server as part of this call. Each HTTP header is a + * name-value pair with string names and either string or binary values. + * + * The passed dictionary has to use NSString keys, corresponding to the header names. The value + * associated to each can be a NSString object or a NSData object. E.g.: + * + * call.requestHeaders = @{@"authorization": @"Bearer ..."}; + * + * call.requestHeaders[@"my-header-bin"] = someData; + * + * After the call is started, trying to modify this property is an error. + * + * The property is initialized to an empty NSMutableDictionary. + */ +@property(atomic, readonly) id<GRPCRequestHeaders> requestHeaders; + +/** + * This dictionary is populated with the HTTP headers received from the server. This happens before + * any response message is received from the server. It has the same structure as the request + * headers dictionary: Keys are NSString header names; names ending with the suffix "-bin" have a + * NSData value; the others have a NSString value. + * + * The value of this property is nil until all response headers are received, and will change before + * any of -writeValue: or -writesFinishedWithError: are sent to the writeable. + */ @property(atomic, readonly) NSDictionary *responseHeaders; -// Same as responseHeaders, but populated with the HTTP trailers received from the server before the -// call finishes. -// -// The value of this property is nil until all response trailers are received, and will change -// before -writesFinishedWithError: is sent to the writeable. +/** + * Same as responseHeaders, but populated with the HTTP trailers received from the server before the + * call finishes. + * + * The value of this property is nil until all response trailers are received, and will change + * before -writesFinishedWithError: is sent to the writeable. + */ @property(atomic, readonly) NSDictionary *responseTrailers; -// The request writer has to write NSData objects into the provided Writeable. The server will -// receive each of those separately and in order as distinct messages. -// A gRPC call might not complete until the request writer finishes. On the other hand, the request -// finishing doesn't necessarily make the call to finish, as the server might continue sending -// messages to the response side of the call indefinitely (depending on the semantics of the -// specific remote method called). -// To finish a call right away, invoke cancel. +/** + * The request writer has to write NSData objects into the provided Writeable. The server will + * receive each of those separately and in order as distinct messages. + * A gRPC call might not complete until the request writer finishes. On the other hand, the request + * finishing doesn't necessarily make the call to finish, as the server might continue sending + * messages to the response side of the call indefinitely (depending on the semantics of the + * specific remote method called). + * To finish a call right away, invoke cancel. + */ - (instancetype)initWithHost:(NSString *)host path:(NSString *)path requestsWriter:(GRXWriter *)requestsWriter NS_DESIGNATED_INITIALIZER; -// Finishes the request side of this call, notifies the server that the RPC should be cancelled, and -// finishes the response side of the call with an error of code CANCELED. +/** + * Finishes the request side of this call, notifies the server that the RPC should be cancelled, and + * finishes the response side of the call with an error of code CANCELED. + */ - (void)cancel; // TODO(jcanizales): Let specify a deadline. As a category of GRXWriter? diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index ff5d1c5aaf..b6986bf59c 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -37,6 +37,7 @@ #include <grpc/support/time.h> #import <RxLibrary/GRXConcurrentWriteable.h> +#import "private/GRPCRequestHeaders.h" #import "private/GRPCWrappedCall.h" #import "private/NSData+GRPC.h" #import "private/NSDictionary+GRPC.h" @@ -93,7 +94,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; // the response arrives. GRPCCall *_retainSelf; - NSMutableDictionary *_requestHeaders; + GRPCRequestHeaders *_requestHeaders; } @synthesize state = _state; @@ -124,21 +125,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; _requestWriter = requestWriter; - _requestHeaders = [NSMutableDictionary dictionary]; + _requestHeaders = [[GRPCRequestHeaders alloc] initWithCall:self]; } return self; } -#pragma mark Metadata - -- (NSMutableDictionary *)requestHeaders { - return _requestHeaders; -} - -- (void)setRequestHeaders:(NSDictionary *)requestHeaders { - _requestHeaders = [NSMutableDictionary dictionaryWithDictionary:requestHeaders]; -} - #pragma mark Finish - (void)finishWithError:(NSError *)errorOrNil { @@ -230,10 +221,10 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; #pragma mark Send headers -- (void)sendHeaders:(NSDictionary *)headers { +- (void)sendHeaders:(id<GRPCRequestHeaders>)headers { // TODO(jcanizales): Add error handlers for async failures - [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] - initWithMetadata:headers ?: @{} handler:nil]]]; + [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers + handler:nil]]]; } #pragma mark GRXWriteable implementation diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h index 2a7b701576..e2d19d506a 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -35,12 +35,16 @@ struct grpc_channel; -// Each separate instance of this class represents at least one TCP connection to the provided host. -// Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|. +/** + * Each separate instance of this class represents at least one TCP connection to the provided host. + * Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|. + */ @interface GRPCChannel : NSObject @property(nonatomic, readonly) struct grpc_channel *unmanagedChannel; -// This initializer takes ownership of the passed channel, and will destroy it when this object is -// deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects. +/** + * This initializer takes ownership of the passed channel, and will destroy it when this object is + * deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects. + */ - (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h index ab8d714d22..fe3b8f39d1 100644 --- a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h +++ b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h @@ -36,15 +36,17 @@ typedef void(^GRPCQueueCompletionHandler)(bool success); -// This class lets one more easily use |grpc_completion_queue|. To use it, pass the value of the -// |unmanagedQueue| property of an instance of this class to |grpc_channel_create_call|. Then for -// every |grpc_call_*| method that accepts a tag, you can pass a block of type -// |GRPCQueueCompletionHandler| (remembering to cast it using |__bridge_retained|). The block is -// guaranteed to eventually be called, by a concurrent queue, and then released. Each such block is -// passed a |bool| that tells if the operation was successful. -// -// Release the GRPCCompletionQueue object only after you are not going to pass any more blocks to -// the |grpc_call| that's using it. +/** + * This class lets one more easily use |grpc_completion_queue|. To use it, pass the value of the + * |unmanagedQueue| property of an instance of this class to |grpc_channel_create_call|. Then for + * every |grpc_call_*| method that accepts a tag, you can pass a block of type + * |GRPCQueueCompletionHandler| (remembering to cast it using |__bridge_retained|). The block is + * guaranteed to eventually be called, by a concurrent queue, and then released. Each such block is + * passed a |bool| that tells if the operation was successful. + * + * Release the GRPCCompletionQueue object only after you are not going to pass any more blocks to + * the |grpc_call| that's using it. + */ @interface GRPCCompletionQueue : NSObject @property(nonatomic, readonly) grpc_completion_queue *unmanagedQueue; diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h index f0bbd53023..6b4f98746d 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.h +++ b/src/objective-c/GRPCClient/private/GRPCHost.h @@ -40,18 +40,18 @@ struct grpc_call; @property(nonatomic, readonly) NSString *address; -// The following properties should only be modified for testing: +/** The following properties should only be modified for testing: */ @property(nonatomic, getter=isSecure) BOOL secure; @property(nonatomic, copy) NSString *pathToCertificates; @property(nonatomic, copy) NSString *hostNameOverride; -// Host objects initialized with the same address are the same. +/** Host objects initialized with the same address are the same. */ + (instancetype)hostWithAddress:(NSString *)address; - (instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER; -// Create a grpc_call object to the provided path on this host. +/** Create a grpc_call object to the provided path on this host. */ - (struct grpc_call *)unmanagedCallWithPath:(NSString *)path completionQueue:(GRPCCompletionQueue *)queue; diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h new file mode 100644 index 0000000000..cf5a1be9d6 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import <Foundation/Foundation.h> +#include <grpc/grpc.h> + +#import "../GRPCCall.h" + +@interface GRPCRequestHeaders : NSObject<GRPCRequestHeaders> + +@property(nonatomic, readonly) NSUInteger count; +@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray; + +- (instancetype)initWithCall:(GRPCCall *)call; + +- (id)objectForKeyedSubscript:(NSString *)key; +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(NSString *)key; + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m new file mode 100644 index 0000000000..d23f21c0f9 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -0,0 +1,118 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import "GRPCRequestHeaders.h" + +#import <Foundation/Foundation.h> + +#import "NSDictionary+GRPC.h" + +// Used by the setter. +static void CheckIsNonNilASCII(NSString *name, NSString* value) { + if (!value) { + [NSException raise:NSInvalidArgumentException format:@"%@ cannot be nil", name]; + } + if (![value canBeConvertedToEncoding:NSASCIIStringEncoding]) { + [NSException raise:NSInvalidArgumentException + format:@"%@ %@ contains non-ASCII characters", name, value]; + } +} + +// Precondition: key isn't nil. +static void CheckKeyValuePairIsValid(NSString *key, id value) { + if ([key hasSuffix:@"-bin"]) { + if (![value isKindOfClass:NSData.class]) { + [NSException raise:NSInvalidArgumentException + format:@"Expected NSData value for header %@ ending in \"-bin\", " + @"instead got %@", key, value]; + } + } else { + if (![value isKindOfClass:NSString.class]) { + [NSException raise:NSInvalidArgumentException + format:@"Expected NSString value for header %@ not ending in \"-bin\", " + @"instead got %@", key, value]; + } + CheckIsNonNilASCII(@"Text header value", (NSString *)value); + } +} + +@implementation GRPCRequestHeaders { + __weak GRPCCall *_call; + NSMutableDictionary *_delegate; +} + +- (instancetype)initWithCall:(GRPCCall *)call { + if ((self = [super init])) { + _call = call; + _delegate = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)checkCallIsNotStarted { + if (_call.state != GRXWriterStateNotStarted) { + [NSException raise:@"Invalid modification" + format:@"Cannot modify request headers after call is started"]; + } +} + +- (id)objectForKeyedSubscript:(NSString *)key { + return _delegate[key.lowercaseString]; +} + +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { + [self checkCallIsNotStarted]; + CheckIsNonNilASCII(@"Header name", key); + key = key.lowercaseString; + CheckKeyValuePairIsValid(key, obj); + _delegate[key] = obj; +} + +- (void)removeObjectForKey:(NSString *)key { + [self checkCallIsNotStarted]; + [_delegate removeObjectForKey:key.lowercaseString]; +} + +- (void)removeAllObjects { + [self checkCallIsNotStarted]; + [_delegate removeAllObjects]; +} + +- (NSUInteger)count { + return _delegate.count; +} + +- (grpc_metadata *)grpc_metadataArray { + return _delegate.grpc_metadataArray; +} +@end diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h index 74257eb058..4e0881e5a2 100644 --- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h @@ -40,13 +40,15 @@ struct grpc_credentials; @interface GRPCSecureChannel : GRPCChannel - (instancetype)initWithHost:(NSString *)host; -// Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for -// pathToCertificates results in using the default root certificates distributed with the library. +/** + * Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for + * pathToCertificates results in using the default root certificates distributed with the library. + */ - (instancetype)initWithHost:(NSString *)host pathToCertificates:(NSString *)path hostNameOverride:(NSString *)hostNameOverride; -// The passed arguments aren't required to be valid beyond the invocation of this initializer. +/** The passed arguments aren't required to be valid beyond the invocation of this initializer. */ - (instancetype)initWithHost:(NSString *)host credentials:(struct grpc_credentials *)credentials args:(grpc_channel_args *)args NS_DESIGNATED_INITIALIZER; diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h index da11cbb761..7747aa53ef 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -35,16 +35,17 @@ #include <grpc/grpc.h> #import "GRPCChannel.h" +#import "GRPCRequestHeaders.h" @interface GRPCOperation : NSObject @property(nonatomic, readonly) grpc_op op; -// Guaranteed to be called when the operation has finished. +/** Guaranteed to be called when the operation has finished. */ - (void)finish; @end @interface GRPCOpSendMetadata : GRPCOperation -- (instancetype)initWithMetadata:(NSDictionary *)metadata +- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void(^)())handler NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index fe3d51da53..cea7c479e0 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -65,7 +65,7 @@ return [self initWithMetadata:nil handler:nil]; } -- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)())handler { +- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler { if (self = [super init]) { _op.op = GRPC_OP_SEND_INITIAL_METADATA; _op.data.send_initial_metadata.count = metadata.count; diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m index 99c890e4ee..7477da7619 100644 --- a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m +++ b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m @@ -40,8 +40,8 @@ @interface NSData (GRPCMetadata) + (instancetype)grpc_dataFromMetadataValue:(grpc_metadata *)metadata; -// Fill a metadata object with the binary value in this NSData and the given key. -- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key; +// Fill a metadata object with the binary value in this NSData. +- (void)grpc_initMetadata:(grpc_metadata *)metadata; @end @implementation NSData (GRPCMetadata) @@ -50,9 +50,7 @@ return [self dataWithBytes:metadata->value length:metadata->value_length]; } -- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key { - // TODO(jcanizales): Encode Unicode chars as ASCII. - metadata->key = [key stringByAppendingString:@"-bin"].UTF8String; +- (void)grpc_initMetadata:(grpc_metadata *)metadata { metadata->value = self.bytes; metadata->value_length = self.length; } @@ -63,8 +61,8 @@ @interface NSString (GRPCMetadata) + (instancetype)grpc_stringFromMetadataValue:(grpc_metadata *)metadata; -// Fill a metadata object with the textual value in this NSString and the given key. -- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key; +// Fill a metadata object with the textual value in this NSString. +- (void)grpc_initMetadata:(grpc_metadata *)metadata; @end @implementation NSString (GRPCMetadata) @@ -74,22 +72,8 @@ encoding:NSASCIIStringEncoding]; } -- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key { - if ([key hasSuffix:@"-bin"]) { - // Disallow this, as at best it will confuse the server. If the app really needs to send a - // textual header with a name ending in "-bin", it can be done by removing the suffix and - // encoding the NSString as a NSData object. - // - // Why raise an exception: In the most common case, the developer knows this won't happen in - // their code, so the exception isn't triggered. In the rare cases when the developer can't - // tell, it's easy enough to add a sanitizing filter before the header is set. There, the - // developer can choose whether to drop such a header, or trim its name. Doing either ourselves, - // silently, would be very unintuitive for the user. - [NSException raise:NSInvalidArgumentException - format:@"Metadata keys ending in '-bin' are reserved for NSData values."]; - } - // TODO(jcanizales): Encode Unicode chars as ASCII. - metadata->key = key.UTF8String; +// Precondition: This object contains only ASCII characters. +- (void)grpc_initMetadata:(grpc_metadata *)metadata { metadata->value = self.UTF8String; metadata->value_length = self.length; } @@ -105,8 +89,6 @@ + (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count { NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithCapacity:count]; for (grpc_metadata *entry = entries; entry < entries + count; entry++) { - // TODO(jcanizales): Verify in a C library test that it's converting header names to lower case - // automatically. NSString *name = [NSString stringWithCString:entry->key encoding:NSASCIIStringEncoding]; if (!name || metadata[name]) { // Log if name is nil? @@ -114,7 +96,6 @@ } id value; if ([name hasSuffix:@"-bin"]) { - name = [name substringToIndex:name.length - 4]; value = [NSData grpc_dataFromMetadataValue:entry]; } else { value = [NSString grpc_stringFromMetadataValue:entry]; @@ -124,19 +105,21 @@ return metadata; } +// Preconditions: All keys are ASCII strings. Keys ending in -bin have NSData values; the others +// have NSString values. - (grpc_metadata *)grpc_metadataArray { grpc_metadata *metadata = gpr_malloc([self count] * sizeof(grpc_metadata)); - int i = 0; - for (id key in self) { + grpc_metadata *current = metadata; + for (NSString* key in self) { id value = self[key]; - grpc_metadata *current = &metadata[i]; - if ([value respondsToSelector:@selector(grpc_initMetadata:withKey:)]) { - [value grpc_initMetadata:current withKey:key]; + current->key = key.UTF8String; + if ([value respondsToSelector:@selector(grpc_initMetadata:)]) { + [value grpc_initMetadata:current]; } else { [NSException raise:NSInvalidArgumentException format:@"Metadata values must be NSString or NSData."]; } - i += 1; + ++current; } return metadata; } diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.h b/src/objective-c/GRPCClient/private/NSError+GRPC.h index e712791271..e0c1efc1f9 100644 --- a/src/objective-c/GRPCClient/private/NSError+GRPC.h +++ b/src/objective-c/GRPCClient/private/NSError+GRPC.h @@ -34,31 +34,10 @@ #import <Foundation/Foundation.h> #include <grpc/grpc.h> -// TODO(jcanizales): Make the domain string public. -extern NSString *const kGRPCErrorDomain; - -// TODO(jcanizales): Make this public and document each code. -typedef NS_ENUM(NSInteger, GRPCErrorCode) { - GRPCErrorCodeCancelled = 1, - GRPCErrorCodeUnknown = 2, - GRPCErrorCodeInvalidArgument = 3, - GRPCErrorCodeDeadlineExceeded = 4, - GRPCErrorCodeNotFound = 5, - GRPCErrorCodeAlreadyExists = 6, - GRPCErrorCodePermissionDenied = 7, - GRPCErrorCodeUnauthenticated = 16, - GRPCErrorCodeResourceExhausted = 8, - GRPCErrorCodeFailedPrecondition = 9, - GRPCErrorCodeAborted = 10, - GRPCErrorCodeOutOfRange = 11, - GRPCErrorCodeUnimplemented = 12, - GRPCErrorCodeInternal = 13, - GRPCErrorCodeUnavailable = 14, - GRPCErrorCodeDataLoss = 15 -}; - @interface NSError (GRPC) -// Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode| -// and whose domain is |kGRPCErrorDomain|. +/** + * Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode| + * and whose domain is |kGRPCErrorDomain|. + */ + (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(char *)details; @end diff --git a/src/objective-c/ProtoRPC/ProtoMethod.h b/src/objective-c/ProtoRPC/ProtoMethod.h index 8f554a0483..a0ed2cf98a 100644 --- a/src/objective-c/ProtoRPC/ProtoMethod.h +++ b/src/objective-c/ProtoRPC/ProtoMethod.h @@ -33,8 +33,10 @@ #import <Foundation/Foundation.h> -// A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint -// can implement multiple services. +/** + * A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint + * can implement multiple services. + */ @interface ProtoMethod : NSObject @property(nonatomic, readonly) NSString *package; @property(nonatomic, readonly) NSString *service; diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m index 889d71a308..9bf66f347a 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.m +++ b/src/objective-c/ProtoRPC/ProtoRPC.m @@ -37,6 +37,22 @@ #import <RxLibrary/GRXWriteable.h> #import <RxLibrary/GRXWriter+Transformations.h> +static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) { + NSDictionary *info = @{ + NSLocalizedDescriptionKey: @"Unable to parse response from the server", + NSLocalizedRecoverySuggestionErrorKey: @"If this RPC is idempotent, retry " + @"with exponential backoff. Otherwise, query the server status before " + @"retrying.", + NSUnderlyingErrorKey: parsingError, + @"Expected class": expectedClass, + @"Received value": proto, + }; + // TODO(jcanizales): Use kGRPCErrorDomain and GRPCErrorCodeInternal when they're public. + return [NSError errorWithDomain:@"io.grpc" + code:13 + userInfo:info]; +} + @implementation ProtoRPC { id<GRXWriteable> _responseWriteable; } @@ -65,14 +81,25 @@ } // A writer that serializes the proto messages to send. GRXWriter *bytesWriter = [requestsWriter map:^id(GPBMessage *proto) { - // TODO(jcanizales): Fail with an understandable error message if the requestsWriter isn't - // sending GPBMessages. + if (![proto isKindOfClass:GPBMessage.class]) { + [NSException raise:NSInvalidArgumentException + format:@"Request must be a proto message: %@", proto]; + } return [proto data]; }]; if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) { + __weak ProtoRPC *weakSelf = self; + // A writeable that parses the proto messages received. _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { - [responsesWriteable writeValue:[responseClass parseFromData:value error:NULL]]; + // TODO(jcanizales): This is done in the main thread, and needs to happen in another thread. + NSError *error = nil; + id parsed = [responseClass parseFromData:value error:&error]; + if (parsed) { + [responsesWriteable writeValue:parsed]; + } else { + [weakSelf finishWithError:ErrorForBadProto(value, responseClass, error)]; + } } completionHandler:^(NSError *errorOrNil) { [responsesWriteable writesFinishedWithError:errorOrNil]; }]; diff --git a/src/objective-c/README.md b/src/objective-c/README.md index 6c27657def..c1d25b96f5 100644 --- a/src/objective-c/README.md +++ b/src/objective-c/README.md @@ -1,3 +1,4 @@ +[![Cocoapods](https://img.shields.io/cocoapods/v/gRPC.svg)](https://cocoapods.org/pods/gRPC) # gRPC for Objective-C - [Install protoc with the gRPC plugin](#install) @@ -17,7 +18,7 @@ services. <a name="install"></a> ## Install protoc with the gRPC plugin -On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][]. +On Mac OS X, install [homebrew][]. Run the following command to install _protoc_ and the gRPC _protoc_ plugin: ```sh @@ -153,7 +154,7 @@ _protoc_, in which case no system modification nor renaming is necessary. <a name="no-cocoapods"></a> ### Integrate the generated gRPC library without using Cocoapods -You need to compile the generated `.pbpbjc.*` files (the enums and messages) without ARC support, +You need to compile the generated `.pbobjc.*` files (the enums and messages) without ARC support, and the generated `.pbrpc.*` files (the services) with ARC support. The generated code depends on v0.5+ of the Objective-C gRPC runtime library and v3.0.0-alpha-3+ of the Objective-C Protobuf runtime library. @@ -168,7 +169,6 @@ Objective-C Protobuf runtime library. [Protocol Buffers]:https://developers.google.com/protocol-buffers/ [homebrew]:http://brew.sh -[linuxbrew]:https://github.com/Homebrew/linuxbrew [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install [example Podfile]:https://github.com/grpc/grpc/blob/master/src/objective-c/examples/Sample/Podfile [sample app]: https://github.com/grpc/grpc/tree/master/src/objective-c/examples/Sample diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.h b/src/objective-c/RxLibrary/GRXBufferedPipe.h index ca94ce275f..03b0359278 100644 --- a/src/objective-c/RxLibrary/GRXBufferedPipe.h +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.h @@ -36,25 +36,27 @@ #import "GRXWriteable.h" #import "GRXWriter.h" -// A buffered pipe is a Writer that also acts as a Writeable. -// Once it is started, whatever values are written into it (via -writeValue:) will be propagated -// immediately, unless flow control prevents it. -// If it is throttled and keeps receiving values, as well as if it receives values before being -// started, it will buffer them and propagate them in order as soon as its state becomes Started. -// If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and -// propagate the error immediately. -// -// Beware that a pipe of this type can't prevent receiving more values when it is paused (for -// example if used to write data to a congested network connection). Because in such situations the -// pipe will keep buffering all data written to it, your application could run out of memory and -// crash. If you want to react to flow control signals to prevent that, instead of using this class -// you can implement an object that conforms to GRXWriter. -// -// Thread-safety: -// The methods of an object of this class should not be called concurrently from different threads. +/** + * A buffered pipe is a Writer that also acts as a Writeable. + * Once it is started, whatever values are written into it (via -writeValue:) will be propagated + * immediately, unless flow control prevents it. + * If it is throttled and keeps receiving values, as well as if it receives values before being + * started, it will buffer them and propagate them in order as soon as its state becomes Started. + * If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and + * propagate the error immediately. + * + * Beware that a pipe of this type can't prevent receiving more values when it is paused (for + * example if used to write data to a congested network connection). Because in such situations the + * pipe will keep buffering all data written to it, your application could run out of memory and + * crash. If you want to react to flow control signals to prevent that, instead of using this class + * you can implement an object that conforms to GRXWriter. + * + * Thread-safety: + * The methods of an object of this class should not be called concurrently from different threads. + */ @interface GRXBufferedPipe : GRXWriter<GRXWriteable> -// Convenience constructor. +/** Convenience constructor. */ + (instancetype)pipe; @end diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h index 1080001905..b2775f98b5 100644 --- a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h @@ -36,36 +36,48 @@ #import "GRXWriter.h" #import "GRXWriteable.h" -// This is a thread-safe wrapper over a GRXWriteable instance. It lets one enqueue calls to a -// GRXWriteable instance for the main thread, guaranteeing that writesFinishedWithError: is the last -// message sent to it (no matter what messages are sent to the wrapper, in what order, nor from -// which thread). It also guarantees that, if cancelWithError: is called from the main thread (e.g. -// by the app cancelling the writes), no further messages are sent to the writeable except -// writesFinishedWithError:. -// -// TODO(jcanizales): Let the user specify another queue for the writeable callbacks. +/** + * This is a thread-safe wrapper over a GRXWriteable instance. It lets one enqueue calls to a + * GRXWriteable instance for the main thread, guaranteeing that writesFinishedWithError: is the last + * message sent to it (no matter what messages are sent to the wrapper, in what order, nor from + * which thread). It also guarantees that, if cancelWithError: is called from the main thread (e.g. + * by the app cancelling the writes), no further messages are sent to the writeable except + * writesFinishedWithError:. + * + * TODO(jcanizales): Let the user specify another queue for the writeable callbacks. + */ @interface GRXConcurrentWriteable : NSObject -// The GRXWriteable passed is the wrapped writeable. -// The GRXWriteable instance is retained until writesFinishedWithError: is sent to it, and released -// after that. +/** + * The GRXWriteable passed is the wrapped writeable. + * The GRXWriteable instance is retained until writesFinishedWithError: is sent to it, and released + * after that. + */ - (instancetype)initWithWriteable:(id<GRXWriteable>)writeable NS_DESIGNATED_INITIALIZER; -// Enqueues writeValue: to be sent to the writeable in the main thread. -// The passed handler is invoked from the main thread after writeValue: returns. +/** + * Enqueues writeValue: to be sent to the writeable in the main thread. + * The passed handler is invoked from the main thread after writeValue: returns. + */ - (void)enqueueValue:(id)value completionHandler:(void (^)())handler; -// Enqueues writesFinishedWithError:nil to be sent to the writeable in the main thread. After that -// message is sent to the writeable, all other methods of this object are effectively noops. +/** + * Enqueues writesFinishedWithError:nil to be sent to the writeable in the main thread. After that + * message is sent to the writeable, all other methods of this object are effectively noops. + */ - (void)enqueueSuccessfulCompletion; -// If the writeable has not yet received a writesFinishedWithError: message, this will enqueue one -// to be sent to it in the main thread, and cancel all other pending messages to the writeable -// enqueued by this object (both past and future). -// The error argument cannot be nil. +/** + * If the writeable has not yet received a writesFinishedWithError: message, this will enqueue one + * to be sent to it in the main thread, and cancel all other pending messages to the writeable + * enqueued by this object (both past and future). + * The error argument cannot be nil. + */ - (void)cancelWithError:(NSError *)error; -// Cancels all pending messages to the writeable enqueued by this object (both past and future). -// Because the writeable won't receive writesFinishedWithError:, this also releases the writeable. +/** + * Cancels all pending messages to the writeable enqueued by this object (both past and future). + * Because the writeable won't receive writesFinishedWithError:, this also releases the writeable. + */ - (void)cancelSilently; @end diff --git a/src/objective-c/RxLibrary/GRXForwardingWriter.h b/src/objective-c/RxLibrary/GRXForwardingWriter.h index f310832284..8d45b8ed8d 100644 --- a/src/objective-c/RxLibrary/GRXForwardingWriter.h +++ b/src/objective-c/RxLibrary/GRXForwardingWriter.h @@ -33,17 +33,19 @@ #import "GRXWriter.h" -// A "proxy" class that simply forwards values, completion, and errors from its input writer to its -// writeable. -// It is useful as a superclass for pipes that act as a transformation of their -// input writer, and for classes that represent objects with input and -// output sequences of values, like an RPC. -// -// Thread-safety: -// All messages sent to this object need to be serialized. When it is started, the writer it wraps -// is started in the same thread. Manual state changes are propagated to the wrapped writer in the -// same thread too. Importantly, all messages the wrapped writer sends to its writeable need to be -// serialized with any message sent to this object. +/** + * A "proxy" class that simply forwards values, completion, and errors from its input writer to its + * writeable. + * It is useful as a superclass for pipes that act as a transformation of their + * input writer, and for classes that represent objects with input and + * output sequences of values, like an RPC. + * + * Thread-safety: + * All messages sent to this object need to be serialized. When it is started, the writer it wraps + * is started in the same thread. Manual state changes are propagated to the wrapped writer in the + * same thread too. Importantly, all messages the wrapped writer sends to its writeable need to be + * serialized with any message sent to this object. + */ @interface GRXForwardingWriter : GRXWriter - (instancetype)initWithWriter:(GRXWriter *)writer NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.h b/src/objective-c/RxLibrary/GRXImmediateWriter.h index 3fcc259434..e22b056ff5 100644 --- a/src/objective-c/RxLibrary/GRXImmediateWriter.h +++ b/src/objective-c/RxLibrary/GRXImmediateWriter.h @@ -35,46 +35,60 @@ #import "GRXWriter.h" -// Utility to construct GRXWriter instances from values that are immediately available when -// required. -// -// Thread-safety: -// -// An object of this class shouldn't be messaged concurrently by more than one thread. It will start -// messaging the writeable before |startWithWriteable:| returns, in the same thread. That is the -// only place where the writer can be paused or stopped prematurely. -// -// If a paused writer of this class is resumed, it will start messaging the writeable, in the same -// thread, before |setState:| returns. Because the object can't be legally accessed concurrently, -// that's the only place where it can be paused again (or stopped). +/** + * Utility to construct GRXWriter instances from values that are immediately available when + * required. + * + * Thread-safety: + * + * An object of this class shouldn't be messaged concurrently by more than one thread. It will start + * messaging the writeable before |startWithWriteable:| returns, in the same thread. That is the + * only place where the writer can be paused or stopped prematurely. + * + * If a paused writer of this class is resumed, it will start messaging the writeable, in the same + * thread, before |setState:| returns. Because the object can't be legally accessed concurrently, + * that's the only place where it can be paused again (or stopped). + */ @interface GRXImmediateWriter : GRXWriter -// Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to -// its writeable. The NSEnumerator is released when it finishes. +/** + * Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to + * its writeable. The NSEnumerator is released when it finishes. + */ + (GRXWriter *)writerWithEnumerator:(NSEnumerator *)enumerator; -// Returns a writer that pushes to its writeable the successive values returned by the passed -// block. When the block first returns nil, it is released. +/** + * Returns a writer that pushes to its writeable the successive values returned by the passed + * block. When the block first returns nil, it is released. + */ + (GRXWriter *)writerWithValueSupplier:(id (^)())block; -// Returns a writer that iterates over the values of the passed container and pushes them to -// its writeable. The container is released when the iteration is over. -// -// Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to -// call one method per element. Because GRXWriteable instances accept values one by one, that speed -// gain doesn't happen here. +/** + * Returns a writer that iterates over the values of the passed container and pushes them to + * its writeable. The container is released when the iteration is over. + * + * Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to + * call one method per element. Because GRXWriteable instances accept values one by one, that speed + * gain doesn't happen here. + */ + (GRXWriter *)writerWithContainer:(id<NSFastEnumeration>)container; -// Returns a writer that sends the passed value to its writeable and then finishes (releasing the -// value). +/** + * Returns a writer that sends the passed value to its writeable and then finishes (releasing the + * value). + */ + (GRXWriter *)writerWithValue:(id)value; -// Returns a writer that, as part of its start method, sends the passed error to the writeable -// (then releasing the error). +/** + * Returns a writer that, as part of its start method, sends the passed error to the writeable + * (then releasing the error). + */ + (GRXWriter *)writerWithError:(NSError *)error; -// Returns a writer that, as part of its start method, finishes immediately without sending any -// values to its writeable. +/** + * Returns a writer that, as part of its start method, finishes immediately without sending any + * values to its writeable. + */ + (GRXWriter *)emptyWriter; @end diff --git a/src/objective-c/RxLibrary/GRXWriteable.h b/src/objective-c/RxLibrary/GRXWriteable.h index 45613d6dd0..7fe805c663 100644 --- a/src/objective-c/RxLibrary/GRXWriteable.h +++ b/src/objective-c/RxLibrary/GRXWriteable.h @@ -33,16 +33,20 @@ #import <Foundation/Foundation.h> -// A GRXWriteable is an object to which a sequence of values can be sent. The -// sequence finishes with an optional error. +/** + * A GRXWriteable is an object to which a sequence of values can be sent. The + * sequence finishes with an optional error. + */ @protocol GRXWriteable <NSObject> -// Push the next value of the sequence to the receiving object. +/** Push the next value of the sequence to the receiving object. */ - (void)writeValue:(id)value; -// Signal that the sequence is completed, or that an error ocurred. After this -// message is sent to the instance, neither it nor writeValue: may be -// called again. +/** + * Signal that the sequence is completed, or that an error ocurred. After this + * message is sent to the instance, neither it nor writeValue: may be + * called again. + */ - (void)writesFinishedWithError:(NSError *)errorOrNil; @end @@ -51,8 +55,10 @@ typedef void (^GRXCompletionHandler)(NSError *errorOrNil); typedef void (^GRXSingleHandler)(id value, NSError *errorOrNil); typedef void (^GRXEventHandler)(BOOL done, id value, NSError *error); -// Utility to create objects that conform to the GRXWriteable protocol, from -// blocks that handle each of the two methods of the protocol. +/** + * Utility to create objects that conform to the GRXWriteable protocol, from + * blocks that handle each of the two methods of the protocol. + */ @interface GRXWriteable : NSObject<GRXWriteable> + (instancetype)writeableWithSingleHandler:(GRXSingleHandler)handler; diff --git a/src/objective-c/RxLibrary/GRXWriter+Immediate.h b/src/objective-c/RxLibrary/GRXWriter+Immediate.h index b75c0a5a64..be880151f4 100644 --- a/src/objective-c/RxLibrary/GRXWriter+Immediate.h +++ b/src/objective-c/RxLibrary/GRXWriter+Immediate.h @@ -35,32 +35,44 @@ @interface GRXWriter (Immediate) -// Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to -// its writeable. The NSEnumerator is released when it finishes. +/** + * Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to + * its writeable. The NSEnumerator is released when it finishes. + */ + (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator; -// Returns a writer that pushes to its writeable the successive values returned by the passed -// block. When the block first returns nil, it is released. +/** + * Returns a writer that pushes to its writeable the successive values returned by the passed + * block. When the block first returns nil, it is released. + */ + (instancetype)writerWithValueSupplier:(id (^)())block; -// Returns a writer that iterates over the values of the passed container and pushes them to -// its writeable. The container is released when the iteration is over. -// -// Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to -// call one method per element. Because GRXWriteable instances accept values one by one, that speed -// gain doesn't happen here. +/** + * Returns a writer that iterates over the values of the passed container and pushes them to + * its writeable. The container is released when the iteration is over. + * + * Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to + * call one method per element. Because GRXWriteable instances accept values one by one, that speed + * gain doesn't happen here. + */ + (instancetype)writerWithContainer:(id<NSFastEnumeration>)container; -// Returns a writer that sends the passed value to its writeable and then finishes (releasing the -// value). +/** + * Returns a writer that sends the passed value to its writeable and then finishes (releasing the + * value). + */ + (instancetype)writerWithValue:(id)value; -// Returns a writer that, as part of its start method, sends the passed error to the writeable -// (then releasing the error). +/** + * Returns a writer that, as part of its start method, sends the passed error to the writeable + * (then releasing the error). + */ + (instancetype)writerWithError:(NSError *)error; -// Returns a writer that, as part of its start method, finishes immediately without sending any -// values to its writeable. +/** + * Returns a writer that, as part of its start method, finishes immediately without sending any + * values to its writeable. + */ + (instancetype)emptyWriter; @end diff --git a/src/objective-c/RxLibrary/GRXWriter+Transformations.h b/src/objective-c/RxLibrary/GRXWriter+Transformations.h index 60c4da37d6..17d61e7541 100644 --- a/src/objective-c/RxLibrary/GRXWriter+Transformations.h +++ b/src/objective-c/RxLibrary/GRXWriter+Transformations.h @@ -35,8 +35,10 @@ @interface GRXWriter (Transformations) -// Returns a writer that wraps the receiver, and has all the values the receiver would write -// transformed by the provided mapping function. +/** + * Returns a writer that wraps the receiver, and has all the values the receiver would write + * transformed by the provided mapping function. + */ - (GRXWriter *)map:(id (^)(id value))map; @end diff --git a/src/objective-c/RxLibrary/GRXWriter.h b/src/objective-c/RxLibrary/GRXWriter.h index b1c994aa38..ff81268446 100644 --- a/src/objective-c/RxLibrary/GRXWriter.h +++ b/src/objective-c/RxLibrary/GRXWriter.h @@ -35,73 +35,87 @@ #import "GRXWriteable.h" -// States of a writer. +/** States of a writer. */ typedef NS_ENUM(NSInteger, GRXWriterState) { - // The writer has not yet been given a writeable to which it can push its values. To have a writer - // transition to the Started state, send it a startWithWriteable: message. - // - // A writer's state cannot be manually set to this value. + /** + * The writer has not yet been given a writeable to which it can push its values. To have a writer + * transition to the Started state, send it a startWithWriteable: message. + * + * A writer's state cannot be manually set to this value. + */ GRXWriterStateNotStarted, - // The writer might push values to the writeable at any moment. + /** The writer might push values to the writeable at any moment. */ GRXWriterStateStarted, - // The writer is temporarily paused, and won't send any more values to the writeable unless its - // state is set back to Started. The writer might still transition to the Finished state at any - // moment, and is allowed to send writesFinishedWithError: to its writeable. + /** + * The writer is temporarily paused, and won't send any more values to the writeable unless its + * state is set back to Started. The writer might still transition to the Finished state at any + * moment, and is allowed to send writesFinishedWithError: to its writeable. + */ GRXWriterStatePaused, - // The writer has released its writeable and won't interact with it anymore. - // - // One seldomly wants to set a writer's state to this value, as its writeable isn't notified with - // a writesFinishedWithError: message. Instead, sending finishWithError: to the writer will make - // it notify the writeable and then transition to this state. + /** + * The writer has released its writeable and won't interact with it anymore. + * + * One seldomly wants to set a writer's state to this value, as its writeable isn't notified with + * a writesFinishedWithError: message. Instead, sending finishWithError: to the writer will make + * it notify the writeable and then transition to this state. + */ GRXWriterStateFinished }; -// An GRXWriter object can produce, on demand, a sequence of values. The sequence may be produced -// asynchronously, and it may consist of any number of elements, including none or an infinite -// number. -// -// GRXWriter is the active dual of NSEnumerator. The difference between them is thus whether the -// object plays an active or passive role during usage: A user of NSEnumerator pulls values off it, -// and passes the values to a writeable. A user of GRXWriter, though, just gives it a writeable, and -// the GRXWriter instance pushes values to the writeable. This makes this protocol suitable to -// represent a sequence of future values, as well as collections with internal iteration. -// -// An instance of GRXWriter can start producing values after a writeable is passed to it. It can -// also be commanded to finish the sequence immediately (with an optional error). Finally, it can be -// asked to pause, and resumed later. All GRXWriter objects support pausing and early termination. -// -// Thread-safety: -// -// State transitions take immediate effect if the object is used from a single thread. Subclasses -// might offer stronger guarantees. -// -// Unless otherwise indicated by a conforming subclass, no messages should be sent concurrently to a -// GRXWriter. I.e., conforming classes aren't required to be thread-safe. +/** + * An GRXWriter object can produce, on demand, a sequence of values. The sequence may be produced + * asynchronously, and it may consist of any number of elements, including none or an infinite + * number. + * + * GRXWriter is the active dual of NSEnumerator. The difference between them is thus whether the + * object plays an active or passive role during usage: A user of NSEnumerator pulls values off it, + * and passes the values to a writeable. A user of GRXWriter, though, just gives it a writeable, and + * the GRXWriter instance pushes values to the writeable. This makes this protocol suitable to + * represent a sequence of future values, as well as collections with internal iteration. + * + * An instance of GRXWriter can start producing values after a writeable is passed to it. It can + * also be commanded to finish the sequence immediately (with an optional error). Finally, it can be + * asked to pause, and resumed later. All GRXWriter objects support pausing and early termination. + * + * Thread-safety: + * + * State transitions take immediate effect if the object is used from a single thread. Subclasses + * might offer stronger guarantees. + * + * Unless otherwise indicated by a conforming subclass, no messages should be sent concurrently to a + * GRXWriter. I.e., conforming classes aren't required to be thread-safe. + */ @interface GRXWriter : NSObject -// This property can be used to query the current state of the writer, which determines how it might -// currently use its writeable. Some state transitions can be triggered by setting this property to -// the corresponding value, and that's useful for advanced use cases like pausing an writer. For -// more details, see the documentation of the enum further down. +/** + * This property can be used to query the current state of the writer, which determines how it might + * currently use its writeable. Some state transitions can be triggered by setting this property to + * the corresponding value, and that's useful for advanced use cases like pausing an writer. For + * more details, see the documentation of the enum further down. + */ @property(nonatomic) GRXWriterState state; -// Transition to the Started state, and start sending messages to the writeable (a reference to it -// is retained). Messages to the writeable may be sent before the method returns, or they may be -// sent later in the future. See GRXWriteable.h for the different messages a writeable can receive. -// -// If this writer draws its values from an external source (e.g. from the filesystem or from a -// server), calling this method will commonly trigger side effects (like network connections). -// -// This method might only be called on writers in the NotStarted state. +/** + * Transition to the Started state, and start sending messages to the writeable (a reference to it + * is retained). Messages to the writeable may be sent before the method returns, or they may be + * sent later in the future. See GRXWriteable.h for the different messages a writeable can receive. + * + * If this writer draws its values from an external source (e.g. from the filesystem or from a + * server), calling this method will commonly trigger side effects (like network connections). + * + * This method might only be called on writers in the NotStarted state. + */ - (void)startWithWriteable:(id<GRXWriteable>)writeable; -// Send writesFinishedWithError:errorOrNil to the writeable. Then release the reference to it and -// transition to the Finished state. -// -// This method might only be called on writers in the Started or Paused state. +/** + * Send writesFinishedWithError:errorOrNil to the writeable. Then release the reference to it and + * transition to the Finished state. + * + * This method might only be called on writers in the Started or Paused state. + */ - (void)finishWithError:(NSError *)errorOrNil; @end diff --git a/src/objective-c/RxLibrary/GRXWriter.m b/src/objective-c/RxLibrary/GRXWriter.m index 019fcbd785..fee33f5556 100644 --- a/src/objective-c/RxLibrary/GRXWriter.m +++ b/src/objective-c/RxLibrary/GRXWriter.m @@ -35,4 +35,14 @@ @implementation GRXWriter +- (void)startWithWriteable:(id<GRXWriteable>)writeable { + NSAssert(NO, @"Missing base implementation for %@", NSStringFromSelector(_cmd)); + [self doesNotRecognizeSelector:_cmd]; +} + +- (void)finishWithError:(NSError *)errorOrNil { + NSAssert(NO, @"Missing base implementation for %@", NSStringFromSelector(_cmd)); + [self doesNotRecognizeSelector:_cmd]; +} + @end diff --git a/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h b/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h index 400e834c6a..0622f7067c 100644 --- a/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h +++ b/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h @@ -35,17 +35,23 @@ @interface NSEnumerator (GRXUtil) -// Returns a NSEnumerator instance that iterates through the elements of the passed container that -// supports fast enumeration. Note that this negates the speed benefits of fast enumeration over -// NSEnumerator. It's only intended for the rare cases when one needs the latter and only has the -// former, e.g. for iteration that needs to be paused and resumed later. +/** + * Returns a NSEnumerator instance that iterates through the elements of the passed container that + * supports fast enumeration. Note that this negates the speed benefits of fast enumeration over + * NSEnumerator. It's only intended for the rare cases when one needs the latter and only has the + * former, e.g. for iteration that needs to be paused and resumed later. + */ + (NSEnumerator *)grx_enumeratorWithContainer:(id<NSFastEnumeration>)container; -// Returns a NSEnumerator instance that provides a single object before finishing. The value is then -// released. +/** + * Returns a NSEnumerator instance that provides a single object before finishing. The value is then + * released. + */ + (NSEnumerator *)grx_enumeratorWithSingleValue:(id)value; -// Returns a NSEnumerator instance that delegates the invocations of nextObject to the passed block. -// When the block first returns nil, it is released. +/** + * Returns a NSEnumerator instance that delegates the invocations of nextObject to the passed block. + * When the block first returns nil, it is released. + */ + (NSEnumerator *)grx_enumeratorWithValueSupplier:(id (^)())block; @end diff --git a/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h index 34cfc4d8a7..fb50c37863 100644 --- a/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h +++ b/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h @@ -33,10 +33,14 @@ #import <Foundation/Foundation.h> -// Concrete subclass of NSEnumerator that delegates the invocations of nextObject to a block passed -// on initialization. +/** + * Concrete subclass of NSEnumerator that delegates the invocations of nextObject to a block passed + * on initialization. + */ @interface GRXNSBlockEnumerator : NSEnumerator -// The first time the passed block returns nil, the enumeration will end and the block will be -// released. +/** + * The first time the passed block returns nil, the enumeration will end and the block will be + * released. + */ - (instancetype)initWithValueSupplier:(id (^)())block; @end diff --git a/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h index 1565029205..62c27dbc7f 100644 --- a/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h +++ b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h @@ -33,11 +33,15 @@ #import <Foundation/Foundation.h> -// This is a bridge to interact through NSEnumerator's interface with objects that only conform to -// NSFastEnumeration. (There's nothing specifically fast about it - you certainly don't win any -// speed by using this instead of a NSEnumerator provided by your container). +/** + * This is a bridge to interact through NSEnumerator's interface with objects that only conform to + * NSFastEnumeration. (There's nothing specifically fast about it - you certainly don't win any + * speed by using this instead of a NSEnumerator provided by your container). + */ @interface GRXNSFastEnumerator : NSEnumerator -// After the iteration of the container (via the NSFastEnumeration protocol) is over, the container -// is released. If the container is modified during enumeration, an exception is thrown. +/** + * After the iteration of the container (via the NSFastEnumeration protocol) is over, the container + * is released. If the container is modified during enumeration, an exception is thrown. + */ - (instancetype)initWithContainer:(id<NSFastEnumeration>)container; @end diff --git a/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h index 12aa51e213..24a21a1b22 100644 --- a/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h +++ b/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h @@ -33,9 +33,11 @@ #import <Foundation/Foundation.h> -// Concrete subclass of NSEnumerator whose instances return a single object before finishing. +/** Concrete subclass of NSEnumerator whose instances return a single object before finishing. */ @interface GRXNSScalarEnumerator : NSEnumerator -// Param value: the single object this instance will produce. After the first invocation of -// nextObject, the value is released. +/** + * Param value: the single object this instance will produce. After the first invocation of + * nextObject, the value is released. + */ - (instancetype)initWithValue:(id)value; @end diff --git a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h index 43b8706864..01a15e2a43 100644 --- a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h +++ b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h @@ -33,7 +33,7 @@ #import "RxLibrary/GRXForwardingWriter.h" -// A "proxy" writer that transforms all the values of its input writer by using a mapping function. +/** A "proxy" writer that transforms all the values of its input writer by using a mapping function. */ @interface GRXMappingWriter : GRXForwardingWriter - (instancetype)initWithWriter:(GRXWriter *)writer map:(id (^)(id value))map NS_DESIGNATED_INITIALIZER; diff --git a/src/objective-c/change-comments.py b/src/objective-c/change-comments.py new file mode 100755 index 0000000000..9aa0e0c9f5 --- /dev/null +++ b/src/objective-c/change-comments.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python2.7 +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Change comments style of source files from // to /** */""" + +import re +import sys + + +if len(sys.argv) < 2: + print("Please provide at least one source file name as argument.") + sys.exit() + +for file_name in sys.argv[1:]: + + print("Modifying format of {file} comments in place...".format( + file=file_name, + )) + + + # Input + + with open(file_name, "r") as input_file: + lines = input_file.readlines() + + def peek(): + return lines[0] + + def read_line(): + return lines.pop(0) + + def more_input_available(): + return lines + + + # Output + + output_lines = [] + + def write(line): + output_lines.append(line) + + def flush_output(): + with open(file_name, "w") as output_file: + for line in output_lines: + output_file.write(line) + + + # Pattern matching + + comment_regex = r'^(\s*)//\s(.*)$' + + def is_comment(line): + return re.search(comment_regex, line) + + def isnt_comment(line): + return not is_comment(line) + + def next_line(predicate): + return more_input_available() and predicate(peek()) + + + # Transformation + + def indentation_of(line): + match = re.search(comment_regex, line) + return match.group(1) + + def content(line): + match = re.search(comment_regex, line) + return match.group(2) + + def format_as_block(comment_block): + if len(comment_block) == 0: + return [] + + indent = indentation_of(comment_block[0]) + + if len(comment_block) == 1: + return [indent + "/** " + content(comment_block[0]) + " */\n"] + + block = ["/**"] + [" * " + content(line) for line in comment_block] + [" */"] + return [indent + line.rstrip() + "\n" for line in block] + + + # Main algorithm + + while more_input_available(): + while next_line(isnt_comment): + write(read_line()) + + comment_block = [] + # Get all lines in the same comment block. We could restrict the indentation + # to be the same as the first line of the block, but it's probably ok. + while (next_line(is_comment)): + comment_block.append(read_line()) + + for line in format_as_block(comment_block): + write(line) + + flush_output() diff --git a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec new file mode 100644 index 0000000000..d4f8084cb5 --- /dev/null +++ b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec @@ -0,0 +1,28 @@ +Pod::Spec.new do |s| + s.name = "RemoteTest" + s.version = "0.0.1" + s.license = "New BSD" + + s.ios.deployment_target = "6.0" + s.osx.deployment_target = "10.8" + + # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. + s.prepare_command = <<-CMD + protoc --objc_out=. --objcgrpc_out=. *.proto + CMD + + s.subspec "Messages" do |ms| + ms.source_files = "*.pbobjc.{h,m}" + ms.header_mappings_dir = "." + ms.requires_arc = false + ms.dependency "Protobuf", "~> 3.0.0-alpha-4" + end + + s.subspec "Services" do |ss| + ss.source_files = "*.pbrpc.{h,m}" + ss.header_mappings_dir = "." + ss.requires_arc = true + ss.dependency "gRPC", "~> 0.7" + ss.dependency "#{s.name}/Messages" + end +end diff --git a/src/node/interop/empty.proto b/src/objective-c/examples/RemoteTestClient/empty.proto index 6d0eb937d6..a678048289 100644 --- a/src/node/interop/empty.proto +++ b/src/objective-c/examples/RemoteTestClient/empty.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -32,6 +31,8 @@ syntax = "proto3"; package grpc.testing; +option objc_class_prefix = "RMT"; + // An empty message that you can re-use to avoid defining duplicated empty // messages in your project. A typical example is to use it as argument or the // return value of a service API. For instance: diff --git a/src/csharp/Grpc.IntegrationTesting/proto/messages.proto b/src/objective-c/examples/RemoteTestClient/messages.proto index 7df85e3c13..85d93c2ff9 100644 --- a/src/csharp/Grpc.IntegrationTesting/proto/messages.proto +++ b/src/objective-c/examples/RemoteTestClient/messages.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -34,6 +33,8 @@ syntax = "proto3"; package grpc.testing; +option objc_class_prefix = "RMT"; + // The type of payload that should be returned. enum PayloadType { // Compressable text format. diff --git a/src/csharp/Grpc.IntegrationTesting/proto/test.proto b/src/objective-c/examples/RemoteTestClient/test.proto index f9e0d2a039..514c3b8095 100644 --- a/src/csharp/Grpc.IntegrationTesting/proto/test.proto +++ b/src/objective-c/examples/RemoteTestClient/test.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -37,6 +36,8 @@ import "messages.proto"; package grpc.testing; +option objc_class_prefix = "RMT"; + // A simple service to test the various types of RPCs and experiment with // performance with various types of payload. service TestService { @@ -44,7 +45,6 @@ service TestService { rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); // One request followed by one response. - // TODO(Issue 527): Describe required server behavior. rpc UnaryCall(SimpleRequest) returns (SimpleResponse); // One request followed by a sequence of responses (streamed download). diff --git a/src/objective-c/examples/SwiftSample/AppDelegate.swift b/src/objective-c/examples/SwiftSample/AppDelegate.swift new file mode 100644 index 0000000000..88027d997b --- /dev/null +++ b/src/objective-c/examples/SwiftSample/AppDelegate.swift @@ -0,0 +1,39 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? +} diff --git a/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard b/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..3a2a49bad8 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> + <dependencies> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/> + </dependencies> + <scenes> + <!--View Controller--> + <scene sceneID="tne-QT-ifu"> + <objects> + <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController"> + <layoutGuides> + <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> + <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> + </layoutGuides> + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> + <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> + </objects> + </scene> + </scenes> +</document> diff --git a/src/objective-c/examples/SwiftSample/Bridging-Header.h b/src/objective-c/examples/SwiftSample/Bridging-Header.h new file mode 100644 index 0000000000..65f768a760 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Bridging-Header.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef SwiftSample_Bridging_Header_h +#define SwiftSample_Bridging_Header_h + +#import <RxLibrary/GRXWriteable.h> +#import <RxLibrary/GRXWriter.h> +#import <RxLibrary/GRXWriter+Immediate.h> +#import <GRPCClient/GRPCCall.h> +#import <ProtoRPC/ProtoMethod.h> +#import <ProtoRPC/ProtoRPC.h> +#import <RemoteTest/Test.pbrpc.h> + +#endif diff --git a/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json b/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..36d2c80d88 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/src/objective-c/examples/SwiftSample/Info.plist b/src/objective-c/examples/SwiftSample/Info.plist new file mode 100644 index 0000000000..10f0450b34 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Info.plist @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>io.grpc.$(PRODUCT_NAME:rfc1034identifier)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>UILaunchStoryboardName</key> + <string>Main</string> + <key>UIMainStoryboardFile</key> + <string>Main</string> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>armv7</string> + </array> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UISupportedInterfaceOrientations~ipad</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> +</dict> +</plist> diff --git a/src/objective-c/examples/SwiftSample/Podfile b/src/objective-c/examples/SwiftSample/Podfile new file mode 100644 index 0000000000..3611b00863 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Podfile @@ -0,0 +1,9 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +pod 'Protobuf', :path => "../../../../third_party/protobuf" +pod 'gRPC', :path => "../../../.." +pod 'RemoteTest', :path => "../RemoteTestClient" + +target 'SwiftSample' do +end diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..cfccdd453f --- /dev/null +++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj @@ -0,0 +1,354 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 253D3A297105CA46DA960A11 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC58ACA18DCCB1553531B885 /* libPods.a */; }; + 633BFFC81B950B210007E424 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633BFFC71B950B210007E424 /* AppDelegate.swift */; }; + 633BFFCA1B950B210007E424 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633BFFC91B950B210007E424 /* ViewController.swift */; }; + 633BFFCD1B950B210007E424 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 633BFFCB1B950B210007E424 /* Main.storyboard */; }; + 633BFFCF1B950B210007E424 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 633BFFCE1B950B210007E424 /* Images.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; }; + 633BFFC21B950B210007E424 /* SwiftSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 633BFFC61B950B210007E424 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 633BFFC71B950B210007E424 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; + 633BFFC91B950B210007E424 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; }; + 633BFFCC1B950B210007E424 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; + 633BFFCE1B950B210007E424 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; + 6367AD231B951655007FD3A4 /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; }; + C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; }; + DC58ACA18DCCB1553531B885 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 633BFFBF1B950B210007E424 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 253D3A297105CA46DA960A11 /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 31F283C976AE97586C17CCD9 /* Pods */ = { + isa = PBXGroup; + children = ( + 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */, + C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = "<group>"; + }; + 633BFFB91B950B210007E424 = { + isa = PBXGroup; + children = ( + 633BFFC41B950B210007E424 /* SwiftSample */, + 633BFFC31B950B210007E424 /* Products */, + 31F283C976AE97586C17CCD9 /* Pods */, + 9D63A7F6423989BA306810CA /* Frameworks */, + ); + sourceTree = "<group>"; + }; + 633BFFC31B950B210007E424 /* Products */ = { + isa = PBXGroup; + children = ( + 633BFFC21B950B210007E424 /* SwiftSample.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + 633BFFC41B950B210007E424 /* SwiftSample */ = { + isa = PBXGroup; + children = ( + 633BFFC71B950B210007E424 /* AppDelegate.swift */, + 633BFFC91B950B210007E424 /* ViewController.swift */, + 633BFFCB1B950B210007E424 /* Main.storyboard */, + 633BFFCE1B950B210007E424 /* Images.xcassets */, + 633BFFC51B950B210007E424 /* Supporting Files */, + 6367AD231B951655007FD3A4 /* Bridging-Header.h */, + ); + name = SwiftSample; + sourceTree = SOURCE_ROOT; + }; + 633BFFC51B950B210007E424 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 633BFFC61B950B210007E424 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = "<group>"; + }; + 9D63A7F6423989BA306810CA /* Frameworks */ = { + isa = PBXGroup; + children = ( + DC58ACA18DCCB1553531B885 /* libPods.a */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 633BFFC11B950B210007E424 /* SwiftSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 633BFFE11B950B210007E424 /* Build configuration list for PBXNativeTarget "SwiftSample" */; + buildPhases = ( + 6BEEB33CA2705D7D2F2210E6 /* Check Pods Manifest.lock */, + 633BFFBE1B950B210007E424 /* Sources */, + 633BFFBF1B950B210007E424 /* Frameworks */, + 633BFFC01B950B210007E424 /* Resources */, + AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftSample; + productName = SwiftSample; + productReference = 633BFFC21B950B210007E424 /* SwiftSample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 633BFFBA1B950B210007E424 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = gRPC; + TargetAttributes = { + 633BFFC11B950B210007E424 = { + CreatedOnToolsVersion = 6.4; + }; + }; + }; + buildConfigurationList = 633BFFBD1B950B210007E424 /* Build configuration list for PBXProject "SwiftSample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 633BFFB91B950B210007E424; + productRefGroup = 633BFFC31B950B210007E424 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 633BFFC11B950B210007E424 /* SwiftSample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 633BFFC01B950B210007E424 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 633BFFCD1B950B210007E424 /* Main.storyboard in Resources */, + 633BFFCF1B950B210007E424 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 6BEEB33CA2705D7D2F2210E6 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 633BFFBE1B950B210007E424 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 633BFFCA1B950B210007E424 /* ViewController.swift in Sources */, + 633BFFC81B950B210007E424 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 633BFFCB1B950B210007E424 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 633BFFCC1B950B210007E424 /* Base */, + ); + name = Main.storyboard; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 633BFFDF1B950B210007E424 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 633BFFE01B950B210007E424 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 633BFFE21B950B210007E424 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h"; + USER_HEADER_SEARCH_PATHS = "Pods/**"; + }; + name = Debug; + }; + 633BFFE31B950B210007E424 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h"; + USER_HEADER_SEARCH_PATHS = "Pods/**"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 633BFFBD1B950B210007E424 /* Build configuration list for PBXProject "SwiftSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 633BFFDF1B950B210007E424 /* Debug */, + 633BFFE01B950B210007E424 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 633BFFE11B950B210007E424 /* Build configuration list for PBXNativeTarget "SwiftSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 633BFFE21B950B210007E424 /* Debug */, + 633BFFE31B950B210007E424 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 633BFFBA1B950B210007E424 /* Project object */; +} diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..3b0f1c15b2 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:SwiftSample.xcodeproj"> + </FileRef> +</Workspace> diff --git a/src/objective-c/examples/SwiftSample/ViewController.swift b/src/objective-c/examples/SwiftSample/ViewController.swift new file mode 100644 index 0000000000..76dad9e132 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/ViewController.swift @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + let RemoteHost = "grpc-test.sandbox.google.com" + + let request = RMTSimpleRequest() + request.responseSize = 10 + request.fillUsername = true + request.fillOauthScope = true + + + // Example gRPC call using a generated proto client library: + + let service = RMTTestService(host: RemoteHost) + service.unaryCallWithRequest(request) { response, error in + if let response = response { + NSLog("1. Finished successfully with response:\n\(response)") + } else { + NSLog("1. Finished with error: \(error!)") + } + } + + + // Same but manipulating headers: + + var RPC : ProtoRPC! // Needed to convince Swift to capture by reference (__block) + RPC = service.RPCToUnaryCallWithRequest(request) { response, error in + if let response = response { + NSLog("2. Finished successfully with response:\n\(response)") + } else { + NSLog("2. Finished with error: \(error!)") + } + NSLog("2. Response headers: \(RPC.responseHeaders)") + NSLog("2. Response trailers: \(RPC.responseTrailers)") + } + + RPC.requestHeaders["My-Header"] = "My value" + + RPC.start() + + + // Same example call using the generic gRPC client library: + + let method = ProtoMethod(package: "grpc.testing", service: "TestService", method: "UnaryCall") + + let requestsWriter = GRXWriter(value: request.data()) + + let call = GRPCCall(host: RemoteHost, path: method.HTTPPath, requestsWriter: requestsWriter) + + call.requestHeaders["My-Header"] = "My value" + + call.startWithWriteable(GRXWriteable { response, error in + if let response = response as? NSData { + NSLog("3. Received response:\n\(RMTSimpleResponse(data: response, error: nil))") + } else { + NSLog("3. Finished with error: \(error!)") + } + NSLog("3. Response headers: \(call.responseHeaders)") + NSLog("3. Response trailers: \(call.responseTrailers)") + }) + } +} diff --git a/src/python/grpcio_test/grpc_test/framework/base/__init__.py b/src/objective-c/format-all-comments.sh index 7086519106..e6b6b5a0b4 100644 --- a/src/python/grpcio_test/grpc_test/framework/base/__init__.py +++ b/src/objective-c/format-all-comments.sh @@ -1,3 +1,4 @@ +#!/bin/bash # Copyright 2015, Google Inc. # All rights reserved. # @@ -27,4 +28,4 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +find . -type f -name "*.h" ! -path "*/Pods/*" ! -path "./generated_libraries/*" ! -path "./examples/*" ! -path "./tests/*" | xargs ./change-comments.py diff --git a/src/objective-c/generated_libraries/RemoteTestClient/test.proto b/src/objective-c/generated_libraries/RemoteTestClient/test.proto index 2f5a5489b3..514c3b8095 100644 --- a/src/objective-c/generated_libraries/RemoteTestClient/test.proto +++ b/src/objective-c/generated_libraries/RemoteTestClient/test.proto @@ -45,7 +45,6 @@ service TestService { rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); // One request followed by one response. - // TODO(Issue 527): Describe required server behavior. rpc UnaryCall(SimpleRequest) returns (SimpleResponse); // One request followed by a sequence of responses (streamed download). diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m index 09a55e0704..00c4b8830d 100644 --- a/src/objective-c/tests/GRPCClientTests.m +++ b/src/objective-c/tests/GRPCClientTests.m @@ -42,9 +42,6 @@ #import <RxLibrary/GRXWriteable.h> #import <RxLibrary/GRXWriter+Immediate.h> -// These are a few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) -// rather than a generated proto library on top of it. - static NSString * const kHostAddress = @"localhost:5050"; static NSString * const kPackage = @"grpc.testing"; static NSString * const kService = @"TestService"; @@ -53,11 +50,10 @@ static ProtoMethod *kInexistentMethod; static ProtoMethod *kEmptyCallMethod; static ProtoMethod *kUnaryCallMethod; -// This is an observer class for testing that responseMetadata is KVO-compliant - +/** Observer class for testing that responseMetadata is KVO-compliant */ @interface PassthroughObserver : NSObject - -- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback; +- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback + NS_DESIGNATED_INITIALIZER; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; @@ -67,23 +63,38 @@ static ProtoMethod *kUnaryCallMethod; void (^_callback)(NSString*, id, NSDictionary*); } +- (instancetype)init { + return [self initWithCallback:nil]; +} + - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback { - self = [super init]; - if (self) { + if (!callback) { + return nil; + } + if ((self = [super init])) { _callback = callback; } return self; - } -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { _callback(keyPath, object, change); [object removeObserver:self forKeyPath:keyPath]; } @end +# pragma mark Tests + +/** + * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than + * a generated proto library on top of it. Its RPCs are sent to a local cleartext server. + * + * TODO(jcanizales): Run them also against a local SSL server and against a remote server. + */ @interface GRPCClientTests : XCTestCase @end @@ -180,6 +191,7 @@ static ProtoMethod *kUnaryCallMethod; [self waitForExpectationsWithTimeout:8 handler:nil]; } +// TODO(jcanizales): Activate this test against the remote server. - (void)testMetadata { __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."]; diff --git a/src/objective-c/tests/InteropTests.h b/src/objective-c/tests/InteropTests.h index 1045c3d124..6d54343b13 100644 --- a/src/objective-c/tests/InteropTests.h +++ b/src/objective-c/tests/InteropTests.h @@ -33,11 +33,17 @@ #import <XCTest/XCTest.h> -// Implements tests as described here: -// https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md - +/** + * Implements tests as described here: + * https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md + * + * This is an abstract class that needs to be subclassed. See |+host|. + */ @interface InteropTests : XCTestCase -// Returns @"grpc-test.sandbox.google.com". -// Override in a subclass to perform the same tests against a different address. +/** + * Host to send the RPCs to. The base implementation returns nil, which would make all tests to + * fail. + * Override in a subclass to perform these tests against a specific address. + */ + (NSString *)host; @end diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m index 1b63fe2059..26877b1ae8 100644 --- a/src/objective-c/tests/InteropTests.m +++ b/src/objective-c/tests/InteropTests.m @@ -78,21 +78,20 @@ #pragma mark Tests -static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; - @implementation InteropTests { RMTTestService *_service; } + (NSString *)host { - return kRemoteSSLHost; + return nil; } - (void)setUp { - _service = [[RMTTestService alloc] initWithHost:self.class.host]; + _service = self.class.host ? [RMTTestService serviceWithHost:self.class.host] : nil; } - (void)testEmptyUnaryRPC { + XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyUnary"]; RMTEmpty *request = [RMTEmpty message]; @@ -110,6 +109,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; } - (void)testLargeUnaryRPC { + XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"]; RMTSimpleRequest *request = [RMTSimpleRequest message]; @@ -132,6 +132,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; } - (void)testClientStreamingRPC { + XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ClientStreaming"]; RMTStreamingInputCallRequest *request1 = [RMTStreamingInputCallRequest message]; @@ -164,6 +165,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; } - (void)testServerStreamingRPC { + XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ServerStreaming"]; NSArray *expectedSizes = @[@31415, @9, @2653, @58979]; @@ -200,6 +202,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; } - (void)testPingPongRPC { + XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; NSArray *requests = @[@27182, @8, @1828, @45904]; @@ -243,6 +246,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; } - (void)testEmptyStreamRPC { + XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyStream"]; [_service fullDuplexCallWithRequestsWriter:[GRXWriter emptyWriter] eventHandler:^(BOOL done, @@ -256,6 +260,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; } - (void)testCancelAfterBeginRPC { + XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterBegin"]; // A buffered pipe to which we never write any value acts as a writer that just hangs. @@ -273,18 +278,19 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; } - (void)testCancelAfterFirstResponseRPC { + XCTAssertNotNil(self.class.host); __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterFirstResponse"]; - + // A buffered pipe to which we write a single value but never close GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; - + __block BOOL receivedResponse = NO; - + id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@21782 requestedResponseSize:@31415]; - + [requestsBuffer writeValue:request]; - + __block ProtoRPC *call = [_service RPCToFullDuplexCallWithRequestsWriter:requestsBuffer eventHandler:^(BOOL done, diff --git a/src/objective-c/tests/InteropTestsLocalCleartext.m b/src/objective-c/tests/InteropTestsLocalCleartext.m index 2d7d3c4b2c..56927a8af6 100644 --- a/src/objective-c/tests/InteropTestsLocalCleartext.m +++ b/src/objective-c/tests/InteropTestsLocalCleartext.m @@ -31,15 +31,13 @@ * */ -// Repeat of the tests in InteropTests.m, but sending the RPCs to a local cleartext server instead -// of the remote SSL one. - #import <GRPCClient/GRPCCall+Tests.h> #import "InteropTests.h" static NSString * const kLocalCleartextHost = @"localhost:5050"; +/** Tests in InteropTests.m, sending the RPCs to a local cleartext server. */ @interface InteropTestsLocalCleartext : InteropTests @end diff --git a/src/objective-c/tests/InteropTestsLocalSSL.m b/src/objective-c/tests/InteropTestsLocalSSL.m index f69f806dcf..9d7afefbfe 100644 --- a/src/objective-c/tests/InteropTestsLocalSSL.m +++ b/src/objective-c/tests/InteropTestsLocalSSL.m @@ -31,15 +31,13 @@ * */ -// Repeat of the tests in InteropTests.m, but sending the RPCs to a local SSL server instead of the -// remote one. - #import <GRPCClient/GRPCCall+Tests.h> #import "InteropTests.h" static NSString * const kLocalSSLHost = @"localhost:5051"; +/** Tests in InteropTests.m, sending the RPCs to a local SSL server. */ @interface InteropTestsLocalSSL : InteropTests @end diff --git a/src/objective-c/tests/InteropTestsRemote.m b/src/objective-c/tests/InteropTestsRemote.m new file mode 100644 index 0000000000..a67be98431 --- /dev/null +++ b/src/objective-c/tests/InteropTestsRemote.m @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import <GRPCClient/GRPCCall+Tests.h> + +#import "InteropTests.h" + +static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; + +/** Tests in InteropTests.m, sending the RPCs to a remote SSL server. */ +@interface InteropTestsRemote : InteropTests +@end + +@implementation InteropTestsRemote + ++ (NSString *)host { + return kRemoteSSLHost; +} + +@end diff --git a/src/objective-c/tests/LocalClearTextTests.m b/src/objective-c/tests/LocalClearTextTests.m deleted file mode 100644 index 976fff55bc..0000000000 --- a/src/objective-c/tests/LocalClearTextTests.m +++ /dev/null @@ -1,164 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#import <UIKit/UIKit.h> -#import <XCTest/XCTest.h> - -#import <GRPCClient/GRPCCall.h> -#import <ProtoRPC/ProtoMethod.h> -#import <RouteGuide/RouteGuide.pbobjc.h> -#import <RouteGuide/RouteGuide.pbrpc.h> -#import <RxLibrary/GRXWriteable.h> -#import <RxLibrary/GRXWriter+Immediate.h> - -// These tests require a gRPC "RouteGuide" sample server to be running locally. You can compile and -// run one by following the instructions here: https://github.com/grpc/grpc/blob/master/examples/cpp/cpptutorial.md#try-it-out -// Be sure to have the C gRPC library installed in your system (for example, by having followed the -// instructions at https://github.com/grpc/homebrew-grpc - -static NSString * const kRouteGuideHost = @"http://localhost:50051"; -static NSString * const kPackage = @"routeguide"; -static NSString * const kService = @"RouteGuide"; - -@interface LocalClearTextTests : XCTestCase -@end - -@implementation LocalClearTextTests - -// This test currently fails: see Issue #1907. -//- (void)testConnectionToLocalServer { -// __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."]; -// -// // This method isn't implemented by the local server. -// GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage -// interface:kService -// method:@"EmptyCall"]; -// -// GRXWriter *requestsWriter = [GRXWriter writerWithValue:[NSData data]]; -// -// GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost -// method:method -// requestsWriter:requestsWriter]; -// -// id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { -// XCTFail(@"Received unexpected response: %@", value); -// } completionHandler:^(NSError *errorOrNil) { -// XCTAssertNotNil(errorOrNil, @"Finished without error!"); -// XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil); -// [expectation fulfill]; -// }]; -// -// [call startWithWriteable:responsesWriteable]; -// -// [self waitForExpectationsWithTimeout:8.0 handler:nil]; -//} - -- (void)testEmptyRPC { - __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."]; - __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; - - ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage - service:kService - method:@"RecordRoute"]; - - GRXWriter *requestsWriter = [GRXWriter emptyWriter]; - - GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost - path:method.HTTPPath - requestsWriter:requestsWriter]; - - id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { - XCTAssertNotNil(value, @"nil value received as response."); - XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value); - [response fulfill]; - } completionHandler:^(NSError *errorOrNil) { - XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); - [completion fulfill]; - }]; - - [call startWithWriteable:responsesWriteable]; - - [self waitForExpectationsWithTimeout:2.0 handler:nil]; -} - -- (void)testSimpleProtoRPC { - __weak XCTestExpectation *response = [self expectationWithDescription:@"Response received."]; - __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; - - ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage - service:kService - method:@"GetFeature"]; - - RGDPoint *point = [RGDPoint message]; - point.latitude = 28E7; - point.longitude = -15E7; - GRXWriter *requestsWriter = [GRXWriter writerWithValue:[point data]]; - - GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost - path:method.HTTPPath - requestsWriter:requestsWriter]; - - id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { - XCTAssertNotNil(value, @"nil value received as response."); - RGDFeature *feature = [RGDFeature parseFromData:value error:NULL]; - XCTAssertEqualObjects(point, feature.location); - XCTAssertNotNil(feature.name, @"Response's name is nil."); - [response fulfill]; - } completionHandler:^(NSError *errorOrNil) { - XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); - [completion fulfill]; - }]; - - [call startWithWriteable:responsesWriteable]; - - [self waitForExpectationsWithTimeout:2.0 handler:nil]; -} - -- (void)testSimpleProtoRPCUsingGeneratedService { - __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; - - RGDPoint *point = [RGDPoint message]; - point.latitude = 28E7; - point.longitude = -15E7; - - RGDRouteGuide *service = [[RGDRouteGuide alloc] initWithHost:kRouteGuideHost]; - [service getFeatureWithRequest:point handler:^(RGDFeature *response, NSError *error) { - XCTAssertNil(error, @"Finished with unexpected error: %@", error); - XCTAssertEqualObjects(point, response.location); - XCTAssertNotNil(response.name, @"Response's name is nil."); - [completion fulfill]; - }]; - - [self waitForExpectationsWithTimeout:2.0 handler:nil]; -} -@end diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index 2aa837f764..2a9b894cf6 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -6,10 +6,26 @@ pod 'gRPC', :path => "../../.." pod 'RemoteTest', :path => "../generated_libraries/RemoteTestClient" pod 'RouteGuide', :path => "../generated_libraries/RouteGuideClient" -link_with 'AllTests' +link_with 'AllTests', + 'RxLibraryUnitTests', + 'InteropTests', + 'InteropTestsLocalSSL', + 'InteropTestsLocalCleartext' target 'Tests' do end target 'AllTests' do end + +target 'RxLibraryUnitTests' do +end + +target 'InteropTestsRemote' do +end + +target 'InteropTestsLocalSSL' do +end + +target 'InteropTestsLocalCleartext' do +end diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 3a1c3d940a..b0429617c0 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -7,16 +7,33 @@ objects = { /* Begin PBXBuildFile section */ + 036D953EE34B1FD523647ACD /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; }; + 08A8BB02D19A53D902B214B8 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; }; + 50267643BA114A2A724D4FDF /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; }; 6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; }; - 63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */; }; 63423F4A1B150A5F006CF63C /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; - 63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; }; 635697CD1B14FC11007A7283 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635697CC1B14FC11007A7283 /* Tests.m */; }; 635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; }; 63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; }; + 6379CC4D1BE1662A001BC0A1 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; }; + 6379CC4E1BE1662B001BC0A1 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; }; + 6379CC501BE16703001BC0A1 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; settings = {ASSET_TAGS = (); }; }; + 6379CC511BE1683B001BC0A1 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; settings = {ASSET_TAGS = (); }; }; + 6379CC531BE17709001BC0A1 /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; settings = {ASSET_TAGS = (); }; }; + 63DC84181BE15179000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; }; + 63DC841E1BE15180000708E8 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; settings = {ASSET_TAGS = (); }; }; + 63DC84281BE15267000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; }; + 63DC842E1BE15278000708E8 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; settings = {ASSET_TAGS = (); }; }; + 63DC842F1BE1527D000708E8 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; }; + 63DC84391BE15294000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; }; + 63DC84481BE152B5000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; }; + 63DC844E1BE15350000708E8 /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; settings = {ASSET_TAGS = (); }; }; + 63DC844F1BE15353000708E8 /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; settings = {ASSET_TAGS = (); }; }; + 63DC84501BE153AA000708E8 /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; settings = {ASSET_TAGS = (); }; }; 63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; }; 63E240D01B6C63DC005F3B0E /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; }; 7D8A186224D39101F90230F6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; }; + DCFAE001609CCBFE69DFA6A1 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -27,6 +44,34 @@ remoteGlobalIDString = 635697C61B14FC11007A7283; remoteInfo = Tests; }; + 63DC84191BE15179000708E8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 635697BF1B14FC11007A7283 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 635697C61B14FC11007A7283; + remoteInfo = Tests; + }; + 63DC84291BE15267000708E8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 635697BF1B14FC11007A7283 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 635697C61B14FC11007A7283; + remoteInfo = Tests; + }; + 63DC843A1BE15294000708E8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 635697BF1B14FC11007A7283 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 635697C61B14FC11007A7283; + remoteInfo = Tests; + }; + 63DC84491BE152B5000708E8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 635697BF1B14FC11007A7283 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 635697C61B14FC11007A7283; + remoteInfo = Tests; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -45,7 +90,6 @@ 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; }; 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = "<group>"; }; - 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalClearTextTests.m; sourceTree = "<group>"; }; 63423F441B150A5F006CF63C /* AllTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AllTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLibraryUnitTests.m; sourceTree = "<group>"; }; 635697C71B14FC11007A7283 /* libTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTests.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -53,6 +97,11 @@ 635697D81B14FC11007A7283 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTests.m; sourceTree = "<group>"; }; 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalCleartext.m; sourceTree = "<group>"; }; + 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsRemote.m; sourceTree = "<group>"; }; + 63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxLibraryUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsRemote.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsLocalSSL.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsLocalCleartext.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63E240CC1B6C4D3A005F3B0E /* InteropTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteropTests.h; sourceTree = "<group>"; }; 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalSSL.m; sourceTree = "<group>"; }; 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TestCertificates.bundle; sourceTree = "<group>"; }; @@ -76,6 +125,42 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 63DC84101BE15179000708E8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 63DC84181BE15179000708E8 /* libTests.a in Frameworks */, + 036D953EE34B1FD523647ACD /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC84201BE15267000708E8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 63DC84281BE15267000708E8 /* libTests.a in Frameworks */, + DCFAE001609CCBFE69DFA6A1 /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC84311BE15294000708E8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 63DC84391BE15294000708E8 /* libTests.a in Frameworks */, + 08A8BB02D19A53D902B214B8 /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC84401BE152B5000708E8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 63DC84481BE152B5000708E8 /* libTests.a in Frameworks */, + 50267643BA114A2A724D4FDF /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -112,6 +197,10 @@ children = ( 635697C71B14FC11007A7283 /* libTests.a */, 63423F441B150A5F006CF63C /* AllTests.xctest */, + 63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */, + 63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */, + 63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */, + 63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */, ); name = Products; sourceTree = "<group>"; @@ -122,10 +211,10 @@ 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */, 63E240CC1B6C4D3A005F3B0E /* InteropTests.h */, 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */, + 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */, 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */, 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */, 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */, - 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */, 635697CC1B14FC11007A7283 /* Tests.m */, 635697D71B14FC11007A7283 /* Supporting Files */, ); @@ -152,6 +241,7 @@ 63423F411B150A5F006CF63C /* Frameworks */, 63423F421B150A5F006CF63C /* Resources */, A441F71824DCB9D0CA297748 /* Copy Pods Resources */, + 5F14F59509E10C2852014F9E /* Embed Pods Frameworks */, ); buildRules = ( ); @@ -180,6 +270,90 @@ productReference = 635697C71B14FC11007A7283 /* libTests.a */; productType = "com.apple.product-type.library.static"; }; + 63DC84121BE15179000708E8 /* RxLibraryUnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */; + buildPhases = ( + B2986CEEE8CDD4901C97598B /* Check Pods Manifest.lock */, + 63DC840F1BE15179000708E8 /* Sources */, + 63DC84101BE15179000708E8 /* Frameworks */, + 63DC84111BE15179000708E8 /* Resources */, + 4F5690DC0E6AD6663FE78B8B /* Embed Pods Frameworks */, + C977426A8727267BBAC7D48E /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 63DC841A1BE15179000708E8 /* PBXTargetDependency */, + ); + name = RxLibraryUnitTests; + productName = RxLibraryUnitTests; + productReference = 63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 63DC84221BE15267000708E8 /* InteropTestsRemote */ = { + isa = PBXNativeTarget; + buildConfigurationList = 63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */; + buildPhases = ( + 4C406327D3907A5E5FBA8AC9 /* Check Pods Manifest.lock */, + 63DC841F1BE15267000708E8 /* Sources */, + 63DC84201BE15267000708E8 /* Frameworks */, + 63DC84211BE15267000708E8 /* Resources */, + 900B6EDD4D16BE7D765C3885 /* Embed Pods Frameworks */, + C2E09DC4BD239F71160F0CC1 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 63DC842A1BE15267000708E8 /* PBXTargetDependency */, + ); + name = InteropTestsRemote; + productName = InteropTests; + productReference = 63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 63DC84331BE15294000708E8 /* InteropTestsLocalSSL */ = { + isa = PBXNativeTarget; + buildConfigurationList = 63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */; + buildPhases = ( + 5C20DCCB71C3991E6FE78C22 /* Check Pods Manifest.lock */, + 63DC84301BE15294000708E8 /* Sources */, + 63DC84311BE15294000708E8 /* Frameworks */, + 63DC84321BE15294000708E8 /* Resources */, + C591129ACE9F6CC5EE03FCDE /* Embed Pods Frameworks */, + 693DD0B453431D64EA24FD66 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 63DC843B1BE15294000708E8 /* PBXTargetDependency */, + ); + name = InteropTestsLocalSSL; + productName = InteropTestsLocalSSL; + productReference = 63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */ = { + isa = PBXNativeTarget; + buildConfigurationList = 63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */; + buildPhases = ( + 7418AC7B3844B29E48D24FC7 /* Check Pods Manifest.lock */, + 63DC843F1BE152B5000708E8 /* Sources */, + 63DC84401BE152B5000708E8 /* Frameworks */, + 63DC84411BE152B5000708E8 /* Resources */, + A8E3AC66DF770B774114A30E /* Embed Pods Frameworks */, + 8AD3130D3C58A0FB32FF2A36 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 63DC844A1BE152B5000708E8 /* PBXTargetDependency */, + ); + name = InteropTestsLocalCleartext; + productName = InteropTestsLocalCleartext; + productReference = 63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -195,6 +369,18 @@ 635697C61B14FC11007A7283 = { CreatedOnToolsVersion = 6.3.1; }; + 63DC84121BE15179000708E8 = { + CreatedOnToolsVersion = 7.0.1; + }; + 63DC84221BE15267000708E8 = { + CreatedOnToolsVersion = 7.0.1; + }; + 63DC84331BE15294000708E8 = { + CreatedOnToolsVersion = 7.0.1; + }; + 63DC84421BE152B5000708E8 = { + CreatedOnToolsVersion = 7.0.1; + }; }; }; buildConfigurationList = 635697C21B14FC11007A7283 /* Build configuration list for PBXProject "Tests" */; @@ -211,6 +397,10 @@ targets = ( 635697C61B14FC11007A7283 /* Tests */, 63423F431B150A5F006CF63C /* AllTests */, + 63DC84121BE15179000708E8 /* RxLibraryUnitTests */, + 63DC84221BE15267000708E8 /* InteropTestsRemote */, + 63DC84331BE15294000708E8 /* InteropTestsLocalSSL */, + 63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */, ); }; /* End PBXProject section */ @@ -224,9 +414,158 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 63DC84111BE15179000708E8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC84211BE15267000708E8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC84321BE15294000708E8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6379CC531BE17709001BC0A1 /* TestCertificates.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC84411BE152B5000708E8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 4C406327D3907A5E5FBA8AC9 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 4F5690DC0E6AD6663FE78B8B /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 5C20DCCB71C3991E6FE78C22 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 5F14F59509E10C2852014F9E /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 693DD0B453431D64EA24FD66 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 7418AC7B3844B29E48D24FC7 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 8AD3130D3C58A0FB32FF2A36 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 900B6EDD4D16BE7D765C3885 /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 914ADDD7106BA9BB8A7E569F /* Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -257,6 +596,81 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; showEnvVarsInLog = 0; }; + A8E3AC66DF770B774114A30E /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + B2986CEEE8CDD4901C97598B /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + C2E09DC4BD239F71160F0CC1 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + C591129ACE9F6CC5EE03FCDE /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C977426A8727267BBAC7D48E /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -265,11 +679,11 @@ buildActionMask = 2147483647; files = ( 63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */, - 63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */, - 63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */, + 6379CC511BE1683B001BC0A1 /* InteropTestsRemote.m in Sources */, 63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */, 6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */, 635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */, + 63DC842E1BE15278000708E8 /* RxLibraryUnitTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -281,6 +695,42 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 63DC840F1BE15179000708E8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63DC841E1BE15180000708E8 /* RxLibraryUnitTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC841F1BE15267000708E8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63DC842F1BE1527D000708E8 /* InteropTests.m in Sources */, + 6379CC501BE16703001BC0A1 /* InteropTestsRemote.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC84301BE15294000708E8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63DC844F1BE15353000708E8 /* InteropTestsLocalSSL.m in Sources */, + 6379CC4D1BE1662A001BC0A1 /* InteropTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 63DC843F1BE152B5000708E8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63DC84501BE153AA000708E8 /* GRPCClientTests.m in Sources */, + 63DC844E1BE15350000708E8 /* InteropTestsLocalCleartext.m in Sources */, + 6379CC4E1BE1662B001BC0A1 /* InteropTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -289,6 +739,26 @@ target = 635697C61B14FC11007A7283 /* Tests */; targetProxy = 63423F4B1B150A5F006CF63C /* PBXContainerItemProxy */; }; + 63DC841A1BE15179000708E8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 635697C61B14FC11007A7283 /* Tests */; + targetProxy = 63DC84191BE15179000708E8 /* PBXContainerItemProxy */; + }; + 63DC842A1BE15267000708E8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 635697C61B14FC11007A7283 /* Tests */; + targetProxy = 63DC84291BE15267000708E8 /* PBXContainerItemProxy */; + }; + 63DC843B1BE15294000708E8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 635697C61B14FC11007A7283 /* Tests */; + targetProxy = 63DC843A1BE15294000708E8 /* PBXContainerItemProxy */; + }; + 63DC844A1BE152B5000708E8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 635697C61B14FC11007A7283 /* Tests */; + targetProxy = 63DC84491BE152B5000708E8 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -418,6 +888,110 @@ }; name = Release; }; + 63DC841C1BE15179000708E8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + INFOPLIST_FILE = Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 63DC841D1BE15179000708E8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */; + buildSettings = { + INFOPLIST_FILE = Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 63DC842C1BE15267000708E8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + INFOPLIST_FILE = Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 63DC842D1BE15267000708E8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */; + buildSettings = { + INFOPLIST_FILE = Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 63DC843D1BE15294000708E8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + INFOPLIST_FILE = Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 63DC843E1BE15294000708E8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */; + buildSettings = { + INFOPLIST_FILE = Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 63DC844C1BE152B5000708E8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + INFOPLIST_FILE = Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 63DC844D1BE152B5000708E8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */; + buildSettings = { + INFOPLIST_FILE = Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -448,6 +1022,42 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 63DC841C1BE15179000708E8 /* Debug */, + 63DC841D1BE15179000708E8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 63DC842C1BE15267000708E8 /* Debug */, + 63DC842D1BE15267000708E8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 63DC843D1BE15294000708E8 /* Debug */, + 63DC843E1BE15294000708E8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 63DC844C1BE152B5000708E8 /* Debug */, + 63DC844D1BE152B5000708E8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 635697BF1B14FC11007A7283 /* Project object */; diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme index a7e0ed110e..e6a052a8ce 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme @@ -23,10 +23,10 @@ </BuildActionEntries> </BuildAction> <TestAction + buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - buildConfiguration = "Debug"> + shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference skipped = "NO"> @@ -45,6 +45,9 @@ Identifier = "GRPCClientTests/testMetadata"> </Test> <Test + Identifier = "InteropTests"> + </Test> + <Test Identifier = "LocalClearTextTests"> </Test> <Test @@ -62,15 +65,18 @@ ReferencedContainer = "container:Tests.xcodeproj"> </BuildableReference> </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> </TestAction> <LaunchAction + buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle = "0" useCustomWorkingDirectory = "NO" - buildConfiguration = "Debug" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" + debugServiceExtension = "internal" allowLocationSimulation = "YES"> <MacroExpansion> <BuildableReference @@ -85,10 +91,10 @@ </AdditionalOptions> </LaunchAction> <ProfileAction + buildConfiguration = "Release" shouldUseLaunchSchemeArgsEnv = "YES" savedToolIdentifier = "" useCustomWorkingDirectory = "NO" - buildConfiguration = "Release" debugDocumentVersioning = "YES"> <MacroExpansion> <BuildableReference diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme new file mode 100644 index 0000000000..ce358bf69f --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0700" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84421BE152B5000708E8" + BuildableName = "InteropTestsLocalCleartext.xctest" + BlueprintName = "InteropTestsLocalCleartext" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84421BE152B5000708E8" + BuildableName = "InteropTestsLocalCleartext.xctest" + BlueprintName = "InteropTestsLocalCleartext" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + <SkippedTests> + <Test + Identifier = "GRPCClientTests/testConnectionToRemoteServer"> + </Test> + <Test + Identifier = "GRPCClientTests/testMetadata"> + </Test> + <Test + Identifier = "InteropTests"> + </Test> + </SkippedTests> + </TestableReference> + </Testables> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84421BE152B5000708E8" + BuildableName = "InteropTestsLocalCleartext.xctest" + BlueprintName = "InteropTestsLocalCleartext" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84421BE152B5000708E8" + BuildableName = "InteropTestsLocalCleartext.xctest" + BlueprintName = "InteropTestsLocalCleartext" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme new file mode 100644 index 0000000000..f268da1fb0 --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0700" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84331BE15294000708E8" + BuildableName = "InteropTestsLocalSSL.xctest" + BlueprintName = "InteropTestsLocalSSL" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84331BE15294000708E8" + BuildableName = "InteropTestsLocalSSL.xctest" + BlueprintName = "InteropTestsLocalSSL" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + <SkippedTests> + <Test + Identifier = "InteropTests"> + </Test> + </SkippedTests> + </TestableReference> + </Testables> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84331BE15294000708E8" + BuildableName = "InteropTestsLocalSSL.xctest" + BlueprintName = "InteropTestsLocalSSL" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84331BE15294000708E8" + BuildableName = "InteropTestsLocalSSL.xctest" + BlueprintName = "InteropTestsLocalSSL" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme new file mode 100644 index 0000000000..186d7208e0 --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0700" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84221BE15267000708E8" + BuildableName = "InteropTestsRemote.xctest" + BlueprintName = "InteropTestsRemote" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84221BE15267000708E8" + BuildableName = "InteropTestsRemote.xctest" + BlueprintName = "InteropTestsRemote" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + <SkippedTests> + <Test + Identifier = "InteropTests"> + </Test> + </SkippedTests> + </TestableReference> + </Testables> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84221BE15267000708E8" + BuildableName = "InteropTestsRemote.xctest" + BlueprintName = "InteropTestsRemote" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84221BE15267000708E8" + BuildableName = "InteropTestsRemote.xctest" + BlueprintName = "InteropTestsRemote" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme new file mode 100644 index 0000000000..3abc1d42e4 --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0700" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84121BE15179000708E8" + BuildableName = "RxLibraryUnitTests.xctest" + BlueprintName = "RxLibraryUnitTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84121BE15179000708E8" + BuildableName = "RxLibraryUnitTests.xctest" + BlueprintName = "RxLibraryUnitTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84121BE15179000708E8" + BuildableName = "RxLibraryUnitTests.xctest" + BlueprintName = "RxLibraryUnitTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "63DC84121BE15179000708E8" + BuildableName = "RxLibraryUnitTests.xctest" + BlueprintName = "RxLibraryUnitTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/src/objective-c/tests/run_tests.sh b/src/objective-c/tests/run_tests.sh index 7b133c1782..598f4e7fa1 100755 --- a/src/objective-c/tests/run_tests.sh +++ b/src/objective-c/tests/run_tests.sh @@ -37,7 +37,7 @@ cd $(dirname $0) # Run the tests server. ../../../bins/$CONFIG/interop_server --port=5050 & -../../../bins/$CONFIG/interop_server --port=5051 --enable_ssl & +../../../bins/$CONFIG/interop_server --port=5051 --use_tls & # Kill them when this script exits. trap 'kill -9 `jobs -p`' EXIT diff --git a/src/php/README.md b/src/php/README.md index afa09d79a1..b1823b9226 100644 --- a/src/php/README.md +++ b/src/php/README.md @@ -5,7 +5,7 @@ This directory contains source code for PHP implementation of gRPC layered on sh #Status -Alpha : Ready for early adopters +Beta ## Environment @@ -14,28 +14,31 @@ Prerequisite: PHP 5.5 or later, `phpunit`, `pecl` **Linux:** ```sh -$ sudo apt-get install php5 php5-dev phpunit php-pear +$ sudo apt-get install php5 php5-dev php-pear ``` **Mac OS X:** ```sh +$ curl -O http://pear.php.net/go-pear.phar +$ sudo php -d detect_unicode=0 go-pear.phar +``` + +**PHPUnit: (Both Linux and Mac OS X)** +```sh $ curl https://phar.phpunit.de/phpunit.phar -o phpunit.phar $ chmod +x phpunit.phar $ sudo mv phpunit.phar /usr/local/bin/phpunit - -$ curl -O http://pear.php.net/go-pear.phar -$ sudo php -d detect_unicode=0 go-pear.phar ``` ## Quick Install **Linux (Debian):** -Add [Debian testing][] to your `sources.list` file. Example: +Add [Debian jessie-backports][] to your `sources.list` file. Example: ```sh -echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" | \ +echo "deb http://http.debian.net/debian jessie-backports main" | \ sudo tee -a /etc/apt/sources.list ``` @@ -49,7 +52,7 @@ sudo apt-get install libgrpc-dev Install the gRPC PHP extension ```sh -sudo pecl install grpc-alpha +sudo pecl install grpc-beta ``` **Mac OS X:** @@ -96,7 +99,7 @@ $ sudo make install # 'make' should have been run by core grpc Install the gRPC PHP extension ```sh -$ sudo pecl install grpc-alpha +$ sudo pecl install grpc-beta ``` OR @@ -109,15 +112,19 @@ $ make $ sudo make install ``` -In your php.ini file, add the line `extension=grpc.so` to load the extension -at PHP startup. +Add this line to your `php.ini` file, e.g. `/etc/php5/cli/php.ini` + +```sh +extension=grpc.so +``` Install Composer ```sh $ cd grpc/src/php $ curl -sS https://getcomposer.org/installer | php -$ php composer.phar install +$ sudo mv composer.phar /usr/local/bin/composer +$ composer install ``` ## Unit Tests @@ -164,7 +171,133 @@ $ cd grpc/src/php $ ./bin/run_gen_code_test.sh ``` +## Use the gRPC PHP extension with Apache + +Install `apache2`, in addition to `php5` above + +```sh +$ sudo apt-get install apache2 +``` + +Add this line to your `php.ini` file, e.g. `/etc/php5/apache2/php.ini` + +```sh +extension=grpc.so +``` + +Restart apache + +```sh +$ sudo service apache2 restart +``` + +Make sure the Node math server is still running, as above. + +```sh +$ cd grpc/src/node +$ nodejs examples/math_server.js +``` + +Make sure you have run `composer install` to generate the `vendor/autoload.php` file + +```sh +$ composer install +``` + +Make sure you have generated the client stub `math.php` + +```sh +$ ./bin/generate_proto_php.sh +``` + +Copy the `math_client.php` file into your Apache document root, e.g. + +```sh +$ cp tests/generated_code/math_client.php /var/www/html +``` + +You may have to fix the first two lines to point the includes to your installation: + +```php +include 'vendor/autoload.php'; +include 'tests/generated_code/math.php'; +``` + +Connect to `localhost/math_client.php` in your browser, or run this from command line: + +```sh +$ curl localhost/math_client.php +``` + +## Use the gRPC PHP extension with Nginx/PHP-FPM + +Install `nginx` and `php5-fpm`, in addition to `php5` above + +```sh +$ sudo apt-get install nginx php5-fpm +``` + +Add this line to your `php.ini` file, e.g. `/etc/php5/fpm/php.ini` + +```sh +extension=grpc.so +``` + +Uncomment the following lines in your `/etc/nginx/sites-available/default` file: + +``` +location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php5-fpm.sock; +} +``` + +Restart nginx and php-fpm + +```sh +$ sudo service nginx restart +$ sudo service php5-fpm restart +``` + +Make sure the Node math server is still running, as above. + +```sh +$ cd grpc/src/node +$ nodejs examples/math_server.js +``` + +Make sure you have run `composer install` to generate the `vendor/autoload.php` file + +```sh +$ composer install +``` + +Make sure you have generated the client stub `math.php` + +```sh +$ ./bin/generate_proto_php.sh +``` + +Copy the `math_client.php` file into your Nginx document root, e.g. + +```sh +$ cp tests/generated_code/math_client.php /var/www/html +``` + +You may have to fix the first two lines to point the includes to your installation: + +```php +include 'vendor/autoload.php'; +include 'tests/generated_code/math.php'; +``` + +Connect to `localhost/math_client.php` in your browser, or run this from command line: + +```sh +$ curl localhost/math_client.php +``` + [homebrew]:http://brew.sh [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install [Node]:https://github.com/grpc/grpc/tree/master/src/node/examples -[Debian testing]:https://www.debian.org/releases/stretch/ +[Debian jessie-backports]:http://backports.debian.org/Instructions/ diff --git a/src/php/bin/interop_client.sh b/src/php/bin/interop_client.sh index 17b888dd4e..2af49cd892 100755 --- a/src/php/bin/interop_client.sh +++ b/src/php/bin/interop_client.sh @@ -31,5 +31,5 @@ set -e cd $(dirname $0) source ./determine_extension_dir.sh -php $extension_dir \ +php $extension_dir -d max_execution_time=300 \ ../tests/interop/interop_client.php $@ 1>&2 diff --git a/src/php/bin/run_gen_code_test.sh b/src/php/bin/run_gen_code_test.sh index 6e56c7207c..1c18756ed2 100755 --- a/src/php/bin/run_gen_code_test.sh +++ b/src/php/bin/run_gen_code_test.sh @@ -32,7 +32,7 @@ set -e cd $(dirname $0) source ./determine_extension_dir.sh export GRPC_TEST_HOST=localhost:50051 -php $extension_dir $(which phpunit) -v --debug --strict \ +php $extension_dir -d max_execution_time=300 $(which phpunit) -v --debug \ ../tests/generated_code/GeneratedCodeTest.php -php $extension_dir $(which phpunit) -v --debug --strict \ +php $extension_dir -d max_execution_time=300 $(which phpunit) -v --debug \ ../tests/generated_code/GeneratedCodeWithCallbackTest.php diff --git a/src/python/grpcio_test/grpc_test/early_adopter/__init__.py b/src/php/bin/run_php_cs_fixer.sh index 7086519106..3e11a12bc1 100644..100755 --- a/src/python/grpcio_test/grpc_test/early_adopter/__init__.py +++ b/src/php/bin/run_php_cs_fixer.sh @@ -1,3 +1,4 @@ +#!/bin/bash # Copyright 2015, Google Inc. # All rights reserved. # @@ -27,4 +28,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +set -e +command -v php-cs-fixer > /dev/null || { + echo "Cannot find php-cs-fixer. Exiting..." + exit 1 +} +cd $(dirname $0)/.. +php-cs-fixer fix lib/Grpc || true +php-cs-fixer fix tests/generated_code || true +php-cs-fixer fix tests/interop || true +php-cs-fixer fix tests/unit_tests || true diff --git a/src/php/bin/run_tests.sh b/src/php/bin/run_tests.sh index 953f408ea8..2a7335e20a 100755 --- a/src/php/bin/run_tests.sh +++ b/src/php/bin/run_tests.sh @@ -31,7 +31,11 @@ # Loads the local shared library, and runs all of the test cases in tests/ # against it set -e -cd $(dirname $0) +cd $(dirname $0)/../../.. +root=$(pwd) +cd src/php/bin source ./determine_extension_dir.sh -php $extension_dir $(which phpunit) -v --debug --strict \ +# in some jenkins macos machine, somehow the PHP build script can't find libgrpc.dylib +export DYLD_LIBRARY_PATH=$root/libs/$config +php $extension_dir -d max_execution_time=300 $(which phpunit) -v --debug \ ../tests/unit_tests diff --git a/src/php/composer.json b/src/php/composer.json index 2d0fe0c87a..1d41f847ac 100644 --- a/src/php/composer.json +++ b/src/php/composer.json @@ -1,7 +1,7 @@ { "name": "grpc/grpc", "description": "gRPC library for PHP", - "version": "0.5.1", + "version": "0.6.0", "homepage": "http://grpc.io", "license": "BSD-3-Clause", "repositories": [ diff --git a/src/php/ext/grpc/README.md b/src/php/ext/grpc/README.md index 88d2c481ec..6e1cb2002f 100644 --- a/src/php/ext/grpc/README.md +++ b/src/php/ext/grpc/README.md @@ -4,7 +4,7 @@ gRPC PHP Extension # Requirements * PHP 5.5+ - * [gRPC core library](https://github.com/grpc/grpc) 0.10.0 + * [gRPC core library](https://github.com/grpc/grpc) 0.11.0 # Installation @@ -22,26 +22,21 @@ Clone the gRPC source code repository $ git clone https://github.com/grpc/grpc.git ``` -Build and install the Protocol Buffers compiler (protoc) +Build and install the gRPC C core libraries -``` -$ # from grpc -$ git checkout --track origin/release-0_9 +```sh +$ cd grpc +$ git checkout --track origin/release-0_11 $ git pull --recurse-submodules && git submodule update --init --recursive -$ cd third_party/protobuf -$ ./autogen.sh -$ ./configure $ make -$ make check $ sudo make install ``` -Build and install the gRPC C core library +Note: you may encounter a warning about the Protobuf compiler `protoc` 3.0.0+ not being installed. The following might help, and will be useful later on when we need to compile the `protoc-gen-php` tool. ```sh -$ # from grpc -$ make -$ sudo make install +$ cd grpc/third_party/protobuf +$ sudo make install # 'make' should have been run by core grpc ``` ## Install the gRPC PHP extension @@ -55,7 +50,7 @@ $ sudo pecl install grpc Note: before a stable release, you may need to do ```sh -$ sudo pecl install grpc-0.5.1 +$ sudo pecl install grpc-beta ``` OR diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index 252623d0c3..3b99de7538 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -265,6 +265,9 @@ PHP_METHOD(Call, startBatch) { HashTable *array_hash; HashPosition array_pointer; HashTable *status_hash; + HashTable *message_hash; + zval **message_value; + zval **message_flags; char *key; uint key_len; ulong index; @@ -319,13 +322,33 @@ PHP_METHOD(Call, startBatch) { metadata.metadata; break; case GRPC_OP_SEND_MESSAGE: - if (Z_TYPE_PP(value) != IS_STRING) { + if (Z_TYPE_PP(value) != IS_ARRAY) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected an array for send message", + 1 TSRMLS_CC); + goto cleanup; + } + message_hash = Z_ARRVAL_PP(value); + if (zend_hash_find(message_hash, "flags", sizeof("flags"), + (void **)&message_flags) == SUCCESS) { + if (Z_TYPE_PP(message_flags) != IS_LONG) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected an int for message flags", + 1 TSRMLS_CC); + } + ops[op_num].flags = Z_LVAL_PP(message_flags) & GRPC_WRITE_USED_MASK; + } + if (zend_hash_find(message_hash, "message", sizeof("message"), + (void **)&message_value) != SUCCESS || + Z_TYPE_PP(message_value) != IS_STRING) { zend_throw_exception(spl_ce_InvalidArgumentException, "Expected a string for send message", 1 TSRMLS_CC); + goto cleanup; } ops[op_num].data.send_message = - string_to_byte_buffer(Z_STRVAL_PP(value), Z_STRLEN_PP(value)); + string_to_byte_buffer(Z_STRVAL_PP(message_value), + Z_STRLEN_PP(message_value)); break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: break; diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c index 8e3b7ff212..e413070b45 100644 --- a/src/php/ext/grpc/credentials.c +++ b/src/php/ext/grpc/credentials.c @@ -111,21 +111,21 @@ PHP_METHOD(Credentials, createDefault) { * @return Credentials The new SSL credentials object */ PHP_METHOD(Credentials, createSsl) { - char *pem_root_certs; + char *pem_root_certs = NULL; grpc_ssl_pem_key_cert_pair pem_key_cert_pair; - int root_certs_length, private_key_length = 0, cert_chain_length = 0; + int root_certs_length = 0, private_key_length = 0, cert_chain_length = 0; pem_key_cert_pair.private_key = pem_key_cert_pair.cert_chain = NULL; - /* "s|s!s! == 1 string, 2 optional nullable strings */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!", + /* "|s!s!s! == 3 optional nullable strings */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!s!", &pem_root_certs, &root_certs_length, &pem_key_cert_pair.private_key, &private_key_length, &pem_key_cert_pair.cert_chain, &cert_chain_length) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "createSsl expects 1 to 3 strings", 1 TSRMLS_CC); + "createSsl expects 3 optional strings", 1 TSRMLS_CC); return; } grpc_credentials *creds = grpc_ssl_credentials_create( diff --git a/src/php/ext/grpc/package.xml b/src/php/ext/grpc/package.xml index bc2a05923b..921cfc6ae6 100644 --- a/src/php/ext/grpc/package.xml +++ b/src/php/ext/grpc/package.xml @@ -10,43 +10,43 @@ <email>grpc-packages@google.com</email> <active>yes</active> </lead> - <date>2015-07-09</date> - <time>21:47:27</time> + <date>2015-10-07</date> + <time>13:40:54</time> <version> - <release>0.5.1</release> - <api>0.5.1</api> + <release>0.6.1</release> + <api>0.6.0</api> </version> <stability> - <release>alpha</release> - <api>alpha</api> + <release>beta</release> + <api>beta</api> </stability> <license>BSD</license> <notes> -Update to wrap gRPC C Core version 0.10.0 +- fixed undefined constant fatal error when run with apache/nginx #2275 </notes> <contents> <dir baseinstalldir="/" name="/"> <file baseinstalldir="/" md5sum="6f19828fb869b7b8a590cbb76b4f996d" name="byte_buffer.c" role="src" /> <file baseinstalldir="/" md5sum="c8de0f819499c48adfc8d7f472c0196b" name="byte_buffer.h" role="src" /> - <file baseinstalldir="/" md5sum="cb45b62f767ae7b4377761df696649fc" name="call.c" role="src" /> + <file baseinstalldir="/" md5sum="d64c9005993de02abac55664b0b9e0b2" name="call.c" role="src" /> <file baseinstalldir="/" md5sum="26acbf04c30162c2d2aca4688bb2aec8" name="call.h" role="src" /> - <file baseinstalldir="/" md5sum="50837fbdb2892795f1871b22e5979762" name="channel.c" role="src" /> - <file baseinstalldir="/" md5sum="f1b66029daeced20b47cf00cc6523fc8" name="channel.h" role="src" /> - <file baseinstalldir="/" md5sum="81a1193e93d8b6602add8ac360de565b" name="completion_queue.c" role="src" /> + <file baseinstalldir="/" md5sum="15e56239b32c803f073e8a2b9f96e8c3" name="channel.c" role="src" /> + <file baseinstalldir="/" md5sum="ed4b00c0cf3702b115d0cfa87450dc09" name="channel.h" role="src" /> + <file baseinstalldir="/" md5sum="55ab7a42f9dd9bfc7e28a61cfc5fca63" name="completion_queue.c" role="src" /> <file baseinstalldir="/" md5sum="f10b5bb232d74a6878e829e2e76cdaa2" name="completion_queue.h" role="src" /> <file baseinstalldir="/" md5sum="a22f8eac0164761058cc4d9eb2ceb069" name="config.m4" role="src" /> - <file baseinstalldir="/" md5sum="8c3f1e11dac623001378bfd53b554f08" name="credentials.c" role="src" /> + <file baseinstalldir="/" md5sum="588752c908f7bc1663f7b8fc922ae661" name="credentials.c" role="src" /> <file baseinstalldir="/" md5sum="6988d6e97c19c8f8e3eb92371cf8246b" name="credentials.h" role="src" /> <file baseinstalldir="/" md5sum="38a1bc979d810c36ebc2a52d4b7b5319" name="CREDITS" role="doc" /> <file baseinstalldir="/" md5sum="3f35b472bbdef5a788cd90617d7d0847" name="LICENSE" role="doc" /> - <file baseinstalldir="/" md5sum="6aaa7a290122d230f2d8c4e4e05da4a9" name="php_grpc.c" role="src" /> + <file baseinstalldir="/" md5sum="b77f1f3941aaf7a21090b493e9f26037" name="php_grpc.c" role="src" /> <file baseinstalldir="/" md5sum="673b07859d9f69232f8a754c56780686" name="php_grpc.h" role="src" /> - <file baseinstalldir="/" md5sum="c1d0b42fd77b7d6740bf7744bee90af5" name="README.md" role="doc" /> - <file baseinstalldir="/" md5sum="30997dd423403e1f8ad09dcee598e5c4" name="server.c" role="src" /> + <file baseinstalldir="/" md5sum="7533a6d3ea02c78cad23a9651de0825d" name="README.md" role="doc" /> + <file baseinstalldir="/" md5sum="3e4e960454ebb2fc7b78a840493f5315" name="server.c" role="src" /> <file baseinstalldir="/" md5sum="4b730f06d14cbbb0642bdbd194749595" name="server.h" role="src" /> - <file baseinstalldir="/" md5sum="f6930beafb6c0e061899262f2f077e98" name="server_credentials.c" role="src" /> + <file baseinstalldir="/" md5sum="34ea881f1fe960d190d0713422cf8916" name="server_credentials.c" role="src" /> <file baseinstalldir="/" md5sum="9c4b4cc06356a8a39a16a085a9b85996" name="server_credentials.h" role="src" /> - <file baseinstalldir="/" md5sum="c89c623cd17177ebde18313fc5c17122" name="timeval.c" role="src" /> + <file baseinstalldir="/" md5sum="7646ec78cb133f66ba59e03c6f451e39" name="timeval.c" role="src" /> <file baseinstalldir="/" md5sum="496e27a100b4d93ca3fb35c924c5e163" name="timeval.h" role="src" /> </dir> </contents> @@ -93,5 +93,40 @@ First alpha release Update to wrap gRPC C Core version 0.10.0 </notes> </release> + <release> + <version> + <release>0.6.0</release> + <api>0.6.0</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2015-09-24</date> + <license>BSD</license> + <notes> +- support per message compression disable +- expose per-call host override option +- expose connectivity API +- expose channel target and call peer +- add user-agent +- update to wrap gRPC C core library beta version 0.11.0 + </notes> + </release> + <release> + <version> + <release>0.6.1</release> + <api>0.6.0</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2015-10-07</date> + <license>BSD</license> + <notes> +- fixed undefined constant fatal error when run with apache/nginx #2275 + </notes> + </release> </changelog> </package> diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c index 0f730ea756..fcd94a6306 100644 --- a/src/php/ext/grpc/php_grpc.c +++ b/src/php/ext/grpc/php_grpc.c @@ -109,91 +109,125 @@ PHP_MINIT_FUNCTION(grpc) { */ /* Register call error constants */ grpc_init(); - REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK, + CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER", - GRPC_CALL_ERROR_NOT_ON_SERVER, CONST_CS); + GRPC_CALL_ERROR_NOT_ON_SERVER, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT", - GRPC_CALL_ERROR_NOT_ON_CLIENT, CONST_CS); + GRPC_CALL_ERROR_NOT_ON_CLIENT, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED", - GRPC_CALL_ERROR_ALREADY_INVOKED, CONST_CS); + GRPC_CALL_ERROR_ALREADY_INVOKED, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED", - GRPC_CALL_ERROR_NOT_INVOKED, CONST_CS); + GRPC_CALL_ERROR_NOT_INVOKED, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED", - GRPC_CALL_ERROR_ALREADY_FINISHED, CONST_CS); + GRPC_CALL_ERROR_ALREADY_FINISHED, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS", - GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, CONST_CS); + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS", - GRPC_CALL_ERROR_INVALID_FLAGS, CONST_CS); + GRPC_CALL_ERROR_INVALID_FLAGS, + CONST_CS | CONST_PERSISTENT); /* Register flag constants */ REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT", GRPC_WRITE_BUFFER_HINT, - CONST_CS); + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS", GRPC_WRITE_NO_COMPRESS, - CONST_CS); + CONST_CS | CONST_PERSISTENT); /* Register status constants */ - REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", GRPC_STATUS_OK, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", GRPC_STATUS_OK, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED", GRPC_STATUS_CANCELLED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", GRPC_STATUS_UNKNOWN, CONST_CS); + CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", GRPC_STATUS_UNKNOWN, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT", - GRPC_STATUS_INVALID_ARGUMENT, CONST_CS); + GRPC_STATUS_INVALID_ARGUMENT, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED", - GRPC_STATUS_DEADLINE_EXCEEDED, CONST_CS); + GRPC_STATUS_DEADLINE_EXCEEDED, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND", GRPC_STATUS_NOT_FOUND, - CONST_CS); + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS", - GRPC_STATUS_ALREADY_EXISTS, CONST_CS); + GRPC_STATUS_ALREADY_EXISTS, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED", - GRPC_STATUS_PERMISSION_DENIED, CONST_CS); + GRPC_STATUS_PERMISSION_DENIED, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED", - GRPC_STATUS_UNAUTHENTICATED, CONST_CS); + GRPC_STATUS_UNAUTHENTICATED, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED", - GRPC_STATUS_RESOURCE_EXHAUSTED, CONST_CS); + GRPC_STATUS_RESOURCE_EXHAUSTED, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION", - GRPC_STATUS_FAILED_PRECONDITION, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE, - CONST_CS); + GRPC_STATUS_FAILED_PRECONDITION, + CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED, + CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE", + GRPC_STATUS_OUT_OF_RANGE, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED", - GRPC_STATUS_UNIMPLEMENTED, CONST_CS); + GRPC_STATUS_UNIMPLEMENTED, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL", GRPC_STATUS_INTERNAL, - CONST_CS); + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE", GRPC_STATUS_UNAVAILABLE, - CONST_CS); + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS", GRPC_STATUS_DATA_LOSS, - CONST_CS); + CONST_CS | CONST_PERSISTENT); /* Register op type constants */ REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_INITIAL_METADATA", - GRPC_OP_SEND_INITIAL_METADATA, CONST_CS); + GRPC_OP_SEND_INITIAL_METADATA, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_MESSAGE", - GRPC_OP_SEND_MESSAGE, CONST_CS); + GRPC_OP_SEND_MESSAGE, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_CLOSE_FROM_CLIENT", - GRPC_OP_SEND_CLOSE_FROM_CLIENT, CONST_CS); + GRPC_OP_SEND_CLOSE_FROM_CLIENT, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_STATUS_FROM_SERVER", - GRPC_OP_SEND_STATUS_FROM_SERVER, CONST_CS); + GRPC_OP_SEND_STATUS_FROM_SERVER, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_INITIAL_METADATA", - GRPC_OP_RECV_INITIAL_METADATA, CONST_CS); + GRPC_OP_RECV_INITIAL_METADATA, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_MESSAGE", - GRPC_OP_RECV_MESSAGE, CONST_CS); + GRPC_OP_RECV_MESSAGE, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_STATUS_ON_CLIENT", - GRPC_OP_RECV_STATUS_ON_CLIENT, CONST_CS); + GRPC_OP_RECV_STATUS_ON_CLIENT, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_CLOSE_ON_SERVER", - GRPC_OP_RECV_CLOSE_ON_SERVER, CONST_CS); + GRPC_OP_RECV_CLOSE_ON_SERVER, + CONST_CS | CONST_PERSISTENT); /* Register connectivity state constants */ REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_IDLE", - GRPC_CHANNEL_IDLE, CONST_CS); + GRPC_CHANNEL_IDLE, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_CONNECTING", - GRPC_CHANNEL_CONNECTING, CONST_CS); + GRPC_CHANNEL_CONNECTING, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_READY", - GRPC_CHANNEL_READY, CONST_CS); + GRPC_CHANNEL_READY, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_TRANSIENT_FAILURE", - GRPC_CHANNEL_TRANSIENT_FAILURE, CONST_CS); + GRPC_CHANNEL_TRANSIENT_FAILURE, + CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_FATAL_FAILURE", - GRPC_CHANNEL_FATAL_FAILURE, CONST_CS); + GRPC_CHANNEL_FATAL_FAILURE, + CONST_CS | CONST_PERSISTENT); grpc_init_call(TSRMLS_C); grpc_init_channel(TSRMLS_C); diff --git a/src/php/lib/Grpc/AbstractCall.php b/src/php/lib/Grpc/AbstractCall.php index 35057224f8..53849d51fc 100644 --- a/src/php/lib/Grpc/AbstractCall.php +++ b/src/php/lib/Grpc/AbstractCall.php @@ -31,65 +31,79 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + namespace Grpc; -abstract class AbstractCall { +abstract class AbstractCall +{ + protected $call; + protected $deserialize; + protected $metadata; - protected $call; - protected $deserialize; - protected $metadata; + /** + * Create a new Call wrapper object. + * + * @param Channel $channel The channel to communicate on + * @param string $method The method to call on the + * remote server + * @param callback $deserialize A callback function to deserialize + * the response + * @param (optional) long $timeout Timeout in microseconds + */ + public function __construct(Channel $channel, + $method, + $deserialize, + $timeout = false) + { + if ($timeout) { + $now = Timeval::now(); + $delta = new Timeval($timeout); + $deadline = $now->add($delta); + } else { + $deadline = Timeval::infFuture(); + } + $this->call = new Call($channel, $method, $deadline); + $this->deserialize = $deserialize; + $this->metadata = null; + } - /** - * Create a new Call wrapper object. - * @param Channel $channel The channel to communicate on - * @param string $method The method to call on the remote server - * @param callback $deserialize A callback function to deserialize - * the response - * @param (optional) long $timeout Timeout in microseconds - */ - public function __construct(Channel $channel, $method, $deserialize, $timeout = false) { - if ($timeout) { - $now = Timeval::now(); - $delta = new Timeval($timeout); - $deadline = $now->add($delta); - } else { - $deadline = Timeval::infFuture(); + /** + * @return The metadata sent by the server. + */ + public function getMetadata() + { + return $this->metadata; } - $this->call = new Call($channel, $method, $deadline); - $this->deserialize = $deserialize; - $this->metadata = null; - } - /** - * @return The metadata sent by the server. - */ - public function getMetadata() { - return $this->metadata; - } + /** + * @return string The URI of the endpoint. + */ + public function getPeer() + { + return $this->call->getPeer(); + } - /** - * @return string The URI of the endpoint. - */ - public function getPeer() { - return $this->call->getPeer(); - } + /** + * Cancels the call. + */ + public function cancel() + { + $this->call->cancel(); + } - /** - * Cancels the call - */ - public function cancel() { - $this->call->cancel(); - } + /** + * Deserialize a response value to an object. + * + * @param string $value The binary value to deserialize + * + * @return The deserialized value + */ + protected function deserializeResponse($value) + { + if ($value === null) { + return; + } - /** - * Deserialize a response value to an object. - * @param string $value The binary value to deserialize - * @return The deserialized value - */ - protected function deserializeResponse($value) { - if ($value === null) { - return null; + return call_user_func($this->deserialize, $value); } - return call_user_func($this->deserialize, $value); - } -}
\ No newline at end of file +} diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php index 2e980c5eed..c26be607ff 100755 --- a/src/php/lib/Grpc/BaseStub.php +++ b/src/php/lib/Grpc/BaseStub.php @@ -31,229 +31,308 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + namespace Grpc; /** * Base class for generated client stubs. Stub methods are expected to call * _simpleRequest or _streamRequest and return the result. */ -class BaseStub { +class BaseStub +{ + private $hostname; + private $channel; + + // a callback function + private $update_metadata; - private $hostname; - private $channel; + /** + * @param $hostname string + * @param $opts array + * - 'update_metadata': (optional) a callback function which takes in a + * metadata array, and returns an updated metadata array + */ + public function __construct($hostname, $opts) + { + $this->hostname = $hostname; + $this->update_metadata = null; + if (isset($opts['update_metadata'])) { + if (is_callable($opts['update_metadata'])) { + $this->update_metadata = $opts['update_metadata']; + } + unset($opts['update_metadata']); + } + $package_config = json_decode( + file_get_contents(dirname(__FILE__).'/../../composer.json'), true); + $opts['grpc.primary_user_agent'] = + 'grpc-php/'.$package_config['version']; + $this->channel = new Channel($hostname, $opts); + } - // a callback function - private $update_metadata; + /** + * @return string The URI of the endpoint. + */ + public function getTarget() + { + return $this->channel->getTarget(); + } - /** - * @param $hostname string - * @param $opts array - * - 'update_metadata': (optional) a callback function which takes in a - * metadata array, and returns an updated metadata array - */ - public function __construct($hostname, $opts) { - $this->hostname = $hostname; - $this->update_metadata = null; - if (isset($opts['update_metadata'])) { - if (is_callable($opts['update_metadata'])) { - $this->update_metadata = $opts['update_metadata']; - } - unset($opts['update_metadata']); + /** + * @param $try_to_connect bool + * + * @return int The grpc connectivity state + */ + public function getConnectivityState($try_to_connect = false) + { + return $this->channel->getConnectivityState($try_to_connect); } - $package_config = json_decode( - file_get_contents(dirname(__FILE__) . '/../../composer.json'), true); - $opts['grpc.primary_user_agent'] = - 'grpc-php/' . $package_config['version']; - $this->channel = new Channel($hostname, $opts); - } - /** - * @return string The URI of the endpoint. - */ - public function getTarget() { - return $this->channel->getTarget(); - } + /** + * @param $timeout in microseconds + * + * @return bool true if channel is ready + * @throw Exception if channel is in FATAL_ERROR state + */ + public function waitForReady($timeout) + { + $new_state = $this->getConnectivityState(true); + if ($this->_checkConnectivityState($new_state)) { + return true; + } - /** - * @param $try_to_connect bool - * @return int The grpc connectivity state - */ - public function getConnectivityState($try_to_connect = false) { - return $this->channel->getConnectivityState($try_to_connect); - } + $now = Timeval::now(); + $delta = new Timeval($timeout); + $deadline = $now->add($delta); - /** - * @param $timeout in microseconds - * @return bool true if channel is ready - * @throw Exception if channel is in FATAL_ERROR state - */ - public function waitForReady($timeout) { - $new_state = $this->getConnectivityState(true); - if ($this->_checkConnectivityState($new_state)) { - return true; + while ($this->channel->watchConnectivityState($new_state, $deadline)) { + // state has changed before deadline + $new_state = $this->getConnectivityState(); + if ($this->_checkConnectivityState($new_state)) { + return true; + } + } + // deadline has passed + $new_state = $this->getConnectivityState(); + + return $this->_checkConnectivityState($new_state); } - $now = Timeval::now(); - $delta = new Timeval($timeout); - $deadline = $now->add($delta); + private function _checkConnectivityState($new_state) + { + if ($new_state == \Grpc\CHANNEL_READY) { + return true; + } + if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) { + throw new \Exception('Failed to connect to server'); + } - while ($this->channel->watchConnectivityState($new_state, $deadline)) { - // state has changed before deadline - $new_state = $this->getConnectivityState(); - if ($this->_checkConnectivityState($new_state)) { - return true; - } + return false; } - // deadline has passed - $new_state = $this->getConnectivityState(); - return $this->_checkConnectivityState($new_state); - } - private function _checkConnectivityState($new_state) { - if ($new_state == \Grpc\CHANNEL_READY) { - return true; + /** + * Close the communication channel associated with this stub. + */ + public function close() + { + $this->channel->close(); } - if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) { - throw new Exception('Failed to connect to server'); + + /** + * constructs the auth uri for the jwt. + */ + private function _get_jwt_aud_uri($method) + { + $last_slash_idx = strrpos($method, '/'); + if ($last_slash_idx === false) { + throw new \InvalidArgumentException( + 'service name must have a slash'); + } + $service_name = substr($method, 0, $last_slash_idx); + + return 'https://'.$this->hostname.$service_name; } - return false; - } - /** - * Close the communication channel associated with this stub - */ - public function close() { - $channel->close(); - } + /** + * extract $timeout from $metadata. + * + * @param $metadata The metadata map + * + * @return list($metadata_copy, $timeout) + */ + private function _extract_timeout_from_metadata($metadata) + { + $timeout = false; + $metadata_copy = $metadata; + if (isset($metadata['timeout'])) { + $timeout = $metadata['timeout']; + unset($metadata_copy['timeout']); + } - /** - * constructs the auth uri for the jwt - */ - private function _get_jwt_aud_uri($method) { - $last_slash_idx = strrpos($method, '/'); - if ($last_slash_idx === false) { - return false; + return [$metadata_copy, $timeout]; } - $service_name = substr($method, 0, $last_slash_idx); - return "https://" . $this->hostname . $service_name; - } - /** - * extract $timeout from $metadata - * @param $metadata The metadata map - * @return list($metadata_copy, $timeout) - */ - private function _extract_timeout_from_metadata($metadata) { - $timeout = false; - $metadata_copy = $metadata; - if (isset($metadata['timeout'])) { - $timeout = $metadata['timeout']; - unset($metadata_copy['timeout']); + /** + * validate and normalize the metadata array. + * + * @param $metadata The metadata map + * + * @return $metadata Validated and key-normalized metadata map + * @throw InvalidArgumentException if key contains invalid characters + */ + private function _validate_and_normalize_metadata($metadata) + { + $metadata_copy = []; + foreach ($metadata as $key => $value) { + if (!preg_match('/^[A-Za-z\d_-]+$/', $key)) { + throw new \InvalidArgumentException( + 'Metadata keys must be nonempty strings containing only '. + 'alphanumeric characters, hyphens and underscores'); + } + $metadata_copy[strtolower($key)] = $value; + } + + return $metadata_copy; } - return array($metadata_copy, $timeout); - } - /* This class is intended to be subclassed by generated code, so all functions - begin with "_" to avoid name collisions. */ + /* This class is intended to be subclassed by generated code, so + * all functions begin with "_" to avoid name collisions. */ - /** - * Call a remote method that takes a single argument and has a single output - * - * @param string $method The name of the method to call - * @param $argument The argument to the method - * @param callable $deserialize A function that deserializes the response - * @param array $metadata A metadata map to send to the server - * @return SimpleSurfaceActiveCall The active call object - */ - public function _simpleRequest($method, - $argument, - callable $deserialize, - $metadata = array()) { - list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata); - $call = new UnaryCall($this->channel, $method, $deserialize, $timeout); - $jwt_aud_uri = $this->_get_jwt_aud_uri($method); - if (is_callable($this->update_metadata)) { - $actual_metadata = call_user_func($this->update_metadata, + /** + * Call a remote method that takes a single argument and has a + * single output. + * + * @param string $method The name of the method to call + * @param $argument The argument to the method + * @param callable $deserialize A function that deserializes the response + * @param array $metadata A metadata map to send to the server + * + * @return SimpleSurfaceActiveCall The active call object + */ + public function _simpleRequest($method, + $argument, + callable $deserialize, + $metadata = [], + $options = []) + { + list($actual_metadata, $timeout) = + $this->_extract_timeout_from_metadata($metadata); + $call = new UnaryCall($this->channel, + $method, + $deserialize, + $timeout); + $jwt_aud_uri = $this->_get_jwt_aud_uri($method); + if (is_callable($this->update_metadata)) { + $actual_metadata = call_user_func($this->update_metadata, $actual_metadata, $jwt_aud_uri); + } + $actual_metadata = $this->_validate_and_normalize_metadata( + $actual_metadata); + $call->start($argument, $actual_metadata, $options); + + return $call; } - $call->start($argument, $actual_metadata); - return $call; - } - /** - * Call a remote method that takes a stream of arguments and has a single - * output - * - * @param string $method The name of the method to call - * @param $arguments An array or Traversable of arguments to stream to the - * server - * @param callable $deserialize A function that deserializes the response - * @param array $metadata A metadata map to send to the server - * @return ClientStreamingSurfaceActiveCall The active call object - */ - public function _clientStreamRequest($method, - $arguments, - callable $deserialize, - $metadata = array()) { - list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata); - $call = new ClientStreamingCall($this->channel, $method, $deserialize, $timeout); - $jwt_aud_uri = $this->_get_jwt_aud_uri($method); - if (is_callable($this->update_metadata)) { - $actual_metadata = call_user_func($this->update_metadata, + /** + * Call a remote method that takes a stream of arguments and has a single + * output. + * + * @param string $method The name of the method to call + * @param $arguments An array or Traversable of arguments to stream to the + * server + * @param callable $deserialize A function that deserializes the response + * @param array $metadata A metadata map to send to the server + * + * @return ClientStreamingSurfaceActiveCall The active call object + */ + public function _clientStreamRequest($method, + callable $deserialize, + $metadata = []) + { + list($actual_metadata, $timeout) = + $this->_extract_timeout_from_metadata($metadata); + $call = new ClientStreamingCall($this->channel, + $method, + $deserialize, + $timeout); + $jwt_aud_uri = $this->_get_jwt_aud_uri($method); + if (is_callable($this->update_metadata)) { + $actual_metadata = call_user_func($this->update_metadata, $actual_metadata, $jwt_aud_uri); + } + $actual_metadata = $this->_validate_and_normalize_metadata( + $actual_metadata); + $call->start($actual_metadata); + + return $call; } - $call->start($arguments, $actual_metadata); - return $call; - } - /** - * Call a remote method that takes a single argument and returns a stream of - * responses - * - * @param string $method The name of the method to call - * @param $argument The argument to the method - * @param callable $deserialize A function that deserializes the responses - * @param array $metadata A metadata map to send to the server - * @return ServerStreamingSurfaceActiveCall The active call object - */ - public function _serverStreamRequest($method, - $argument, - callable $deserialize, - $metadata = array()) { - list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata); - $call = new ServerStreamingCall($this->channel, $method, $deserialize, $timeout); - $jwt_aud_uri = $this->_get_jwt_aud_uri($method); - if (is_callable($this->update_metadata)) { - $actual_metadata = call_user_func($this->update_metadata, + /** + * Call a remote method that takes a single argument and returns a stream of + * responses. + * + * @param string $method The name of the method to call + * @param $argument The argument to the method + * @param callable $deserialize A function that deserializes the responses + * @param array $metadata A metadata map to send to the server + * + * @return ServerStreamingSurfaceActiveCall The active call object + */ + public function _serverStreamRequest($method, + $argument, + callable $deserialize, + $metadata = [], + $options = []) + { + list($actual_metadata, $timeout) = + $this->_extract_timeout_from_metadata($metadata); + $call = new ServerStreamingCall($this->channel, + $method, + $deserialize, + $timeout); + $jwt_aud_uri = $this->_get_jwt_aud_uri($method); + if (is_callable($this->update_metadata)) { + $actual_metadata = call_user_func($this->update_metadata, $actual_metadata, $jwt_aud_uri); + } + $actual_metadata = $this->_validate_and_normalize_metadata( + $actual_metadata); + $call->start($argument, $actual_metadata, $options); + + return $call; } - $call->start($argument, $actual_metadata); - return $call; - } - /** - * Call a remote method with messages streaming in both directions - * - * @param string $method The name of the method to call - * @param callable $deserialize A function that deserializes the responses - * @param array $metadata A metadata map to send to the server - * @return BidiStreamingSurfaceActiveCall The active call object - */ - public function _bidiRequest($method, - callable $deserialize, - $metadata = array()) { - list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata); - $call = new BidiStreamingCall($this->channel, $method, $deserialize, $timeout); - $jwt_aud_uri = $this->_get_jwt_aud_uri($method); - if (is_callable($this->update_metadata)) { - $actual_metadata = call_user_func($this->update_metadata, + /** + * Call a remote method with messages streaming in both directions. + * + * @param string $method The name of the method to call + * @param callable $deserialize A function that deserializes the responses + * @param array $metadata A metadata map to send to the server + * + * @return BidiStreamingSurfaceActiveCall The active call object + */ + public function _bidiRequest($method, + callable $deserialize, + $metadata = []) + { + list($actual_metadata, $timeout) = + $this->_extract_timeout_from_metadata($metadata); + $call = new BidiStreamingCall($this->channel, + $method, + $deserialize, + $timeout); + $jwt_aud_uri = $this->_get_jwt_aud_uri($method); + if (is_callable($this->update_metadata)) { + $actual_metadata = call_user_func($this->update_metadata, $actual_metadata, $jwt_aud_uri); + } + $actual_metadata = $this->_validate_and_normalize_metadata( + $actual_metadata); + $call->start($actual_metadata); + + return $call; } - $call->start($actual_metadata); - return $call; - } } diff --git a/src/php/lib/Grpc/BidiStreamingCall.php b/src/php/lib/Grpc/BidiStreamingCall.php index 76c642bef4..bf813c12e7 100644 --- a/src/php/lib/Grpc/BidiStreamingCall.php +++ b/src/php/lib/Grpc/BidiStreamingCall.php @@ -31,62 +31,87 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + namespace Grpc; /** * Represents an active call that allows for sending and recieving messages in * streams in any order. */ -class BidiStreamingCall extends AbstractCall { - /** - * Start the call - * @param array $metadata Metadata to send with the call, if applicable - */ - public function start($metadata) { - $this->call->startBatch([OP_SEND_INITIAL_METADATA => $metadata]); - } +class BidiStreamingCall extends AbstractCall +{ + /** + * Start the call. + * + * @param array $metadata Metadata to send with the call, if applicable + */ + public function start($metadata = []) + { + $this->call->startBatch([ + OP_SEND_INITIAL_METADATA => $metadata, + ]); + } + + /** + * Reads the next value from the server. + * + * @return The next value from the server, or null if there is none + */ + public function read() + { + $batch = [OP_RECV_MESSAGE => true]; + if ($this->metadata === null) { + $batch[OP_RECV_INITIAL_METADATA] = true; + } + $read_event = $this->call->startBatch($batch); + if ($this->metadata === null) { + $this->metadata = $read_event->metadata; + } - /** - * Reads the next value from the server. - * @return The next value from the server, or null if there is none - */ - public function read() { - $batch = [OP_RECV_MESSAGE => true]; - if ($this->metadata === null) { - $batch[OP_RECV_INITIAL_METADATA] = true; + return $this->deserializeResponse($read_event->message); } - $read_event = $this->call->startBatch($batch); - if ($this->metadata === null) { - $this->metadata = $read_event->metadata; + + /** + * Write a single message to the server. This cannot be called after + * writesDone is called. + * + * @param ByteBuffer $data The data to write + * @param array $options an array of options, possible keys: + * 'flags' => a number + */ + public function write($data, $options = []) + { + $message_array = ['message' => $data->serialize()]; + if (isset($options['flags'])) { + $message_array['flags'] = $options['flags']; + } + $this->call->startBatch([ + OP_SEND_MESSAGE => $message_array, + ]); } - return $this->deserializeResponse($read_event->message); - } - /** - * Write a single message to the server. This cannot be called after - * writesDone is called. - * @param ByteBuffer $data The data to write - */ - public function write($data) { - $this->call->startBatch([OP_SEND_MESSAGE => $data->serialize()]); - } + /** + * Indicate that no more writes will be sent. + */ + public function writesDone() + { + $this->call->startBatch([ + OP_SEND_CLOSE_FROM_CLIENT => true, + ]); + } - /** - * Indicate that no more writes will be sent. - */ - public function writesDone() { - $this->call->startBatch([OP_SEND_CLOSE_FROM_CLIENT => true]); - } + /** + * Wait for the server to send the status, and return it. + * + * @return object The status object, with integer $code, string $details, + * and array $metadata members + */ + public function getStatus() + { + $status_event = $this->call->startBatch([ + OP_RECV_STATUS_ON_CLIENT => true, + ]); - /** - * Wait for the server to send the status, and return it. - * @return object The status object, with integer $code, string $details, - * and array $metadata members - */ - public function getStatus() { - $status_event = $this->call->startBatch([ - OP_RECV_STATUS_ON_CLIENT => true - ]); - return $status_event->status; - } -}
\ No newline at end of file + return $status_event->status; + } +} diff --git a/src/php/lib/Grpc/ClientStreamingCall.php b/src/php/lib/Grpc/ClientStreamingCall.php index 61439d3f47..500cfe0d7a 100644 --- a/src/php/lib/Grpc/ClientStreamingCall.php +++ b/src/php/lib/Grpc/ClientStreamingCall.php @@ -31,36 +31,61 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + namespace Grpc; /** * Represents an active call that sends a stream of messages and then gets a * single response. */ -class ClientStreamingCall extends AbstractCall { - /** - * Start the call. - * @param Traversable $arg_iter The iterator of arguments to send - * @param array $metadata Metadata to send with the call, if applicable - */ - public function start($arg_iter, $metadata = array()) { - $event = $this->call->startBatch([OP_SEND_INITIAL_METADATA => $metadata]); - foreach($arg_iter as $arg) { - $this->call->startBatch([OP_SEND_MESSAGE => $arg->serialize()]); +class ClientStreamingCall extends AbstractCall +{ + /** + * Start the call. + * + * @param array $metadata Metadata to send with the call, if applicable + */ + public function start($metadata = []) + { + $this->call->startBatch([ + OP_SEND_INITIAL_METADATA => $metadata, + ]); + } + + /** + * Write a single message to the server. This cannot be called after + * wait is called. + * + * @param ByteBuffer $data The data to write + * @param array $options an array of options, possible keys: + * 'flags' => a number + */ + public function write($data, $options = []) + { + $message_array = ['message' => $data->serialize()]; + if (isset($options['flags'])) { + $message_array['flags'] = $options['flags']; + } + $this->call->startBatch([ + OP_SEND_MESSAGE => $message_array, + ]); } - $this->call->startBatch([OP_SEND_CLOSE_FROM_CLIENT => true]); - } - /** - * Wait for the server to respond with data and a status - * @return [response data, status] - */ - public function wait() { - $event = $this->call->startBatch([ - OP_RECV_INITIAL_METADATA => true, - OP_RECV_MESSAGE => true, - OP_RECV_STATUS_ON_CLIENT => true]); - $this->metadata = $event->metadata; - return array($this->deserializeResponse($event->message), $event->status); - } -}
\ No newline at end of file + /** + * Wait for the server to respond with data and a status. + * + * @return [response data, status] + */ + public function wait() + { + $event = $this->call->startBatch([ + OP_SEND_CLOSE_FROM_CLIENT => true, + OP_RECV_INITIAL_METADATA => true, + OP_RECV_MESSAGE => true, + OP_RECV_STATUS_ON_CLIENT => true, + ]); + $this->metadata = $event->metadata; + + return [$this->deserializeResponse($event->message), $event->status]; + } +} diff --git a/src/php/lib/Grpc/ServerStreamingCall.php b/src/php/lib/Grpc/ServerStreamingCall.php index 631c863345..da48523717 100644 --- a/src/php/lib/Grpc/ServerStreamingCall.php +++ b/src/php/lib/Grpc/ServerStreamingCall.php @@ -31,47 +31,66 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + namespace Grpc; /** * Represents an active call that sends a single message and then gets a stream - * of reponses + * of reponses. */ -class ServerStreamingCall extends AbstractCall { - /** - * Start the call - * @param $arg The argument to send - * @param array $metadata Metadata to send with the call, if applicable - */ - public function start($arg, $metadata = array()) { - $event = $this->call->startBatch([ - OP_SEND_INITIAL_METADATA => $metadata, - OP_RECV_INITIAL_METADATA => true, - OP_SEND_MESSAGE => $arg->serialize(), - OP_SEND_CLOSE_FROM_CLIENT => true]); - $this->metadata = $event->metadata; - } +class ServerStreamingCall extends AbstractCall +{ + /** + * Start the call. + * + * @param $data The data to send + * @param array $metadata Metadata to send with the call, if applicable + * @param array $options an array of options, possible keys: + * 'flags' => a number + */ + public function start($data, $metadata = [], $options = []) + { + $message_array = ['message' => $data->serialize()]; + if (isset($options['flags'])) { + $message_array['flags'] = $options['flags']; + } + $event = $this->call->startBatch([ + OP_SEND_INITIAL_METADATA => $metadata, + OP_RECV_INITIAL_METADATA => true, + OP_SEND_MESSAGE => $message_array, + OP_SEND_CLOSE_FROM_CLIENT => true, + ]); + $this->metadata = $event->metadata; + } - /** - * @return An iterator of response values - */ - public function responses() { - $response = $this->call->startBatch([OP_RECV_MESSAGE => true])->message; - while($response !== null) { - yield $this->deserializeResponse($response); - $response = $this->call->startBatch([OP_RECV_MESSAGE => true])->message; + /** + * @return An iterator of response values + */ + public function responses() + { + $response = $this->call->startBatch([ + OP_RECV_MESSAGE => true, + ])->message; + while ($response !== null) { + yield $this->deserializeResponse($response); + $response = $this->call->startBatch([ + OP_RECV_MESSAGE => true, + ])->message; + } } - } - /** - * Wait for the server to send the status, and return it. - * @return object The status object, with integer $code, string $details, - * and array $metadata members - */ - public function getStatus() { - $status_event = $this->call->startBatch([ - OP_RECV_STATUS_ON_CLIENT => true - ]); - return $status_event->status; - } -}
\ No newline at end of file + /** + * Wait for the server to send the status, and return it. + * + * @return object The status object, with integer $code, string $details, + * and array $metadata members + */ + public function getStatus() + { + $status_event = $this->call->startBatch([ + OP_RECV_STATUS_ON_CLIENT => true, + ]); + + return $status_event->status; + } +} diff --git a/src/php/lib/Grpc/UnaryCall.php b/src/php/lib/Grpc/UnaryCall.php index 97a10a40f4..b57903d6d0 100644 --- a/src/php/lib/Grpc/UnaryCall.php +++ b/src/php/lib/Grpc/UnaryCall.php @@ -31,35 +31,50 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + namespace Grpc; /** * Represents an active call that sends a single message and then gets a single * response. */ -class UnaryCall extends AbstractCall { - /** - * Start the call - * @param $arg The argument to send - * @param array $metadata Metadata to send with the call, if applicable - */ - public function start($arg, $metadata = array()) { - $event = $this->call->startBatch([ - OP_SEND_INITIAL_METADATA => $metadata, - OP_RECV_INITIAL_METADATA => true, - OP_SEND_MESSAGE => $arg->serialize(), - OP_SEND_CLOSE_FROM_CLIENT => true]); - $this->metadata = $event->metadata; - } +class UnaryCall extends AbstractCall +{ + /** + * Start the call. + * + * @param $data The data to send + * @param array $metadata Metadata to send with the call, if applicable + * @param array $options an array of options, possible keys: + * 'flags' => a number + */ + public function start($data, $metadata = [], $options = []) + { + $message_array = ['message' => $data->serialize()]; + if (isset($options['flags'])) { + $message_array['flags'] = $options['flags']; + } + $event = $this->call->startBatch([ + OP_SEND_INITIAL_METADATA => $metadata, + OP_RECV_INITIAL_METADATA => true, + OP_SEND_MESSAGE => $message_array, + OP_SEND_CLOSE_FROM_CLIENT => true, + ]); + $this->metadata = $event->metadata; + } + + /** + * Wait for the server to respond with data and a status. + * + * @return [response data, status] + */ + public function wait() + { + $event = $this->call->startBatch([ + OP_RECV_MESSAGE => true, + OP_RECV_STATUS_ON_CLIENT => true, + ]); - /** - * Wait for the server to respond with data and a status - * @return [response data, status] - */ - public function wait() { - $event = $this->call->startBatch([ - OP_RECV_MESSAGE => true, - OP_RECV_STATUS_ON_CLIENT => true]); - return array($this->deserializeResponse($event->message), $event->status); - } -}
\ No newline at end of file + return [$this->deserializeResponse($event->message), $event->status]; + } +} diff --git a/src/php/phpunit.xml b/src/php/phpunit.xml new file mode 100644 index 0000000000..52ea6e8f0e --- /dev/null +++ b/src/php/phpunit.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="tests/bootstrap.php" colors="true"> + <testsuites> + <testsuite name="grpc-unit-tests"> + <directory suffix="Test.php">tests/unit_tests</directory> + </testsuite> + <testsuite name="grpc-genereated-code-tests"> + <file>tests/generated_code/GeneratedCodeTest.php</file> + <file>tests/generated_code/GeneratedCodeWithCallbackTest.php</file> + </testsuite> + </testsuites> + <filter> + <whitelist> + <directory suffix=".php">lib/Grpc</directory> + </whitelist> + </filter> + <logging> + <log type="coverage-html" target="./log/codeCoverage" charset="UTF-8" + yui="true" highlight="true" + lowUpperBound="75" highLowerBound="95"/> + </logging> +</phpunit> diff --git a/src/php/tests/bootstrap.php b/src/php/tests/bootstrap.php new file mode 100644 index 0000000000..b61f2c40a5 --- /dev/null +++ b/src/php/tests/bootstrap.php @@ -0,0 +1,21 @@ +<?php +/* + * Copyright 2014 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +error_reporting(E_ALL | E_STRICT); +require dirname(__DIR__) . '/vendor/autoload.php'; +date_default_timezone_set('UTC'); diff --git a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php index a368dd4ee0..4a0bd6a1f1 100644 --- a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php +++ b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php @@ -31,82 +31,212 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php'); -require 'math.php'; -abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase { - /* These tests require that a server exporting the math service must be - * running on $GRPC_TEST_HOST */ - protected static $client; - protected static $timeout; - - public function testWaitForNotReady() { - $this->assertFalse(self::$client->waitForReady(1)); - } - - public function testWaitForReady() { - $this->assertTrue(self::$client->waitForReady(250000)); - } - - public function testGetTarget() { - $this->assertTrue(is_string(self::$client->getTarget())); - } - - public function testSimpleRequest() { - $div_arg = new math\DivArgs(); - $div_arg->setDividend(7); - $div_arg->setDivisor(4); - $call = self::$client->Div($div_arg); - $this->assertTrue(is_string($call->getPeer())); - list($response, $status) = $call->wait(); - $this->assertSame(1, $response->getQuotient()); - $this->assertSame(3, $response->getRemainder()); - $this->assertSame(\Grpc\STATUS_OK, $status->code); - } - - public function testServerStreaming() { - $fib_arg = new math\FibArgs(); - $fib_arg->setLimit(7); - $call = self::$client->Fib($fib_arg); - $this->assertTrue(is_string($call->getPeer())); - $result_array = iterator_to_array($call->responses()); - $extract_num = function($num){ - return $num->getNum(); - }; - $values = array_map($extract_num, $result_array); - $this->assertSame([1, 1, 2, 3, 5, 8, 13], $values); - $status = $call->getStatus(); - $this->assertSame(\Grpc\STATUS_OK, $status->code); - } - - public function testClientStreaming() { - $num_iter = function() { - for ($i = 0; $i < 7; $i++) { +require_once realpath(dirname(__FILE__).'/../../vendor/autoload.php'); +require_once dirname(__FILE__).'/math.php'; + +abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase +{ + /** + * These tests require that a server exporting the math service must be + * running on $GRPC_TEST_HOST. + */ + protected static $client; + protected static $timeout; + + public function testWaitForNotReady() + { + $this->assertFalse(self::$client->waitForReady(1)); + } + + public function testWaitForReady() + { + $this->assertTrue(self::$client->waitForReady(250000)); + } + + public function testAlreadyReady() + { + $this->assertTrue(self::$client->waitForReady(250000)); + $this->assertTrue(self::$client->waitForReady(100)); + } + + public function testGetTarget() + { + $this->assertTrue(is_string(self::$client->getTarget())); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testClose() + { + self::$client->close(); + $div_arg = new math\DivArgs(); + $call = self::$client->Div($div_arg); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidMetadata() + { + $div_arg = new math\DivArgs(); + $call = self::$client->Div($div_arg, [' ' => 'abc123']); + } + + public function testGetCallMetadata() + { + $div_arg = new math\DivArgs(); + $call = self::$client->Div($div_arg); + $this->assertTrue(is_array($call->getMetadata())); + } + + public function testTimeout() + { + $div_arg = new math\DivArgs(); + $call = self::$client->Div($div_arg, ['timeout' => 100]); + list($response, $status) = $call->wait(); + $this->assertSame(\Grpc\STATUS_DEADLINE_EXCEEDED, $status->code); + } + + public function testCancel() + { + $div_arg = new math\DivArgs(); + $call = self::$client->Div($div_arg); + $call->cancel(); + list($response, $status) = $call->wait(); + $this->assertSame(\Grpc\STATUS_CANCELLED, $status->code); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidMethodName() + { + $invalid_client = new DummyInvalidClient('host', []); + $div_arg = new math\DivArgs(); + $invalid_client->InvalidUnaryCall($div_arg); + } + + public function testWriteFlags() + { + $div_arg = new math\DivArgs(); + $div_arg->setDividend(7); + $div_arg->setDivisor(4); + $call = self::$client->Div($div_arg, [], + ['flags' => Grpc\WRITE_NO_COMPRESS]); + $this->assertTrue(is_string($call->getPeer())); + list($response, $status) = $call->wait(); + $this->assertSame(1, $response->getQuotient()); + $this->assertSame(3, $response->getRemainder()); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testWriteFlagsServerStreaming() + { + $fib_arg = new math\FibArgs(); + $fib_arg->setLimit(7); + $call = self::$client->Fib($fib_arg, [], + ['flags' => Grpc\WRITE_NO_COMPRESS]); + $result_array = iterator_to_array($call->responses()); + $status = $call->getStatus(); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testWriteFlagsClientStreaming() + { + $call = self::$client->Sum(); $num = new math\Num(); - $num->setNum($i); - yield $num; - } - }; - $call = self::$client->Sum($num_iter()); - $this->assertTrue(is_string($call->getPeer())); - list($response, $status) = $call->wait(); - $this->assertSame(21, $response->getNum()); - $this->assertSame(\Grpc\STATUS_OK, $status->code); - } - - public function testBidiStreaming() { - $call = self::$client->DivMany(); - $this->assertTrue(is_string($call->getPeer())); - for ($i = 0; $i < 7; $i++) { - $div_arg = new math\DivArgs(); - $div_arg->setDividend(2 * $i + 1); - $div_arg->setDivisor(2); - $call->write($div_arg); - $response = $call->read(); - $this->assertSame($i, $response->getQuotient()); - $this->assertSame(1, $response->getRemainder()); - } - $call->writesDone(); - $status = $call->getStatus(); - $this->assertSame(\Grpc\STATUS_OK, $status->code); - } + $num->setNum(1); + $call->write($num, ['flags' => Grpc\WRITE_NO_COMPRESS]); + list($response, $status) = $call->wait(); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testWriteFlagsBidiStreaming() + { + $call = self::$client->DivMany(); + $div_arg = new math\DivArgs(); + $div_arg->setDividend(7); + $div_arg->setDivisor(4); + $call->write($div_arg, ['flags' => Grpc\WRITE_NO_COMPRESS]); + $response = $call->read(); + $call->writesDone(); + $status = $call->getStatus(); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testSimpleRequest() + { + $div_arg = new math\DivArgs(); + $div_arg->setDividend(7); + $div_arg->setDivisor(4); + $call = self::$client->Div($div_arg); + $this->assertTrue(is_string($call->getPeer())); + list($response, $status) = $call->wait(); + $this->assertSame(1, $response->getQuotient()); + $this->assertSame(3, $response->getRemainder()); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testServerStreaming() + { + $fib_arg = new math\FibArgs(); + $fib_arg->setLimit(7); + $call = self::$client->Fib($fib_arg); + $this->assertTrue(is_string($call->getPeer())); + $result_array = iterator_to_array($call->responses()); + $extract_num = function ($num) { + return $num->getNum(); + }; + $values = array_map($extract_num, $result_array); + $this->assertSame([1, 1, 2, 3, 5, 8, 13], $values); + $status = $call->getStatus(); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testClientStreaming() + { + $call = self::$client->Sum(); + $this->assertTrue(is_string($call->getPeer())); + for ($i = 0; $i < 7; ++$i) { + $num = new math\Num(); + $num->setNum($i); + $call->write($num); + } + list($response, $status) = $call->wait(); + $this->assertSame(21, $response->getNum()); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testBidiStreaming() + { + $call = self::$client->DivMany(); + $this->assertTrue(is_string($call->getPeer())); + for ($i = 0; $i < 7; ++$i) { + $div_arg = new math\DivArgs(); + $div_arg->setDividend(2 * $i + 1); + $div_arg->setDivisor(2); + $call->write($div_arg); + $response = $call->read(); + $this->assertSame($i, $response->getQuotient()); + $this->assertSame(1, $response->getRemainder()); + } + $call->writesDone(); + $status = $call->getStatus(); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } +} + +class DummyInvalidClient extends \Grpc\BaseStub +{ + public function InvalidUnaryCall(\math\DivArgs $argument, + $metadata = [], + $options = []) + { + return $this->_simpleRequest('invalidMethodName', + $argument, + function () {}, + $metadata, + $options); + } } diff --git a/src/php/tests/generated_code/GeneratedCodeTest.php b/src/php/tests/generated_code/GeneratedCodeTest.php index a1a2ce81db..7043e8e1d1 100755 --- a/src/php/tests/generated_code/GeneratedCodeTest.php +++ b/src/php/tests/generated_code/GeneratedCodeTest.php @@ -31,11 +31,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -require 'AbstractGeneratedCodeTest.php'; +require_once dirname(__FILE__).'/AbstractGeneratedCodeTest.php'; -class GeneratedCodeTest extends AbstractGeneratedCodeTest { - public static function setUpBeforeClass() { - self::$client = new math\MathClient( +class GeneratedCodeTest extends AbstractGeneratedCodeTest +{ + public function setUp() + { + self::$client = new math\MathClient( getenv('GRPC_TEST_HOST'), []); - } + } + + public static function tearDownAfterClass() + { + self::$client->close(); + } } diff --git a/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php b/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php index 68f57d34ad..5a20e684c7 100644 --- a/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php +++ b/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php @@ -31,17 +31,25 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -require 'AbstractGeneratedCodeTest.php'; +require_once dirname(__FILE__).'/AbstractGeneratedCodeTest.php'; -class GeneratedCodeWithCallbackTest extends AbstractGeneratedCodeTest { - public static function setUpBeforeClass() { - self::$client = new math\MathClient( - getenv('GRPC_TEST_HOST'), ['update_metadata' => - function($a_hash, - $client = array()) { - $a_copy = $a_hash; - $a_copy['foo'] = ['bar']; - return $a_copy; - }]); - } +class GeneratedCodeWithCallbackTest extends AbstractGeneratedCodeTest +{ + public function setUp() + { + self::$client = new math\MathClient( + getenv('GRPC_TEST_HOST'), + ['update_metadata' => function ($a_hash, + $client = []) { + $a_copy = $a_hash; + $a_copy['foo'] = ['bar']; + + return $a_copy; + }]); + } + + public static function tearDownAfterClass() + { + self::$client->close(); + } } diff --git a/src/php/tests/generated_code/math_client.php b/src/php/tests/generated_code/math_client.php new file mode 100644 index 0000000000..76ccabc068 --- /dev/null +++ b/src/php/tests/generated_code/math_client.php @@ -0,0 +1,103 @@ +<?php +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +# Fix the following two lines to point to your installation +include 'vendor/autoload.php'; +include 'tests/generated_code/math.php'; + +function p($line) +{ + print("$line<br/>\n"); +} + +$host = 'localhost:50051'; +p("Connecting to host: $host"); +$client = new math\MathClient($host, []); +p('Client class: '.get_class($client)); +p(''); + +p('Running unary call test:'); +$dividend = 7; +$divisor = 4; +$div_arg = new math\DivArgs(); +$div_arg->setDividend($dividend); +$div_arg->setDivisor($divisor); +$call = $client->Div($div_arg); +p('Call peer: '.$call->getPeer()); +p("Dividing $dividend by $divisor"); +list($response, $status) = $call->wait(); +p('quotient = '.$response->getQuotient()); +p('remainder = '.$response->getRemainder()); +p(''); + +p('Running server streaming test:'); +$limit = 7; +$fib_arg = new math\FibArgs(); +$fib_arg->setLimit($limit); +$call = $client->Fib($fib_arg); +$result_array = iterator_to_array($call->responses()); +$result = ''; +foreach ($result_array as $num) { + $result .= ' '.$num->getNum(); +} +p("The first $limit Fibonacci numbers are:".$result); +p(''); + +p('Running client streaming test:'); +$call = $client->Sum(); +for ($i = 0; $i <= $limit; ++$i) { + $num = new math\Num(); + $num->setNum($i); + $call->write($num); +} +list($response, $status) = $call->wait(); +p(sprintf('The first %d positive integers sum to: %d', + $limit, $response->getNum())); +p(''); + +p('Running bidi-streaming test:'); +$call = $client->DivMany(); +for ($i = 0; $i < 7; ++$i) { + $div_arg = new math\DivArgs(); + $dividend = 2 * $i + 1; + $divisor = 3; + $div_arg->setDividend($dividend); + $div_arg->setDivisor($divisor); + $call->write($div_arg); + p("client writing: $dividend / $divisor"); + $response = $call->read(); + p(sprintf('server writing: quotient = %d, remainder = %d', + $response->getQuotient(), $response->getRemainder())); +} +$call->writesDone(); diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php index bd15ee4303..3019866561 100755 --- a/src/php/tests/interop/interop_client.php +++ b/src/php/tests/interop/interop_client.php @@ -31,348 +31,488 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php'); +require_once realpath(dirname(__FILE__).'/../../vendor/autoload.php'); require 'empty.php'; -require 'message_set.php'; require 'messages.php'; require 'test.php'; +use Google\Auth\CredentialsLoader; +use Google\Auth\ApplicationDefaultCredentials; +use GuzzleHttp\ClientInterface; /** * Assertion function that always exits with an error code if the assertion is - * falsy + * falsy. + * * @param $value Assertion value. Should be true. * @param $error_message Message to display if the assertion is false */ -function hardAssert($value, $error_message) { - if (!$value) { - echo $error_message . "\n"; - exit(1); - } +function hardAssert($value, $error_message) +{ + if (!$value) { + echo $error_message."\n"; + exit(1); + } } /** * Run the empty_unary test. - * Passes when run against the Node server as of 2015-04-30 + * * @param $stub Stub object that has service methods */ -function emptyUnary($stub) { - list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait(); - hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); - hardAssert($result !== null, 'Call completed with a null response'); +function emptyUnary($stub) +{ + list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait(); + hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); + hardAssert($result !== null, 'Call completed with a null response'); } /** * Run the large_unary test. - * Passes when run against the C++/Node server as of 2015-04-30 + * * @param $stub Stub object that has service methods */ -function largeUnary($stub) { - performLargeUnary($stub); +function largeUnary($stub) +{ + performLargeUnary($stub); } /** - * Shared code between large unary test and auth test + * Shared code between large unary test and auth test. + * * @param $stub Stub object that has service methods * @param $fillUsername boolean whether to fill result with username * @param $fillOauthScope boolean whether to fill result with oauth scope */ -function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false) { - $request_len = 271828; - $response_len = 314159; - - $request = new grpc\testing\SimpleRequest(); - $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); - $request->setResponseSize($response_len); - $payload = new grpc\testing\Payload(); - $payload->setType(grpc\testing\PayloadType::COMPRESSABLE); - $payload->setBody(str_repeat("\0", $request_len)); - $request->setPayload($payload); - $request->setFillUsername($fillUsername); - $request->setFillOauthScope($fillOauthScope); - - list($result, $status) = $stub->UnaryCall($request)->wait(); - hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); - hardAssert($result !== null, 'Call returned a null response'); - $payload = $result->getPayload(); - hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, +function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false, + $metadata = []) +{ + $request_len = 271828; + $response_len = 314159; + + $request = new grpc\testing\SimpleRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + $request->setResponseSize($response_len); + $payload = new grpc\testing\Payload(); + $payload->setType(grpc\testing\PayloadType::COMPRESSABLE); + $payload->setBody(str_repeat("\0", $request_len)); + $request->setPayload($payload); + $request->setFillUsername($fillUsername); + $request->setFillOauthScope($fillOauthScope); + + list($result, $status) = $stub->UnaryCall($request, $metadata)->wait(); + hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); + hardAssert($result !== null, 'Call returned a null response'); + $payload = $result->getPayload(); + hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, 'Payload had the wrong type'); - hardAssert(strlen($payload->getBody()) === $response_len, + hardAssert(strlen($payload->getBody()) === $response_len, 'Payload had the wrong length'); - hardAssert($payload->getBody() === str_repeat("\0", $response_len), + hardAssert($payload->getBody() === str_repeat("\0", $response_len), 'Payload had the wrong content'); - return $result; + + return $result; } /** * Run the service account credentials auth test. - * Passes when run against the cloud server as of 2015-04-30 + * * @param $stub Stub object that has service methods * @param $args array command line args */ -function serviceAccountCreds($stub, $args) { - if (!array_key_exists('oauth_scope', $args)) { - throw new Exception('Missing oauth scope'); - } - $jsonKey = json_decode( - file_get_contents(getenv(Google\Auth\CredentialsLoader::ENV_VAR)), - true); - $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true); - hardAssert($result->getUsername() == $jsonKey['client_email'], +function serviceAccountCreds($stub, $args) +{ + if (!array_key_exists('oauth_scope', $args)) { + throw new Exception('Missing oauth scope'); + } + $jsonKey = json_decode( + file_get_contents(getenv(CredentialsLoader::ENV_VAR)), + true); + $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true); + hardAssert($result->getUsername() == $jsonKey['client_email'], 'invalid email returned'); - hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false, + hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false, 'invalid oauth scope returned'); } /** * Run the compute engine credentials auth test. - * Has not been run from gcloud as of 2015-05-05 + * Has not been run from gcloud as of 2015-05-05. + * * @param $stub Stub object that has service methods * @param $args array command line args */ -function computeEngineCreds($stub, $args) { - if (!array_key_exists('oauth_scope', $args)) { - throw new Exception('Missing oauth scope'); - } - if (!array_key_exists('default_service_account', $args)) { - throw new Exception('Missing default_service_account'); - } - $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true); - hardAssert($args['default_service_account'] == $result->getUsername(), +function computeEngineCreds($stub, $args) +{ + if (!array_key_exists('oauth_scope', $args)) { + throw new Exception('Missing oauth scope'); + } + if (!array_key_exists('default_service_account', $args)) { + throw new Exception('Missing default_service_account'); + } + $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true); + hardAssert($args['default_service_account'] == $result->getUsername(), 'invalid email returned'); } /** * Run the jwt token credentials auth test. - * Passes when run against the cloud server as of 2015-05-12 + * * @param $stub Stub object that has service methods * @param $args array command line args */ -function jwtTokenCreds($stub, $args) { - $jsonKey = json_decode( - file_get_contents(getenv(Google\Auth\CredentialsLoader::ENV_VAR)), - true); - $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true); - hardAssert($result->getUsername() == $jsonKey['client_email'], +function jwtTokenCreds($stub, $args) +{ + $jsonKey = json_decode( + file_get_contents(getenv(CredentialsLoader::ENV_VAR)), + true); + $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true); + hardAssert($result->getUsername() == $jsonKey['client_email'], 'invalid email returned'); } /** - * Run the client_streaming test. - * Passes when run against the Node server as of 2015-04-30 + * Run the oauth2_auth_token auth test. + * * @param $stub Stub object that has service methods + * @param $args array command line args */ -function clientStreaming($stub) { - $request_lengths = array(27182, 8, 1828, 45904); +function oauth2AuthToken($stub, $args) +{ + $jsonKey = json_decode( + file_get_contents(getenv(CredentialsLoader::ENV_VAR)), + true); + $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true); + hardAssert($result->getUsername() == $jsonKey['client_email'], + 'invalid email returned'); +} - $requests = array_map( - function($length) { - $request = new grpc\testing\StreamingInputCallRequest(); - $payload = new grpc\testing\Payload(); - $payload->setBody(str_repeat("\0", $length)); - $request->setPayload($payload); - return $request; - }, $request_lengths); +/** + * Run the per_rpc_creds auth test. + * + * @param $stub Stub object that has service methods + * @param $args array command line args + */ +function perRpcCreds($stub, $args) +{ + $jsonKey = json_decode( + file_get_contents(getenv(CredentialsLoader::ENV_VAR)), + true); + $auth_credentials = ApplicationDefaultCredentials::getCredentials( + $args['oauth_scope'] + ); + $token = $auth_credentials->fetchAuthToken(); + $metadata = [CredentialsLoader::AUTH_METADATA_KEY => [sprintf('%s %s', + $token['token_type'], + $token['access_token'])]]; + $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true, + $metadata); + hardAssert($result->getUsername() == $jsonKey['client_email'], + 'invalid email returned'); +} - list($result, $status) = $stub->StreamingInputCall($requests)->wait(); - hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); - hardAssert($result->getAggregatedPayloadSize() === 74922, +/** + * Run the client_streaming test. + * + * @param $stub Stub object that has service methods + */ +function clientStreaming($stub) +{ + $request_lengths = [27182, 8, 1828, 45904]; + + $requests = array_map( + function ($length) { + $request = new grpc\testing\StreamingInputCallRequest(); + $payload = new grpc\testing\Payload(); + $payload->setBody(str_repeat("\0", $length)); + $request->setPayload($payload); + + return $request; + }, $request_lengths); + + $call = $stub->StreamingInputCall(); + foreach ($requests as $request) { + $call->write($request); + } + list($result, $status) = $call->wait(); + hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); + hardAssert($result->getAggregatedPayloadSize() === 74922, 'aggregated_payload_size was incorrect'); } /** * Run the server_streaming test. - * Passes when run against the Node server as of 2015-04-30 + * * @param $stub Stub object that has service methods. */ -function serverStreaming($stub) { - $sizes = array(31415, 9, 2653, 58979); +function serverStreaming($stub) +{ + $sizes = [31415, 9, 2653, 58979]; - $request = new grpc\testing\StreamingOutputCallRequest(); - $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); - foreach($sizes as $size) { - $response_parameters = new grpc\testing\ResponseParameters(); - $response_parameters->setSize($size); - $request->addResponseParameters($response_parameters); - } - - $call = $stub->StreamingOutputCall($request); - $i = 0; - foreach($call->responses() as $value) { - hardAssert($i < 4, 'Too many responses'); - $payload = $value->getPayload(); - hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, - 'Payload ' . $i . ' had the wrong type'); - hardAssert(strlen($payload->getBody()) === $sizes[$i], - 'Response ' . $i . ' had the wrong length'); - $i += 1; - } - hardAssert($call->getStatus()->code === Grpc\STATUS_OK, + $request = new grpc\testing\StreamingOutputCallRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + foreach ($sizes as $size) { + $response_parameters = new grpc\testing\ResponseParameters(); + $response_parameters->setSize($size); + $request->addResponseParameters($response_parameters); + } + + $call = $stub->StreamingOutputCall($request); + $i = 0; + foreach ($call->responses() as $value) { + hardAssert($i < 4, 'Too many responses'); + $payload = $value->getPayload(); + hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, + 'Payload '.$i.' had the wrong type'); + hardAssert(strlen($payload->getBody()) === $sizes[$i], + 'Response '.$i.' had the wrong length'); + $i += 1; + } + hardAssert($call->getStatus()->code === Grpc\STATUS_OK, 'Call did not complete successfully'); } /** * Run the ping_pong test. - * Passes when run against the Node server as of 2015-04-30 + * + * @param $stub Stub object that has service methods. + */ +function pingPong($stub) +{ + $request_lengths = [27182, 8, 1828, 45904]; + $response_lengths = [31415, 9, 2653, 58979]; + + $call = $stub->FullDuplexCall(); + for ($i = 0; $i < 4; ++$i) { + $request = new grpc\testing\StreamingOutputCallRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + $response_parameters = new grpc\testing\ResponseParameters(); + $response_parameters->setSize($response_lengths[$i]); + $request->addResponseParameters($response_parameters); + $payload = new grpc\testing\Payload(); + $payload->setBody(str_repeat("\0", $request_lengths[$i])); + $request->setPayload($payload); + + $call->write($request); + $response = $call->read(); + + hardAssert($response !== null, 'Server returned too few responses'); + $payload = $response->getPayload(); + hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, + 'Payload '.$i.' had the wrong type'); + hardAssert(strlen($payload->getBody()) === $response_lengths[$i], + 'Payload '.$i.' had the wrong length'); + } + $call->writesDone(); + hardAssert($call->read() === null, 'Server returned too many responses'); + hardAssert($call->getStatus()->code === Grpc\STATUS_OK, + 'Call did not complete successfully'); +} + +/** + * Run the empty_stream test. + * * @param $stub Stub object that has service methods. */ -function pingPong($stub) { - $request_lengths = array(27182, 8, 1828, 45904); - $response_lengths = array(31415, 9, 2653, 58979); +function emptyStream($stub) +{ + $call = $stub->FullDuplexCall(); + $call->writesDone(); + hardAssert($call->read() === null, 'Server returned too many responses'); + hardAssert($call->getStatus()->code === Grpc\STATUS_OK, + 'Call did not complete successfully'); +} - $call = $stub->FullDuplexCall(); - for($i = 0; $i < 4; $i++) { +/** + * Run the cancel_after_begin test. + * + * @param $stub Stub object that has service methods. + */ +function cancelAfterBegin($stub) +{ + $call = $stub->StreamingInputCall(); + $call->cancel(); + list($result, $status) = $call->wait(); + hardAssert($status->code === Grpc\STATUS_CANCELLED, + 'Call status was not CANCELLED'); +} + +/** + * Run the cancel_after_first_response test. + * + * @param $stub Stub object that has service methods. + */ +function cancelAfterFirstResponse($stub) +{ + $call = $stub->FullDuplexCall(); $request = new grpc\testing\StreamingOutputCallRequest(); $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); $response_parameters = new grpc\testing\ResponseParameters(); - $response_parameters->setSize($response_lengths[$i]); + $response_parameters->setSize(31415); $request->addResponseParameters($response_parameters); $payload = new grpc\testing\Payload(); - $payload->setBody(str_repeat("\0", $request_lengths[$i])); + $payload->setBody(str_repeat("\0", 27182)); $request->setPayload($payload); $call->write($request); $response = $call->read(); - hardAssert($response !== null, 'Server returned too few responses'); - $payload = $response->getPayload(); - hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, - 'Payload ' . $i . ' had the wrong type'); - hardAssert(strlen($payload->getBody()) === $response_lengths[$i], - 'Payload ' . $i . ' had the wrong length'); - } - $call->writesDone(); - hardAssert($call->read() === null, 'Server returned too many responses'); - hardAssert($call->getStatus()->code === Grpc\STATUS_OK, - 'Call did not complete successfully'); -} - -/** - * Run the cancel_after_first_response test. - * Passes when run against the Node server as of 2015-04-30 - * @param $stub Stub object that has service methods. - */ -function cancelAfterFirstResponse($stub) { - $call = $stub->FullDuplexCall(); - $request = new grpc\testing\StreamingOutputCallRequest(); - $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); - $response_parameters = new grpc\testing\ResponseParameters(); - $response_parameters->setSize(31415); - $request->addResponseParameters($response_parameters); - $payload = new grpc\testing\Payload(); - $payload->setBody(str_repeat("\0", 27182)); - $request->setPayload($payload); - - $call->write($request); - $response = $call->read(); - - $call->cancel(); - hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED, + $call->cancel(); + hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED, 'Call status was not CANCELLED'); } -function timeoutOnSleepingServer($stub) { - $call = $stub->FullDuplexCall(array('timeout' => 1000)); - $request = new grpc\testing\StreamingOutputCallRequest(); - $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); - $response_parameters = new grpc\testing\ResponseParameters(); - $response_parameters->setSize(8); - $request->addResponseParameters($response_parameters); - $payload = new grpc\testing\Payload(); - $payload->setBody(str_repeat("\0", 9)); - $request->setPayload($payload); - - $call->write($request); - $response = $call->read(); - - hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED, +function timeoutOnSleepingServer($stub) +{ + $call = $stub->FullDuplexCall(['timeout' => 1000]); + $request = new grpc\testing\StreamingOutputCallRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + $response_parameters = new grpc\testing\ResponseParameters(); + $response_parameters->setSize(8); + $request->addResponseParameters($response_parameters); + $payload = new grpc\testing\Payload(); + $payload->setBody(str_repeat("\0", 9)); + $request->setPayload($payload); + + $call->write($request); + $response = $call->read(); + + hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED, 'Call status was not DEADLINE_EXCEEDED'); } -$args = getopt('', array('server_host:', 'server_port:', 'test_case:', - 'server_host_override:', 'oauth_scope:', - 'default_service_account:')); -if (!array_key_exists('server_host', $args) || - !array_key_exists('server_port', $args) || - !array_key_exists('test_case', $args)) { - throw new Exception('Missing argument'); +$args = getopt('', ['server_host:', 'server_port:', 'test_case:', + 'use_tls::', 'use_test_ca::', + 'server_host_override:', 'oauth_scope:', + 'default_service_account:', ]); +if (!array_key_exists('server_host', $args)) { + throw new Exception('Missing argument: --server_host is required'); +} +if (!array_key_exists('server_port', $args)) { + throw new Exception('Missing argument: --server_port is required'); +} +if (!array_key_exists('test_case', $args)) { + throw new Exception('Missing argument: --test_case is required'); } if ($args['server_port'] == 443) { - $server_address = $args['server_host']; + $server_address = $args['server_host']; } else { - $server_address = $args['server_host'] . ':' . $args['server_port']; + $server_address = $args['server_host'].':'.$args['server_port']; +} + +$test_case = $args['test_case']; + +$host_override = 'foo.test.google.fr'; +if (array_key_exists('server_host_override', $args)) { + $host_override = $args['server_host_override']; +} + +$use_tls = false; +if (array_key_exists('use_tls', $args) && + $args['use_tls'] != 'false') { + $use_tls = true; +} + +$use_test_ca = false; +if (array_key_exists('use_test_ca', $args) && + $args['use_test_ca'] != 'false') { + $use_test_ca = true; } -if (!array_key_exists('server_host_override', $args)) { - $args['server_host_override'] = 'foo.test.google.fr'; +$opts = []; + +if ($use_tls) { + if ($use_test_ca) { + $ssl_credentials = Grpc\Credentials::createSsl( + file_get_contents(dirname(__FILE__).'/../data/ca.pem')); + } else { + $ssl_credentials = Grpc\Credentials::createSsl(); + } + $opts['credentials'] = $ssl_credentials; + $opts['grpc.ssl_target_name_override'] = $host_override; } -$ssl_cert_file = getenv('SSL_CERT_FILE'); -if (!$ssl_cert_file) { - $ssl_cert_file = dirname(__FILE__) . '/../data/ca.pem'; +if (in_array($test_case, ['service_account_creds', + 'compute_engine_creds', 'jwt_token_creds', ])) { + if ($test_case == 'jwt_token_creds') { + $auth_credentials = ApplicationDefaultCredentials::getCredentials(); + } else { + $auth_credentials = ApplicationDefaultCredentials::getCredentials( + $args['oauth_scope'] + ); + } + $opts['update_metadata'] = $auth_credentials->getUpdateMetadataFunc(); } -$credentials = Grpc\Credentials::createSsl(file_get_contents($ssl_cert_file)); - -$opts = [ - 'grpc.ssl_target_name_override' => $args['server_host_override'], - 'credentials' => $credentials, - ]; - -if (in_array($args['test_case'], array( - 'service_account_creds', - 'compute_engine_creds', - 'jwt_token_creds'))) { - if ($args['test_case'] == 'jwt_token_creds') { - $auth = Google\Auth\ApplicationDefaultCredentials::getCredentials(); - } else { - $auth = Google\Auth\ApplicationDefaultCredentials::getCredentials( - $args['oauth_scope']); - } - $opts['update_metadata'] = $auth->getUpdateMetadataFunc(); +if ($test_case == 'oauth2_auth_token') { + $auth_credentials = ApplicationDefaultCredentials::getCredentials( + $args['oauth_scope'] + ); + $token = $auth_credentials->fetchAuthToken(); + $update_metadata = + function ($metadata, + $authUri = null, + ClientInterface $client = null) use ($token) { + $metadata_copy = $metadata; + $metadata_copy[CredentialsLoader::AUTH_METADATA_KEY] = + [sprintf('%s %s', + $token['token_type'], + $token['access_token'])]; + + return $metadata_copy; + }; + $opts['update_metadata'] = $update_metadata; } $stub = new grpc\testing\TestServiceClient($server_address, $opts); echo "Connecting to $server_address\n"; -echo "Running test case $args[test_case]\n"; - -switch ($args['test_case']) { - case 'empty_unary': - emptyUnary($stub); - break; - case 'large_unary': - largeUnary($stub); - break; - case 'client_streaming': - clientStreaming($stub); - break; - case 'server_streaming': - serverStreaming($stub); - break; - case 'ping_pong': - pingPong($stub); - break; - case 'cancel_after_first_response': - cancelAfterFirstResponse($stub); - break; - case 'timeout_on_sleeping_server': - timeoutOnSleepingServer($stub); - break; - case 'service_account_creds': - serviceAccountCreds($stub, $args); - break; - case 'compute_engine_creds': - computeEngineCreds($stub, $args); - break; - case 'jwt_token_creds': - jwtTokenCreds($stub, $args); - break; - case 'cancel_after_begin': - // Currently unimplementable with the current API design - // Specifically, in the ClientStreamingCall->start() method, the - // messages are sent immediately after metadata is sent. There is - // currently no way to cancel before messages are sent. - default: - exit(1); +echo "Running test case $test_case\n"; + +switch ($test_case) { + case 'empty_unary': + emptyUnary($stub); + break; + case 'large_unary': + largeUnary($stub); + break; + case 'client_streaming': + clientStreaming($stub); + break; + case 'server_streaming': + serverStreaming($stub); + break; + case 'ping_pong': + pingPong($stub); + break; + case 'empty_stream': + emptyStream($stub); + break; + case 'cancel_after_begin': + cancelAfterBegin($stub); + break; + case 'cancel_after_first_response': + cancelAfterFirstResponse($stub); + break; + case 'timeout_on_sleeping_server': + timeoutOnSleepingServer($stub); + break; + case 'service_account_creds': + serviceAccountCreds($stub, $args); + break; + case 'compute_engine_creds': + computeEngineCreds($stub, $args); + break; + case 'jwt_token_creds': + jwtTokenCreds($stub, $args); + break; + case 'oauth2_auth_token': + oauth2AuthToken($stub, $args); + break; + case 'per_rpc_creds': + perRpcCreds($stub, $args); + break; + default: + echo "Unsupported test case $test_case\n"; + exit(1); } diff --git a/src/php/tests/interop/message_set.php b/src/php/tests/interop/message_set.php deleted file mode 100755 index c35c6d74b2..0000000000 --- a/src/php/tests/interop/message_set.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0 -// Source: net/proto2/bridge/proto/message_set.proto -// Date: 2014-12-03 22:02:20 - -namespace proto2\bridge { - - class MessageSet extends \DrSlump\Protobuf\Message { - - - /** @var \Closure[] */ - protected static $__extensions = array(); - - public static function descriptor() - { - $descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'proto2.bridge.MessageSet'); - - foreach (self::$__extensions as $cb) { - $descriptor->addField($cb(), true); - } - - return $descriptor; - } - } -} - diff --git a/src/php/tests/interop/test.proto b/src/php/tests/interop/test.proto index 39c08f3544..0d169e7f64 100644 --- a/src/php/tests/interop/test.proto +++ b/src/php/tests/interop/test.proto @@ -44,7 +44,6 @@ service TestService { rpc EmptyCall(grpc.testing.EmptyMessage) returns (grpc.testing.EmptyMessage); // One request followed by one response. - // TODO(Issue 527): Describe required server behavior. rpc UnaryCall(SimpleRequest) returns (SimpleResponse); // One request followed by a sequence of responses (streamed download). diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php index caff15ee11..3b697b50c3 100755 --- a/src/php/tests/unit_tests/CallTest.php +++ b/src/php/tests/unit_tests/CallTest.php @@ -31,56 +31,64 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -class CallTest extends PHPUnit_Framework_TestCase{ - static $server; - static $port; +class CallTest extends PHPUnit_Framework_TestCase +{ + public static $server; + public static $port; - public static function setUpBeforeClass() { - self::$server = new Grpc\Server([]); - self::$port = self::$server->addHttp2Port('0.0.0.0:0'); - } + public static function setUpBeforeClass() + { + self::$server = new Grpc\Server([]); + self::$port = self::$server->addHttp2Port('0.0.0.0:0'); + } - public function setUp() { - $this->channel = new Grpc\Channel('localhost:' . self::$port, []); - $this->call = new Grpc\Call($this->channel, - '/foo', - Grpc\Timeval::infFuture()); - } + public function setUp() + { + $this->channel = new Grpc\Channel('localhost:'.self::$port, []); + $this->call = new Grpc\Call($this->channel, + '/foo', + Grpc\Timeval::infFuture()); + } - public function testAddEmptyMetadata() { - $batch = [ - Grpc\OP_SEND_INITIAL_METADATA => [] - ]; - $result = $this->call->startBatch($batch); - $this->assertTrue($result->send_metadata); - } + public function testAddEmptyMetadata() + { + $batch = [ + Grpc\OP_SEND_INITIAL_METADATA => [], + ]; + $result = $this->call->startBatch($batch); + $this->assertTrue($result->send_metadata); + } - public function testAddSingleMetadata() { - $batch = [ - Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value']] - ]; - $result = $this->call->startBatch($batch); - $this->assertTrue($result->send_metadata); - } + public function testAddSingleMetadata() + { + $batch = [ + Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value']], + ]; + $result = $this->call->startBatch($batch); + $this->assertTrue($result->send_metadata); + } - public function testAddMultiValueMetadata() { - $batch = [ - Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value1', 'value2']] - ]; - $result = $this->call->startBatch($batch); - $this->assertTrue($result->send_metadata); - } + public function testAddMultiValueMetadata() + { + $batch = [ + Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value1', 'value2']], + ]; + $result = $this->call->startBatch($batch); + $this->assertTrue($result->send_metadata); + } - public function testAddSingleAndMultiValueMetadata() { - $batch = [ - Grpc\OP_SEND_INITIAL_METADATA => ['key1' => ['value1'], - 'key2' => ['value2', 'value3']] - ]; - $result = $this->call->startBatch($batch); - $this->assertTrue($result->send_metadata); - } + public function testAddSingleAndMultiValueMetadata() + { + $batch = [ + Grpc\OP_SEND_INITIAL_METADATA => ['key1' => ['value1'], + 'key2' => ['value2', 'value3'], ], + ]; + $result = $this->call->startBatch($batch); + $this->assertTrue($result->send_metadata); + } - public function testGetPeer() { - $this->assertTrue(is_string($this->call->getPeer())); - } + public function testGetPeer() + { + $this->assertTrue(is_string($this->call->getPeer())); + } } diff --git a/src/php/tests/unit_tests/EndToEndTest.php b/src/php/tests/unit_tests/EndToEndTest.php index 4c0cf91d51..5a38262451 100755 --- a/src/php/tests/unit_tests/EndToEndTest.php +++ b/src/php/tests/unit_tests/EndToEndTest.php @@ -31,172 +31,228 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -class EndToEndTest extends PHPUnit_Framework_TestCase{ - public function setUp() { - $this->server = new Grpc\Server([]); - $this->port = $this->server->addHttp2Port('0.0.0.0:0'); - $this->channel = new Grpc\Channel('localhost:' . $this->port, []); - $this->server->start(); - } - - public function tearDown() { - unset($this->channel); - unset($this->server); - } - - public function testSimpleRequestBody() { - $deadline = Grpc\Timeval::infFuture(); - $status_text = 'xyz'; - $call = new Grpc\Call($this->channel, - 'dummy_method', - $deadline); - - $event = $call->startBatch([ - Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_CLOSE_FROM_CLIENT => true - ]); - - $this->assertTrue($event->send_metadata); - $this->assertTrue($event->send_close); - - $event = $this->server->requestCall(); - $this->assertSame('dummy_method', $event->method); - $server_call = $event->call; - - $event = $server_call->startBatch([ - Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_STATUS_FROM_SERVER => [ - 'metadata' => [], - 'code' => Grpc\STATUS_OK, - 'details' => $status_text - ], - Grpc\OP_RECV_CLOSE_ON_SERVER => true - ]); - - $this->assertTrue($event->send_metadata); - $this->assertTrue($event->send_status); - $this->assertFalse($event->cancelled); - - $event = $call->startBatch([ - Grpc\OP_RECV_INITIAL_METADATA => true, - Grpc\OP_RECV_STATUS_ON_CLIENT => true - ]); - - $status = $event->status; - $this->assertSame([], $status->metadata); - $this->assertSame(Grpc\STATUS_OK, $status->code); - $this->assertSame($status_text, $status->details); - - unset($call); - unset($server_call); - } - - public function testClientServerFullRequestResponse() { - $deadline = Grpc\Timeval::infFuture(); - $req_text = 'client_server_full_request_response'; - $reply_text = 'reply:client_server_full_request_response'; - $status_text = 'status:client_server_full_response_text'; - - $call = new Grpc\Call($this->channel, - 'dummy_method', - $deadline); - - $event = $call->startBatch([ - Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, - Grpc\OP_SEND_MESSAGE => $req_text - ]); - - $this->assertTrue($event->send_metadata); - $this->assertTrue($event->send_close); - $this->assertTrue($event->send_message); - - $event = $this->server->requestCall(); - $this->assertSame('dummy_method', $event->method); - $server_call = $event->call; - - $event = $server_call->startBatch([ - Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_MESSAGE => $reply_text, - Grpc\OP_SEND_STATUS_FROM_SERVER => [ - 'metadata' => [], - 'code' => Grpc\STATUS_OK, - 'details' => $status_text - ], - Grpc\OP_RECV_MESSAGE => true, - Grpc\OP_RECV_CLOSE_ON_SERVER => true, - ]); - - $this->assertTrue($event->send_metadata); - $this->assertTrue($event->send_status); - $this->assertTrue($event->send_message); - $this->assertFalse($event->cancelled); - $this->assertSame($req_text, $event->message); - - $event = $call->startBatch([ - Grpc\OP_RECV_INITIAL_METADATA => true, - Grpc\OP_RECV_MESSAGE => true, - Grpc\OP_RECV_STATUS_ON_CLIENT => true, - ]); - - $this->assertSame([], $event->metadata); - $this->assertSame($reply_text, $event->message); - $status = $event->status; - $this->assertSame([], $status->metadata); - $this->assertSame(Grpc\STATUS_OK, $status->code); - $this->assertSame($status_text, $status->details); - - unset($call); - unset($server_call); - } - - public function testGetTarget() { - $this->assertTrue(is_string($this->channel->getTarget())); - } - - public function testGetConnectivityState() { - $this->assertTrue($this->channel->getConnectivityState() == Grpc\CHANNEL_IDLE); - } - - public function testWatchConnectivityStateFailed() { - $idle_state = $this->channel->getConnectivityState(true); - $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); - - $now = Grpc\Timeval::now(); - $delta = new Grpc\Timeval(1); - $deadline = $now->add($delta); - - $this->assertFalse($this->channel->watchConnectivityState( +class EndToEndTest extends PHPUnit_Framework_TestCase +{ + public function setUp() + { + $this->server = new Grpc\Server([]); + $this->port = $this->server->addHttp2Port('0.0.0.0:0'); + $this->channel = new Grpc\Channel('localhost:'.$this->port, []); + $this->server->start(); + } + + public function tearDown() + { + unset($this->channel); + unset($this->server); + } + + public function testSimpleRequestBody() + { + $deadline = Grpc\Timeval::infFuture(); + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text, + ], + Grpc\OP_RECV_CLOSE_ON_SERVER => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertFalse($event->cancelled); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testMessageWriteFlags() + { + $deadline = Grpc\Timeval::infFuture(); + $req_text = 'message_write_flags_test'; + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_MESSAGE => ['message' => $req_text, + 'flags' => Grpc\WRITE_NO_COMPRESS, ], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text, + ], + ]); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testClientServerFullRequestResponse() + { + $deadline = Grpc\Timeval::infFuture(); + $req_text = 'client_server_full_request_response'; + $reply_text = 'reply:client_server_full_request_response'; + $status_text = 'status:client_server_full_response_text'; + + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + Grpc\OP_SEND_MESSAGE => ['message' => $req_text], + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + $this->assertTrue($event->send_message); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_MESSAGE => ['message' => $reply_text], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text, + ], + Grpc\OP_RECV_MESSAGE => true, + Grpc\OP_RECV_CLOSE_ON_SERVER => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertTrue($event->send_message); + $this->assertFalse($event->cancelled); + $this->assertSame($req_text, $event->message); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_MESSAGE => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $this->assertSame([], $event->metadata); + $this->assertSame($reply_text, $event->message); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testGetTarget() + { + $this->assertTrue(is_string($this->channel->getTarget())); + } + + public function testGetConnectivityState() + { + $this->assertTrue($this->channel->getConnectivityState() == Grpc\CHANNEL_IDLE); + } + + public function testWatchConnectivityStateFailed() + { + $idle_state = $this->channel->getConnectivityState(); + $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); + + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(500000); // should timeout + $deadline = $now->add($delta); + + $this->assertFalse($this->channel->watchConnectivityState( $idle_state, $deadline)); - } + } - public function testWatchConnectivityStateSuccess() { - $idle_state = $this->channel->getConnectivityState(true); - $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); + public function testWatchConnectivityStateSuccess() + { + $idle_state = $this->channel->getConnectivityState(true); + $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); - $now = Grpc\Timeval::now(); - $delta = new Grpc\Timeval(3000000); // should finish well before - $deadline = $now->add($delta); + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(3000000); // should finish well before + $deadline = $now->add($delta); - $this->assertTrue($this->channel->watchConnectivityState( + $this->assertTrue($this->channel->watchConnectivityState( $idle_state, $deadline)); - $new_state = $this->channel->getConnectivityState(); - $this->assertTrue($idle_state != $new_state); - } + $new_state = $this->channel->getConnectivityState(); + $this->assertTrue($idle_state != $new_state); + } - public function testWatchConnectivityStateDoNothing() { - $idle_state = $this->channel->getConnectivityState(); - $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); + public function testWatchConnectivityStateDoNothing() + { + $idle_state = $this->channel->getConnectivityState(); + $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); - $now = Grpc\Timeval::now(); - $delta = new Grpc\Timeval(100000); - $deadline = $now->add($delta); + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(100000); + $deadline = $now->add($delta); - $this->assertFalse($this->channel->watchConnectivityState( + $this->assertFalse($this->channel->watchConnectivityState( $idle_state, $deadline)); - $new_state = $this->channel->getConnectivityState(); - $this->assertTrue($new_state == Grpc\CHANNEL_IDLE); - } + $new_state = $this->channel->getConnectivityState(); + $this->assertTrue($new_state == Grpc\CHANNEL_IDLE); + } } diff --git a/src/php/tests/unit_tests/SecureEndToEndTest.php b/src/php/tests/unit_tests/SecureEndToEndTest.php index 60341b983d..e66bde376c 100755 --- a/src/php/tests/unit_tests/SecureEndToEndTest.php +++ b/src/php/tests/unit_tests/SecureEndToEndTest.php @@ -31,139 +31,193 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -class SecureEndToEndTest extends PHPUnit_Framework_TestCase{ - public function setUp() { - $credentials = Grpc\Credentials::createSsl( - file_get_contents(dirname(__FILE__) . '/../data/ca.pem')); - $server_credentials = Grpc\ServerCredentials::createSsl( - null, - file_get_contents(dirname(__FILE__) . '/../data/server1.key'), - file_get_contents(dirname(__FILE__) . '/../data/server1.pem')); - $this->server = new Grpc\Server(); - $this->port = $this->server->addSecureHttp2Port('0.0.0.0:0', +class SecureEndToEndTest extends PHPUnit_Framework_TestCase +{ + public function setUp() + { + $credentials = Grpc\Credentials::createSsl( + file_get_contents(dirname(__FILE__).'/../data/ca.pem')); + $server_credentials = Grpc\ServerCredentials::createSsl( + null, + file_get_contents(dirname(__FILE__).'/../data/server1.key'), + file_get_contents(dirname(__FILE__).'/../data/server1.pem')); + $this->server = new Grpc\Server(); + $this->port = $this->server->addSecureHttp2Port('0.0.0.0:0', $server_credentials); - $this->server->start(); - $this->host_override = 'foo.test.google.fr'; - $this->channel = new Grpc\Channel( - 'localhost:' . $this->port, - [ + $this->server->start(); + $this->host_override = 'foo.test.google.fr'; + $this->channel = new Grpc\Channel( + 'localhost:'.$this->port, + [ 'grpc.ssl_target_name_override' => $this->host_override, 'grpc.default_authority' => $this->host_override, - 'credentials' => $credentials - ]); - } - - public function tearDown() { - unset($this->channel); - unset($this->server); - } - - public function testSimpleRequestBody() { - $deadline = Grpc\Timeval::infFuture(); - $status_text = 'xyz'; - $call = new Grpc\Call($this->channel, - 'dummy_method', - $deadline, - $this->host_override); - - $event = $call->startBatch([ - Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_CLOSE_FROM_CLIENT => true - ]); - - $this->assertTrue($event->send_metadata); - $this->assertTrue($event->send_close); - - $event = $this->server->requestCall(); - $this->assertSame('dummy_method', $event->method); - $server_call = $event->call; - - $event = $server_call->startBatch([ - Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_STATUS_FROM_SERVER => [ - 'metadata' => [], - 'code' => Grpc\STATUS_OK, - 'details' => $status_text - ], - Grpc\OP_RECV_CLOSE_ON_SERVER => true - ]); - - $this->assertTrue($event->send_metadata); - $this->assertTrue($event->send_status); - $this->assertFalse($event->cancelled); - - $event = $call->startBatch([ - Grpc\OP_RECV_INITIAL_METADATA => true, - Grpc\OP_RECV_STATUS_ON_CLIENT => true - ]); - - $this->assertSame([], $event->metadata); - $status = $event->status; - $this->assertSame([], $status->metadata); - $this->assertSame(Grpc\STATUS_OK, $status->code); - $this->assertSame($status_text, $status->details); - - unset($call); - unset($server_call); - } - - public function testClientServerFullRequestResponse() { - $deadline = Grpc\Timeval::infFuture(); - $req_text = 'client_server_full_request_response'; - $reply_text = 'reply:client_server_full_request_response'; - $status_text = 'status:client_server_full_response_text'; - - $call = new Grpc\Call($this->channel, - 'dummy_method', - $deadline, - $this->host_override); - - $event = $call->startBatch([ - Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, - Grpc\OP_SEND_MESSAGE => $req_text - ]); - - $this->assertTrue($event->send_metadata); - $this->assertTrue($event->send_close); - $this->assertTrue($event->send_message); - - $event = $this->server->requestCall(); - $this->assertSame('dummy_method', $event->method); - $server_call = $event->call; - - $event = $server_call->startBatch([ - Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_MESSAGE => $reply_text, - Grpc\OP_SEND_STATUS_FROM_SERVER => [ - 'metadata' => [], - 'code' => Grpc\STATUS_OK, - 'details' => $status_text - ], - Grpc\OP_RECV_MESSAGE => true, - Grpc\OP_RECV_CLOSE_ON_SERVER => true, - ]); - - $this->assertTrue($event->send_metadata); - $this->assertTrue($event->send_status); - $this->assertTrue($event->send_message); - $this->assertFalse($event->cancelled); - $this->assertSame($req_text, $event->message); - - $event = $call->startBatch([ - Grpc\OP_RECV_INITIAL_METADATA => true, - Grpc\OP_RECV_MESSAGE => true, - Grpc\OP_RECV_STATUS_ON_CLIENT => true, - ]); - - $this->assertSame([], $event->metadata); - $this->assertSame($reply_text, $event->message); - $status = $event->status; - $this->assertSame([], $status->metadata); - $this->assertSame(Grpc\STATUS_OK, $status->code); - $this->assertSame($status_text, $status->details); - - unset($call); - unset($server_call); - } + 'credentials' => $credentials, + ] + ); + } + + public function tearDown() + { + unset($this->channel); + unset($this->server); + } + + public function testSimpleRequestBody() + { + $deadline = Grpc\Timeval::infFuture(); + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline, + $this->host_override); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text, + ], + Grpc\OP_RECV_CLOSE_ON_SERVER => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertFalse($event->cancelled); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $this->assertSame([], $event->metadata); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testMessageWriteFlags() + { + $deadline = Grpc\Timeval::infFuture(); + $req_text = 'message_write_flags_test'; + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline, + $this->host_override); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_MESSAGE => ['message' => $req_text, + 'flags' => Grpc\WRITE_NO_COMPRESS, ], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text, + ], + ]); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $this->assertSame([], $event->metadata); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testClientServerFullRequestResponse() + { + $deadline = Grpc\Timeval::infFuture(); + $req_text = 'client_server_full_request_response'; + $reply_text = 'reply:client_server_full_request_response'; + $status_text = 'status:client_server_full_response_text'; + + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline, + $this->host_override); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + Grpc\OP_SEND_MESSAGE => ['message' => $req_text], + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + $this->assertTrue($event->send_message); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_MESSAGE => ['message' => $reply_text], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text, + ], + Grpc\OP_RECV_MESSAGE => true, + Grpc\OP_RECV_CLOSE_ON_SERVER => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertTrue($event->send_message); + $this->assertFalse($event->cancelled); + $this->assertSame($req_text, $event->message); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_MESSAGE => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $this->assertSame([], $event->metadata); + $this->assertSame($reply_text, $event->message); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } } diff --git a/src/php/tests/unit_tests/TimevalTest.php b/src/php/tests/unit_tests/TimevalTest.php index 7b4925cad6..1d2a8d303e 100755 --- a/src/php/tests/unit_tests/TimevalTest.php +++ b/src/php/tests/unit_tests/TimevalTest.php @@ -31,56 +31,64 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -class TimevalTest extends PHPUnit_Framework_TestCase{ - public function testCompareSame() { - $zero = Grpc\Timeval::zero(); - $this->assertSame(0, Grpc\Timeval::compare($zero, $zero)); - } +class TimevalTest extends PHPUnit_Framework_TestCase +{ + public function testCompareSame() + { + $zero = Grpc\Timeval::zero(); + $this->assertSame(0, Grpc\Timeval::compare($zero, $zero)); + } - public function testPastIsLessThanZero() { - $zero = Grpc\Timeval::zero(); - $past = Grpc\Timeval::infPast(); - $this->assertLessThan(0, Grpc\Timeval::compare($past, $zero)); - $this->assertGreaterThan(0, Grpc\Timeval::compare($zero, $past)); - } + public function testPastIsLessThanZero() + { + $zero = Grpc\Timeval::zero(); + $past = Grpc\Timeval::infPast(); + $this->assertLessThan(0, Grpc\Timeval::compare($past, $zero)); + $this->assertGreaterThan(0, Grpc\Timeval::compare($zero, $past)); + } - public function testFutureIsGreaterThanZero() { - $zero = Grpc\Timeval::zero(); - $future = Grpc\Timeval::infFuture(); - $this->assertLessThan(0, Grpc\Timeval::compare($zero, $future)); - $this->assertGreaterThan(0, Grpc\Timeval::compare($future, $zero)); - } + public function testFutureIsGreaterThanZero() + { + $zero = Grpc\Timeval::zero(); + $future = Grpc\Timeval::infFuture(); + $this->assertLessThan(0, Grpc\Timeval::compare($zero, $future)); + $this->assertGreaterThan(0, Grpc\Timeval::compare($future, $zero)); + } - /** - * @depends testFutureIsGreaterThanZero - */ - public function testNowIsBetweenZeroAndFuture() { - $zero = Grpc\Timeval::zero(); - $future = Grpc\Timeval::infFuture(); - $now = Grpc\Timeval::now(); - $this->assertLessThan(0, Grpc\Timeval::compare($zero, $now)); - $this->assertLessThan(0, Grpc\Timeval::compare($now, $future)); - } + /** + * @depends testFutureIsGreaterThanZero + */ + public function testNowIsBetweenZeroAndFuture() + { + $zero = Grpc\Timeval::zero(); + $future = Grpc\Timeval::infFuture(); + $now = Grpc\Timeval::now(); + $this->assertLessThan(0, Grpc\Timeval::compare($zero, $now)); + $this->assertLessThan(0, Grpc\Timeval::compare($now, $future)); + } - public function testNowAndAdd() { - $now = Grpc\Timeval::now(); - $delta = new Grpc\Timeval(1000); - $deadline = $now->add($delta); - $this->assertGreaterThan(0, Grpc\Timeval::compare($deadline, $now)); - } + public function testNowAndAdd() + { + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(1000); + $deadline = $now->add($delta); + $this->assertGreaterThan(0, Grpc\Timeval::compare($deadline, $now)); + } - public function testNowAndSubtract() { - $now = Grpc\Timeval::now(); - $delta = new Grpc\Timeval(1000); - $deadline = $now->subtract($delta); - $this->assertLessThan(0, Grpc\Timeval::compare($deadline, $now)); - } + public function testNowAndSubtract() + { + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(1000); + $deadline = $now->subtract($delta); + $this->assertLessThan(0, Grpc\Timeval::compare($deadline, $now)); + } - public function testAddAndSubtract() { - $now = Grpc\Timeval::now(); - $delta = new Grpc\Timeval(1000); - $deadline = $now->add($delta); - $back_to_now = $deadline->subtract($delta); - $this->assertSame(0, Grpc\Timeval::compare($back_to_now, $now)); - } + public function testAddAndSubtract() + { + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(1000); + $deadline = $now->add($delta); + $back_to_now = $deadline->subtract($delta); + $this->assertSame(0, Grpc\Timeval::compare($back_to_now, $now)); + } } diff --git a/src/python/README.md b/src/python/README.md index a21deb33ef..a3133b5aad 100644 --- a/src/python/README.md +++ b/src/python/README.md @@ -4,11 +4,11 @@ The Python facility of gRPC. Status ------- -Alpha : Ready for early adopters +Beta : Core behavior well-used and proven; bugs lurk off the beaten path. PREREQUISITES ------------- -- Python 2.7, virtualenv, pip +- python-virtualenv, python-pip, python3-pip, python-dev, python3-dev - [homebrew][] on Mac OS X. These simplify the installation of the gRPC C core. INSTALLATION @@ -16,10 +16,10 @@ INSTALLATION **Linux (Debian):** -Add [Debian testing][] to your `sources.list` file. Example: +Add [Debian jessie-backports][] to your `sources.list` file. Example: ```sh -echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" | \ +echo "deb http://http.debian.net/debian jessie-backports main" | \ sudo tee -a /etc/apt/sources.list ``` @@ -92,4 +92,4 @@ $ ../../tools/distrib/python/submit.py [gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install [Quick Start]:http://www.grpc.io/docs/tutorials/basic/python.html [detailed example]:http://www.grpc.io/docs/installation/python.html -[Debian testing]:https://www.debian.org/releases/stretch/ +[Debian jessie-backports]:http://backports.debian.org/Instructions/ diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py index 89c0fbf0f3..8a2f2d6283 100644 --- a/src/python/grpcio/commands.py +++ b/src/python/grpcio/commands.py @@ -64,7 +64,7 @@ class SphinxDocumentation(setuptools.Command): import sphinx.apidoc metadata = self.distribution.metadata src_dir = os.path.join( - os.getcwd(), self.distribution.package_dir['grpc']) + os.getcwd(), self.distribution.package_dir[''], 'grpc') sys.path.append(src_dir) sphinx.apidoc.main([ '', '--force', '--full', '-H', metadata.name, '-A', metadata.author, diff --git a/src/python/grpcio/grpc/_adapter/_c/types.h b/src/python/grpcio/grpc/_adapter/_c/types.h index ec0687a9fd..31fd470d36 100644 --- a/src/python/grpcio/grpc/_adapter/_c/types.h +++ b/src/python/grpcio/grpc/_adapter/_c/types.h @@ -112,6 +112,8 @@ void pygrpc_Call_dealloc(Call *self); PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs); PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs); PyObject *pygrpc_Call_peer(Call *self); +PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args, + PyObject *kwargs); extern PyTypeObject pygrpc_Call_type; diff --git a/src/python/grpcio/grpc/_adapter/_c/types/call.c b/src/python/grpcio/grpc/_adapter/_c/types/call.c index 42a50151f6..5604aba39d 100644 --- a/src/python/grpcio/grpc/_adapter/_c/types/call.c +++ b/src/python/grpcio/grpc/_adapter/_c/types/call.c @@ -43,6 +43,8 @@ PyMethodDef pygrpc_Call_methods[] = { {"start_batch", (PyCFunction)pygrpc_Call_start_batch, METH_KEYWORDS, ""}, {"cancel", (PyCFunction)pygrpc_Call_cancel, METH_KEYWORDS, ""}, {"peer", (PyCFunction)pygrpc_Call_peer, METH_NOARGS, ""}, + {"set_credentials", (PyCFunction)pygrpc_Call_set_credentials, METH_KEYWORDS, + ""}, {NULL} }; const char pygrpc_Call_doc[] = "See grpc._adapter._types.Call."; @@ -169,3 +171,16 @@ PyObject *pygrpc_Call_peer(Call *self) { gpr_free(peer); return py_peer; } +PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args, + PyObject *kwargs) { + ClientCredentials *creds; + grpc_call_error errcode; + static char *keywords[] = {"creds", NULL}; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "O!:set_credentials", keywords, + &pygrpc_ClientCredentials_type, &creds)) { + return NULL; + } + errcode = grpc_call_set_credentials(self->c_call, creds->c_creds); + return PyInt_FromLong(errcode); +} diff --git a/src/python/grpcio/grpc/_adapter/_intermediary_low.py b/src/python/grpcio/grpc/_adapter/_intermediary_low.py index 06358e72bc..e2feec6ffb 100644 --- a/src/python/grpcio/grpc/_adapter/_intermediary_low.py +++ b/src/python/grpcio/grpc/_adapter/_intermediary_low.py @@ -59,6 +59,7 @@ from grpc._adapter import _types _IGNORE_ME_TAG = object() Code = _types.StatusCode +WriteFlags = _types.OpWriteFlags class Status(collections.namedtuple('Status', ['code', 'details'])): @@ -125,9 +126,9 @@ class Call(object): ], _TagAdapter(finish_tag, Event.Kind.FINISH)) return err0 if err0 != _types.CallError.OK else err1 if err1 != _types.CallError.OK else err2 if err2 != _types.CallError.OK else _types.CallError.OK - def write(self, message, tag): + def write(self, message, tag, flags): return self._internal.start_batch([ - _types.OpArgs.send_message(message, 0) + _types.OpArgs.send_message(message, flags) ], _TagAdapter(tag, Event.Kind.WRITE_ACCEPTED)) def complete(self, tag): @@ -163,6 +164,12 @@ class Call(object): def cancel(self): return self._internal.cancel() + def peer(self): + return self._internal.peer() + + def set_credentials(self, creds): + return self._internal.set_credentials(creds._internal) + class Channel(object): """Adapter from old _low.Channel interface to new _low.Channel.""" diff --git a/src/python/grpcio/grpc/_adapter/_low.py b/src/python/grpcio/grpc/_adapter/_low.py index 3859ebb0e2..70ceb2a911 100644 --- a/src/python/grpcio/grpc/_adapter/_low.py +++ b/src/python/grpcio/grpc/_adapter/_low.py @@ -78,6 +78,9 @@ class Call(_types.Call): def peer(self): return self.call.peer() + def set_credentials(self, creds): + return self.call.set_credentials(creds) + class Channel(_types.Channel): diff --git a/src/python/grpcio/grpc/_adapter/fore.py b/src/python/grpcio/grpc/_adapter/fore.py index daa41e8bde..acdd69c420 100644 --- a/src/python/grpcio/grpc/_adapter/fore.py +++ b/src/python/grpcio/grpc/_adapter/fore.py @@ -56,7 +56,7 @@ class _LowWrite(enum.Enum): def _write(call, rpc_state, payload): serialized_payload = rpc_state.serializer(payload) if rpc_state.write.low is _LowWrite.OPEN: - call.write(serialized_payload, call) + call.write(serialized_payload, call, 0) rpc_state.write.low = _LowWrite.ACTIVE else: rpc_state.write.pending.append(serialized_payload) @@ -164,7 +164,7 @@ class ForeLink(base_interfaces.ForeLink, activated.Activated): if rpc_state.write.pending: serialized_payload = rpc_state.write.pending.pop(0) - call.write(serialized_payload, call) + call.write(serialized_payload, call, 0) elif rpc_state.write.high is _common.HighWrite.CLOSED: _status(call, rpc_state) else: diff --git a/src/python/grpcio/grpc/_adapter/rear.py b/src/python/grpcio/grpc/_adapter/rear.py index fd6f45f7a7..17fa47f746 100644 --- a/src/python/grpcio/grpc/_adapter/rear.py +++ b/src/python/grpcio/grpc/_adapter/rear.py @@ -78,7 +78,7 @@ class _RPCState(object): def _write(operation_id, call, outstanding, write_state, serialized_payload): if write_state.low is _LowWrite.OPEN: - call.write(serialized_payload, operation_id) + call.write(serialized_payload, operation_id, 0) outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) write_state.low = _LowWrite.ACTIVE elif write_state.low is _LowWrite.ACTIVE: @@ -144,7 +144,7 @@ class RearLink(base_interfaces.RearLink, activated.Activated): if event.write_accepted: if rpc_state.common.write.pending: rpc_state.call.write( - rpc_state.common.write.pending.pop(0), operation_id) + rpc_state.common.write.pending.pop(0), operation_id, 0) rpc_state.outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) elif rpc_state.common.write.high is _common.HighWrite.CLOSED: rpc_state.call.complete(operation_id) @@ -263,7 +263,7 @@ class RearLink(base_interfaces.RearLink, activated.Activated): low_state = _LowWrite.OPEN else: serialized_payload = request_serializer(payload) - call.write(serialized_payload, operation_id) + call.write(serialized_payload, operation_id, 0) outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) low_state = _LowWrite.ACTIVE diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx index 4349786b3a..ed037b660a 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx @@ -29,6 +29,7 @@ cimport cpython +from grpc._cython._cygrpc cimport grpc from grpc._cython._cygrpc cimport records @@ -49,7 +50,7 @@ cdef class Call: cpython.Py_INCREF(operation_tag) return grpc.grpc_call_start_batch( self.c_call, cy_operations.c_ops, cy_operations.c_nops, - <cpython.PyObject *>operation_tag) + <cpython.PyObject *>operation_tag, NULL) def cancel(self, grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE, @@ -67,9 +68,10 @@ cdef class Call: raise TypeError("expected details to be str or bytes") if error_code != grpc.GRPC_STATUS__DO_NOT_USE: self.references.append(details) - return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details) + return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details, + NULL) else: - return grpc.grpc_call_cancel(self.c_call) + return grpc.grpc_call_cancel(self.c_call, NULL) def __dealloc__(self): if self.c_call != NULL: diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx index b20313818d..b52ddb6bcd 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx @@ -30,6 +30,7 @@ from grpc._cython._cygrpc cimport call from grpc._cython._cygrpc cimport completion_queue from grpc._cython._cygrpc cimport credentials +from grpc._cython._cygrpc cimport grpc from grpc._cython._cygrpc cimport records @@ -49,15 +50,17 @@ cdef class Channel: else: raise TypeError("expected target to be str or bytes") if client_credentials is None: - self.c_channel = grpc.grpc_channel_create(target, c_arguments) + self.c_channel = grpc.grpc_insecure_channel_create(target, c_arguments, + NULL) else: self.c_channel = grpc.grpc_secure_channel_create( - client_credentials.c_credentials, target, c_arguments) + client_credentials.c_credentials, target, c_arguments, NULL) self.references.append(client_credentials) self.references.append(target) self.references.append(arguments) - def create_call(self, completion_queue.CompletionQueue queue not None, + def create_call(self, call.Call parent, int flags, + completion_queue.CompletionQueue queue not None, method, host, records.Timespec deadline not None): if queue.is_shutting_down: raise ValueError("queue must not be shutting down or shutdown") @@ -75,8 +78,13 @@ cdef class Channel: raise TypeError("expected host to be str or bytes") cdef call.Call operation_call = call.Call() operation_call.references = [self, method, host, queue] + cdef grpc.grpc_call *parent_call = NULL + if parent is not None: + parent_call = parent.c_call operation_call.c_call = grpc.grpc_channel_create_call( - self.c_channel, queue.c_completion_queue, method, host, deadline.c_time) + self.c_channel, parent_call, flags, + queue.c_completion_queue, method, host, deadline.c_time, + NULL) return operation_call def __dealloc__(self): diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx index 886d85360a..a7a265eab7 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx @@ -30,6 +30,7 @@ cimport cpython from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport grpc from grpc._cython._cygrpc cimport records import threading @@ -39,7 +40,7 @@ import time cdef class CompletionQueue: def __cinit__(self): - self.c_completion_queue = grpc.grpc_completion_queue_create() + self.c_completion_queue = grpc.grpc_completion_queue_create(NULL) self.is_shutting_down = False self.is_shutdown = False self.poll_condition = threading.Condition() @@ -48,7 +49,8 @@ cdef class CompletionQueue: def poll(self, records.Timespec deadline=None): # We name this 'poll' to avoid problems with CPython's expectations for # 'special' methods (like next and __next__). - cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future + cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future( + grpc.GPR_CLOCK_REALTIME) cdef records.OperationTag tag = None cdef object user_tag = None cdef call.Call operation_call = None @@ -66,7 +68,7 @@ cdef class CompletionQueue: self.is_polling = True with nogil: event = grpc.grpc_completion_queue_next( - self.c_completion_queue, c_deadline) + self.c_completion_queue, c_deadline, NULL) with self.poll_condition: self.is_polling = False self.poll_condition.notify() diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx index dc40a7a611..608207f0a2 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx @@ -27,6 +27,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from grpc._cython._cygrpc cimport grpc from grpc._cython._cygrpc cimport records @@ -81,13 +82,11 @@ def client_credentials_ssl(pem_root_certificates, credentials.references.append(pem_root_certificates) if ssl_pem_key_cert_pair is not None: credentials.c_credentials = grpc.grpc_ssl_credentials_create( - c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair - ) + c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair, NULL) credentials.references.append(ssl_pem_key_cert_pair) else: credentials.c_credentials = grpc.grpc_ssl_credentials_create( - c_pem_root_certificates, NULL - ) + c_pem_root_certificates, NULL, NULL) def client_credentials_composite_credentials( ClientCredentials credentials_1 not None, @@ -96,18 +95,20 @@ def client_credentials_composite_credentials( raise ValueError("passed credentials must both be valid") cdef ClientCredentials credentials = ClientCredentials() credentials.c_credentials = grpc.grpc_composite_credentials_create( - credentials_1.c_credentials, credentials_2.c_credentials) + credentials_1.c_credentials, credentials_2.c_credentials, NULL) credentials.references.append(credentials_1) credentials.references.append(credentials_2) return credentials -def client_credentials_compute_engine(): +def client_credentials_google_compute_engine(): cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_compute_engine_credentials_create() + credentials.c_credentials = ( + grpc.grpc_google_compute_engine_credentials_create(NULL)) return credentials #TODO rename to something like client_credentials_service_account_jwt_access. -def client_credentials_jwt(json_key, records.Timespec token_lifetime not None): +def client_credentials_service_account_jwt_access( + json_key, records.Timespec token_lifetime not None): if isinstance(json_key, bytes): pass elif isinstance(json_key, basestring): @@ -115,12 +116,13 @@ def client_credentials_jwt(json_key, records.Timespec token_lifetime not None): else: raise TypeError("expected json_key to be str or bytes") cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_service_account_jwt_access_credentials_create( - json_key, token_lifetime.c_time) + credentials.c_credentials = ( + grpc.grpc_service_account_jwt_access_credentials_create( + json_key, token_lifetime.c_time, NULL)) credentials.references.append(json_key) return credentials -def client_credentials_refresh_token(json_refresh_token): +def client_credentials_google_refresh_token(json_refresh_token): if isinstance(json_refresh_token, bytes): pass elif isinstance(json_refresh_token, basestring): @@ -128,12 +130,12 @@ def client_credentials_refresh_token(json_refresh_token): else: raise TypeError("expected json_refresh_token to be str or bytes") cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_refresh_token_credentials_create( - json_refresh_token) + credentials.c_credentials = grpc.grpc_google_refresh_token_credentials_create( + json_refresh_token, NULL) credentials.references.append(json_refresh_token) return credentials -def client_credentials_iam(authorization_token, authority_selector): +def client_credentials_google_iam(authorization_token, authority_selector): if isinstance(authorization_token, bytes): pass elif isinstance(authorization_token, basestring): @@ -147,13 +149,14 @@ def client_credentials_iam(authorization_token, authority_selector): else: raise TypeError("expected authority_selector to be str or bytes") cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_iam_credentials_create( - authorization_token, authority_selector) + credentials.c_credentials = grpc.grpc_google_iam_credentials_create( + authorization_token, authority_selector, NULL) credentials.references.append(authorization_token) credentials.references.append(authority_selector) return credentials -def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs): +def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs, + bint force_client_auth): if pem_root_certs is None: pass elif isinstance(pem_root_certs, bytes): @@ -181,7 +184,6 @@ def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs): (<records.SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair) credentials.c_credentials = grpc.grpc_ssl_server_credentials_create( pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, - credentials.c_ssl_pem_key_cert_pairs_count - ) + credentials.c_ssl_pem_key_cert_pairs_count, force_client_auth, NULL) return credentials diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd index 8b46972490..62d40e7a58 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd @@ -64,15 +64,25 @@ cdef extern from "grpc/support/port_platform.h": cdef extern from "grpc/support/time.h": + ctypedef enum gpr_clock_type: + GPR_CLOCK_MONOTONIC + GPR_CLOCK_REALTIME + GPR_CLOCK_PRECISE + GPR_TIMESPAN + ctypedef struct gpr_timespec: libc.time.time_t seconds "tv_sec" int nanoseconds "tv_nsec" + gpr_clock_type clock_type + + gpr_timespec gpr_time_0(gpr_clock_type type) + gpr_timespec gpr_inf_future(gpr_clock_type type) + gpr_timespec gpr_inf_past(gpr_clock_type type) - cdef gpr_timespec gpr_time_0 - cdef gpr_timespec gpr_inf_future - cdef gpr_timespec gpr_inf_past + gpr_timespec gpr_now(gpr_clock_type clock) - gpr_timespec gpr_now() + gpr_timespec gpr_convert_clock_type(gpr_timespec t, + gpr_clock_type target_clock) cdef extern from "grpc/status.h": @@ -255,38 +265,44 @@ cdef extern from "grpc/grpc.h": void grpc_init() void grpc_shutdown() - grpc_completion_queue *grpc_completion_queue_create() + grpc_completion_queue *grpc_completion_queue_create(void *reserved) grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, - gpr_timespec deadline) nogil + gpr_timespec deadline, + void *reserved) nogil void grpc_completion_queue_shutdown(grpc_completion_queue *cq) void grpc_completion_queue_destroy(grpc_completion_queue *cq) grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, - size_t nops, void *tag) - grpc_call_error grpc_call_cancel(grpc_call *call) + size_t nops, void *tag, void *reserved) + grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved) grpc_call_error grpc_call_cancel_with_status(grpc_call *call, grpc_status_code status, - const char *description) + const char *description, + void *reserved) void grpc_call_destroy(grpc_call *call) - grpc_channel *grpc_channel_create(const char *target, - const grpc_channel_args *args) + grpc_channel *grpc_insecure_channel_create(const char *target, + const grpc_channel_args *args, + void *reserved) grpc_call *grpc_channel_create_call(grpc_channel *channel, + grpc_call *parent_call, + gpr_uint32 propagation_mask, grpc_completion_queue *completion_queue, const char *method, const char *host, - gpr_timespec deadline) + gpr_timespec deadline, void *reserved) void grpc_channel_destroy(grpc_channel *channel) - grpc_server *grpc_server_create(const grpc_channel_args *args) + grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) grpc_call_error grpc_server_request_call( grpc_server *server, grpc_call **call, grpc_call_details *details, grpc_metadata_array *request_metadata, grpc_completion_queue *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void *tag_new) void grpc_server_register_completion_queue(grpc_server *server, - grpc_completion_queue *cq) - int grpc_server_add_http2_port(grpc_server *server, const char *addr) + grpc_completion_queue *cq, + void *reserved) + int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) void grpc_server_start(grpc_server *server) void grpc_server_shutdown_and_notify( grpc_server *server, grpc_completion_queue *cq, void *tag) @@ -306,22 +322,27 @@ cdef extern from "grpc/grpc_security.h": grpc_credentials *grpc_google_default_credentials_create() grpc_credentials *grpc_ssl_credentials_create( - const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, + void *reserved) grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1, - grpc_credentials *creds2) - grpc_credentials *grpc_compute_engine_credentials_create() - grpc_credentials *grpc_service_account_jwt_access_credentials_create(const char *json_key, - gpr_timespec token_lifetime) - grpc_credentials *grpc_refresh_token_credentials_create( - const char *json_refresh_token) - grpc_credentials *grpc_iam_credentials_create(const char *authorization_token, - const char *authority_selector) + grpc_credentials *creds2, + void *reserved) + grpc_credentials *grpc_google_compute_engine_credentials_create( + void *reserved) + grpc_credentials *grpc_service_account_jwt_access_credentials_create( + const char *json_key, + gpr_timespec token_lifetime, void *reserved) + grpc_credentials *grpc_google_refresh_token_credentials_create( + const char *json_refresh_token, void *reserved) + grpc_credentials *grpc_google_iam_credentials_create( + const char *authorization_token, const char *authority_selector, + void *reserved) void grpc_credentials_release(grpc_credentials *creds) grpc_channel *grpc_secure_channel_create( grpc_credentials *creds, const char *target, - const grpc_channel_args *args) + const grpc_channel_args *args, void *reserved) ctypedef struct grpc_server_credentials: # We don't care about the internals (and in fact don't know them) @@ -330,7 +351,7 @@ cdef extern from "grpc/grpc_security.h": grpc_server_credentials *grpc_ssl_server_credentials_create( const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs) + size_t num_key_cert_pairs, int force_client_auth, void *reserved) void grpc_server_credentials_release(grpc_server_credentials *creds) int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx index 4814769fd2..8edee09c2d 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx @@ -87,28 +87,38 @@ cdef class Timespec: def __cinit__(self, time): if time is None: - self.c_time = grpc.gpr_now() + self.c_time = grpc.gpr_now(grpc.GPR_CLOCK_REALTIME) elif isinstance(time, float): if time == float("+inf"): - self.c_time = grpc.gpr_inf_future + self.c_time = grpc.gpr_inf_future(grpc.GPR_CLOCK_REALTIME) elif time == float("-inf"): - self.c_time = grpc.gpr_inf_past + self.c_time = grpc.gpr_inf_past(grpc.GPR_CLOCK_REALTIME) else: self.c_time.seconds = time self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9 + self.c_time.clock_type = grpc.GPR_CLOCK_REALTIME else: raise TypeError("expected time to be float") @property def seconds(self): - return self.c_time.seconds + # TODO(atash) ensure that everywhere a Timespec is created that it's + # converted to GPR_CLOCK_REALTIME then and not every time someone wants to + # read values off in Python. + cdef grpc.gpr_timespec real_time = ( + grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME)) + return real_time.seconds @property def nanoseconds(self): - return self.c_time.nanoseconds + cdef grpc.gpr_timespec real_time = ( + grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME)) + return real_time.nanoseconds def __float__(self): - return <double>self.c_time.seconds + <double>self.c_time.nanoseconds / 1e9 + cdef grpc.gpr_timespec real_time = ( + grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME)) + return <double>real_time.seconds + <double>real_time.nanoseconds / 1e9 infinite_future = Timespec(float("+inf")) infinite_past = Timespec(float("-inf")) @@ -339,13 +349,16 @@ cdef class _MetadataIterator: self.i = 0 self.metadata = metadata + def __iter__(self): + return self + def __next__(self): if self.i < len(self.metadata): result = self.metadata[self.i] self.i = self.i + 1 return result else: - raise StopIteration() + raise StopIteration cdef class Metadata: @@ -536,13 +549,16 @@ cdef class _OperationsIterator: self.i = 0 self.operations = operations + def __iter__(self): + return self + def __next__(self): if self.i < len(self.operations): result = self.operations[self.i] self.i = self.i + 1 return result else: - raise StopIteration() + raise StopIteration cdef class Operations: diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx index dcf9d38337..6d20d2910c 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx @@ -32,6 +32,7 @@ cimport cpython from grpc._cython._cygrpc cimport call from grpc._cython._cygrpc cimport completion_queue from grpc._cython._cygrpc cimport credentials +from grpc._cython._cygrpc cimport grpc from grpc._cython._cygrpc cimport records import time @@ -46,7 +47,7 @@ cdef class Server: if arguments is not None: c_arguments = &arguments.c_args self.references.append(arguments) - self.c_server = grpc.grpc_server_create(c_arguments) + self.c_server = grpc.grpc_server_create(c_arguments, NULL) self.is_started = False self.is_shutting_down = False self.is_shutdown = False @@ -78,7 +79,7 @@ cdef class Server: if self.is_started: raise ValueError("cannot register completion queues after start") grpc.grpc_server_register_completion_queue( - self.c_server, queue.c_completion_queue) + self.c_server, queue.c_completion_queue, NULL) self.registered_completion_queues.append(queue) def start(self): @@ -103,7 +104,7 @@ cdef class Server: return grpc.grpc_server_add_secure_http2_port( self.c_server, address, server_credentials.c_credentials) else: - return grpc.grpc_server_add_http2_port(self.c_server, address) + return grpc.grpc_server_add_insecure_http2_port(self.c_server, address) def shutdown(self, completion_queue.CompletionQueue queue not None, tag): cdef records.OperationTag operation_tag diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx index f4d9661580..1ef2997db5 100644 --- a/src/python/grpcio/grpc/_cython/cygrpc.pyx +++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx @@ -78,11 +78,13 @@ client_credentials_google_default = ( client_credentials_ssl = credentials.client_credentials_ssl client_credentials_composite_credentials = ( credentials.client_credentials_composite_credentials) -client_credentials_compute_engine = ( - credentials.client_credentials_compute_engine) -client_credentials_jwt = credentials.client_credentials_jwt -client_credentials_refresh_token = credentials.client_credentials_refresh_token -client_credentials_iam = credentials.client_credentials_iam +client_credentials_google_compute_engine = ( + credentials.client_credentials_google_compute_engine) +client_credentials_jwt_access = ( + credentials.client_credentials_service_account_jwt_access) +client_credentials_refresh_token = ( + credentials.client_credentials_google_refresh_token) +client_credentials_google_iam = credentials.client_credentials_google_iam server_credentials_ssl = credentials.server_credentials_ssl CompletionQueue = completion_queue.CompletionQueue diff --git a/src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py b/src/python/grpcio/grpc/_links/_constants.py index 8674666418..117fc5a639 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py +++ b/src/python/grpcio/grpc/_links/_constants.py @@ -27,20 +27,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""One of the tests of the Face layer of RPC Framework.""" +"""Constants for use within this package.""" -import unittest +from grpc._adapter import _intermediary_low +from grpc.beta import interfaces as beta_interfaces -from grpc_test.framework.face import _test_case -from grpc_test.framework.face.testing import blocking_invocation_inline_service_test_case as test_case +LOW_STATUS_CODE_TO_HIGH_STATUS_CODE = { + low: high for low, high in zip( + _intermediary_low.Code, beta_interfaces.StatusCode) +} - -class BlockingInvocationInlineServiceTest( - _test_case.FaceTestCase, - test_case.BlockingInvocationInlineServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) +HIGH_STATUS_CODE_TO_LOW_STATUS_CODE = { + high: low for low, high in LOW_STATUS_CODE_TO_HIGH_STATUS_CODE.items() +} diff --git a/src/python/grpcio/grpc/_links/invocation.py b/src/python/grpcio/grpc/_links/invocation.py index 1676fe7941..67ef86a176 100644 --- a/src/python/grpcio/grpc/_links/invocation.py +++ b/src/python/grpcio/grpc/_links/invocation.py @@ -36,6 +36,8 @@ import threading import time from grpc._adapter import _intermediary_low +from grpc._links import _constants +from grpc.beta import interfaces as beta_interfaces from grpc.framework.foundation import activated from grpc.framework.foundation import logging_pool from grpc.framework.foundation import relay @@ -72,11 +74,28 @@ class _LowWrite(enum.Enum): CLOSED = 'CLOSED' +class _Context(beta_interfaces.GRPCInvocationContext): + + def __init__(self): + self._lock = threading.Lock() + self._disable_next_compression = False + + def disable_next_request_compression(self): + with self._lock: + self._disable_next_compression = True + + def next_compression_disabled(self): + with self._lock: + disabled = self._disable_next_compression + self._disable_next_compression = False + return disabled + + class _RPCState(object): def __init__( self, call, request_serializer, response_deserializer, sequence_number, - read, allowance, high_write, low_write, due): + read, allowance, high_write, low_write, due, context): self.call = call self.request_serializer = request_serializer self.response_deserializer = response_deserializer @@ -86,6 +105,7 @@ class _RPCState(object): self.high_write = high_write self.low_write = low_write self.due = due + self.context = context def _no_longer_due(kind, rpc_state, key, rpc_states): @@ -168,14 +188,17 @@ class _Kernel(object): termination = links.Ticket.Termination.CANCELLATION elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED: termination = links.Ticket.Termination.EXPIRATION + elif event.status.code is _intermediary_low.Code.UNIMPLEMENTED: + termination = links.Ticket.Termination.REMOTE_FAILURE elif event.status.code is _intermediary_low.Code.UNKNOWN: termination = links.Ticket.Termination.LOCAL_FAILURE else: termination = links.Ticket.Termination.TRANSMISSION_FAILURE + code = _constants.LOW_STATUS_CODE_TO_HIGH_STATUS_CODE[event.status.code] ticket = links.Ticket( operation_id, rpc_state.sequence_number, None, None, None, None, None, - None, None, event.metadata, event.status.code, event.status.details, - termination, None) + None, None, event.metadata, code, event.status.details, termination, + None) rpc_state.sequence_number += 1 self._relay.add_value(ticket) @@ -205,7 +228,7 @@ class _Kernel(object): def _invoke( self, operation_id, group, method, initial_metadata, payload, termination, - timeout, allowance): + timeout, allowance, options): """Invoke an RPC. Args: @@ -220,6 +243,7 @@ class _Kernel(object): timeout: A duration of time in seconds to allow for the RPC. allowance: The number of payloads (beyond the free first one) that the local ticket exchange mate has granted permission to be read. + options: A beta_interfaces.GRPCCallOptions value or None. """ if termination is links.Ticket.Termination.COMPLETION: high_write = _HighWrite.CLOSED @@ -237,6 +261,8 @@ class _Kernel(object): call = _intermediary_low.Call( self._channel, self._completion_queue, '/%s/%s' % (group, method), self._host, time.time() + timeout) + if options is not None and options.credentials is not None: + call.set_credentials(options.credentials._intermediary_low_credentials) if transformed_initial_metadata is not None: for metadata_key, metadata_value in transformed_initial_metadata: call.add_metadata(metadata_key, metadata_value) @@ -250,17 +276,33 @@ class _Kernel(object): low_write = _LowWrite.OPEN due = set((_METADATA, _FINISH,)) else: - call.write(request_serializer(payload), operation_id) + if options is not None and options.disable_compression: + flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS + else: + flags = 0 + call.write(request_serializer(payload), operation_id, flags) low_write = _LowWrite.ACTIVE due = set((_WRITE, _METADATA, _FINISH,)) + context = _Context() self._rpc_states[operation_id] = _RPCState( - call, request_serializer, response_deserializer, 0, + call, request_serializer, response_deserializer, 1, _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance), - high_write, low_write, due) + high_write, low_write, due, context) + protocol = links.Protocol(links.Protocol.Kind.INVOCATION_CONTEXT, context) + ticket = links.Ticket( + operation_id, 0, None, None, None, None, None, None, None, None, None, + None, None, protocol) + self._relay.add_value(ticket) def _advance(self, operation_id, rpc_state, payload, termination, allowance): if payload is not None: - rpc_state.call.write(rpc_state.request_serializer(payload), operation_id) + disable_compression = rpc_state.context.next_compression_disabled() + if disable_compression: + flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS + else: + flags = 0 + rpc_state.call.write( + rpc_state.request_serializer(payload), operation_id, flags) rpc_state.low_write = _LowWrite.ACTIVE rpc_state.due.add(_WRITE) @@ -288,10 +330,15 @@ class _Kernel(object): if self._completion_queue is None: logging.error('Received invocation ticket %s after stop!', ticket) else: + if (ticket.protocol is not None and + ticket.protocol.kind is links.Protocol.Kind.CALL_OPTION): + grpc_call_options = ticket.protocol.value + else: + grpc_call_options = None self._invoke( ticket.operation_id, ticket.group, ticket.method, ticket.initial_metadata, ticket.payload, ticket.termination, - ticket.timeout, ticket.allowance) + ticket.timeout, ticket.allowance, grpc_call_options) else: rpc_state = self._rpc_states.get(ticket.operation_id) if rpc_state is not None: diff --git a/src/python/grpcio/grpc/_links/service.py b/src/python/grpcio/grpc/_links/service.py index 94e7cfc716..f56df84007 100644 --- a/src/python/grpcio/grpc/_links/service.py +++ b/src/python/grpcio/grpc/_links/service.py @@ -36,6 +36,8 @@ import threading import time from grpc._adapter import _intermediary_low +from grpc._links import _constants +from grpc.beta import interfaces as beta_interfaces from grpc.framework.foundation import logging_pool from grpc.framework.foundation import relay from grpc.framework.interfaces.links import links @@ -88,12 +90,34 @@ class _LowWrite(enum.Enum): CLOSED = 'CLOSED' +class _Context(beta_interfaces.GRPCServicerContext): + + def __init__(self, call): + self._lock = threading.Lock() + self._call = call + self._disable_next_compression = False + + def peer(self): + with self._lock: + return self._call.peer() + + def disable_next_response_compression(self): + with self._lock: + self._disable_next_compression = True + + def next_compression_disabled(self): + with self._lock: + disabled = self._disable_next_compression + self._disable_next_compression = False + return disabled + + class _RPCState(object): def __init__( self, request_deserializer, response_serializer, sequence_number, read, early_read, allowance, high_write, low_write, premetadataed, - terminal_metadata, code, message, due): + terminal_metadata, code, message, due, context): self.request_deserializer = request_deserializer self.response_serializer = response_serializer self.sequence_number = sequence_number @@ -109,6 +133,7 @@ class _RPCState(object): self.code = code self.message = message self.due = due + self.context = context def _no_longer_due(kind, rpc_state, key, rpc_states): @@ -122,13 +147,13 @@ def _metadatafy(call, metadata): call.add_metadata(metadata_key, metadata_value) -def _status(termination_kind, code, details): - effective_details = b'' if details is None else details - if code is None: - effective_code = _TERMINATION_KIND_TO_CODE[termination_kind] +def _status(termination_kind, high_code, details): + low_details = b'' if details is None else details + if high_code is None: + low_code = _TERMINATION_KIND_TO_CODE[termination_kind] else: - effective_code = code - return _intermediary_low.Status(effective_code, effective_details) + low_code = _constants.HIGH_STATUS_CODE_TO_LOW_STATUS_CODE[high_code] + return _intermediary_low.Status(low_code, low_details) class _Kernel(object): @@ -162,14 +187,16 @@ class _Kernel(object): (group, method), _IDENTITY) call.read(call) + context = _Context(call) self._rpc_states[call] = _RPCState( request_deserializer, response_serializer, 1, _Read.READING, None, 1, _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None, - set((_READ, _FINISH,))) + set((_READ, _FINISH,)), context) + protocol = links.Protocol(links.Protocol.Kind.SERVICER_CONTEXT, context) ticket = links.Ticket( call, 0, group, method, links.Ticket.Subscription.FULL, service_acceptance.deadline - time.time(), None, event.metadata, None, - None, None, None, None, 'TODO: Service Context Object!') + None, None, None, None, protocol) self._relay.add_value(ticket) def _on_read_event(self, event): @@ -310,7 +337,12 @@ class _Kernel(object): self._relay.add_value(early_read_ticket) if ticket.payload is not None: - call.write(rpc_state.response_serializer(ticket.payload), call) + disable_compression = rpc_state.context.next_compression_disabled() + if disable_compression: + flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS + else: + flags = 0 + call.write(rpc_state.response_serializer(ticket.payload), call, flags) rpc_state.due.add(_WRITE) rpc_state.low_write = _LowWrite.ACTIVE diff --git a/src/python/grpcio/grpc/beta/_connectivity_channel.py b/src/python/grpcio/grpc/beta/_connectivity_channel.py index 457ede79f2..61674a70ad 100644 --- a/src/python/grpcio/grpc/beta/_connectivity_channel.py +++ b/src/python/grpcio/grpc/beta/_connectivity_channel.py @@ -33,18 +33,24 @@ import threading import time from grpc._adapter import _low +from grpc._adapter import _types +from grpc.beta import interfaces from grpc.framework.foundation import callable_util _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = ( 'Exception calling channel subscription callback!') +_LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = { + state: connectivity for state, connectivity in zip( + _types.ConnectivityState, interfaces.ChannelConnectivity) +} + class ConnectivityChannel(object): - def __init__(self, low_channel, mapping): + def __init__(self, low_channel): self._lock = threading.Lock() self._low_channel = low_channel - self._mapping = mapping self._polling = False self._connectivity = None @@ -88,7 +94,8 @@ class ConnectivityChannel(object): try_to_connect = initial_try_to_connect low_connectivity = low_channel.check_connectivity_state(try_to_connect) with self._lock: - self._connectivity = self._mapping[low_connectivity] + self._connectivity = _LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[ + low_connectivity] callbacks = tuple( callback for callback, unused_but_known_to_be_none_connectivity in self._callbacks_and_connectivities) @@ -112,7 +119,8 @@ class ConnectivityChannel(object): if event.success or try_to_connect: low_connectivity = low_channel.check_connectivity_state(try_to_connect) with self._lock: - self._connectivity = self._mapping[low_connectivity] + self._connectivity = _LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[ + low_connectivity] if not self._delivering: callbacks = self._deliveries(self._connectivity) if callbacks: diff --git a/src/python/grpcio/grpc/beta/_server.py b/src/python/grpcio/grpc/beta/_server.py index 4e46ffd17f..05b954d186 100644 --- a/src/python/grpcio/grpc/beta/_server.py +++ b/src/python/grpcio/grpc/beta/_server.py @@ -32,9 +32,11 @@ import threading from grpc._links import service +from grpc.beta import interfaces from grpc.framework.core import implementations as _core_implementations from grpc.framework.crust import implementations as _crust_implementations from grpc.framework.foundation import logging_pool +from grpc.framework.interfaces.base import base from grpc.framework.interfaces.links import utilities _DEFAULT_POOL_SIZE = 8 @@ -42,6 +44,23 @@ _DEFAULT_TIMEOUT = 300 _MAXIMUM_TIMEOUT = 24 * 60 * 60 +class _GRPCServicer(base.Servicer): + + def __init__(self, delegate): + self._delegate = delegate + + def service(self, group, method, context, output_operator): + try: + return self._delegate.service(group, method, context, output_operator) + except base.NoSuchMethodError as e: + if e.code is None and e.details is None: + raise base.NoSuchMethodError( + interfaces.StatusCode.UNIMPLEMENTED, + b'Method "%s" of service "%s" not implemented!' % (method, group)) + else: + raise + + def _disassemble(grpc_link, end_link, pool, event, grace): grpc_link.begin_stop() end_link.stop(grace).wait() @@ -53,7 +72,7 @@ def _disassemble(grpc_link, end_link, pool, event, grace): event.set() -class Server(object): +class Server(interfaces.Server): def __init__(self, grpc_link, end_link, pool): self._grpc_link = grpc_link @@ -63,17 +82,17 @@ class Server(object): def add_insecure_port(self, address): return self._grpc_link.add_port(address, None) - def add_secure_port(self, address, intermediary_low_server_credentials): + def add_secure_port(self, address, server_credentials): return self._grpc_link.add_port( - address, intermediary_low_server_credentials) + address, server_credentials._intermediary_low_credentials) # pylint: disable=protected-access - def start(self): + def _start(self): self._grpc_link.join_link(self._end_link) self._end_link.join_link(self._grpc_link) self._grpc_link.start() self._end_link.start() - def stop(self, grace): + def _stop(self, grace): stop_event = threading.Event() if 0 < grace: disassembly_thread = threading.Thread( @@ -86,6 +105,20 @@ class Server(object): _disassemble(self._grpc_link, self._end_link, self._pool, stop_event, 0) return stop_event + def start(self): + self._start() + + def stop(self, grace): + return self._stop(grace) + + def __enter__(self): + self._start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._stop(0).wait() + return False + def server( implementations, multi_implementation, request_deserializers, @@ -99,8 +132,9 @@ def server( service_thread_pool = thread_pool assembly_thread_pool = None - servicer = _crust_implementations.servicer( - implementations, multi_implementation, service_thread_pool) + servicer = _GRPCServicer( + _crust_implementations.servicer( + implementations, multi_implementation, service_thread_pool)) grpc_link = service.service_link(request_deserializers, response_serializers) diff --git a/src/python/grpcio/grpc/beta/_stub.py b/src/python/grpcio/grpc/beta/_stub.py index cfbecb852b..11dab889cd 100644 --- a/src/python/grpcio/grpc/beta/_stub.py +++ b/src/python/grpcio/grpc/beta/_stub.py @@ -49,6 +49,12 @@ class _AutoIntermediary(object): def __getattr__(self, attr): return getattr(self._delegate, attr) + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + return False + def __del__(self): self._on_deletion() diff --git a/src/python/grpcio/grpc/beta/beta.py b/src/python/grpcio/grpc/beta/implementations.py index b3a161087f..c9d64ad35a 100644 --- a/src/python/grpcio/grpc/beta/beta.py +++ b/src/python/grpcio/grpc/beta/implementations.py @@ -40,6 +40,7 @@ from grpc._adapter import _types from grpc.beta import _connectivity_channel from grpc.beta import _server from grpc.beta import _stub +from grpc.beta import interfaces from grpc.framework.common import cardinality # pylint: disable=unused-import from grpc.framework.interfaces.face import face # pylint: disable=unused-import @@ -47,32 +48,6 @@ _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = ( 'Exception calling channel subscription callback!') -@enum.unique -class ChannelConnectivity(enum.Enum): - """Mirrors grpc_connectivity_state in the gRPC Core. - - Attributes: - IDLE: The channel is idle. - CONNECTING: The channel is connecting. - READY: The channel is ready to conduct RPCs. - TRANSIENT_FAILURE: The channel has seen a failure from which it expects to - recover. - FATAL_FAILURE: The channel has seen a failure from which it cannot recover. - """ - - IDLE = (_types.ConnectivityState.IDLE, 'idle',) - CONNECTING = (_types.ConnectivityState.CONNECTING, 'connecting',) - READY = (_types.ConnectivityState.READY, 'ready',) - TRANSIENT_FAILURE = ( - _types.ConnectivityState.TRANSIENT_FAILURE, 'transient failure',) - FATAL_FAILURE = (_types.ConnectivityState.FATAL_FAILURE, 'fatal failure',) - -_LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = { - state: connectivity for state, connectivity in zip( - _types.ConnectivityState, ChannelConnectivity) -} - - class ClientCredentials(object): """A value encapsulating the data required to create a secure Channel. @@ -118,13 +93,14 @@ class Channel(object): self._low_channel = low_channel self._intermediary_low_channel = intermediary_low_channel self._connectivity_channel = _connectivity_channel.ConnectivityChannel( - low_channel, _LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY) + low_channel) def subscribe(self, callback, try_to_connect=None): """Subscribes to this Channel's connectivity. Args: - callback: A callable to be invoked and passed this Channel's connectivity. + callback: A callable to be invoked and passed an + interfaces.ChannelConnectivity identifying this Channel's connectivity. The callable will be invoked immediately upon subscription and again for every change to this Channel's connectivity thereafter until it is unsubscribed. @@ -144,7 +120,7 @@ class Channel(object): self._connectivity_channel.unsubscribe(callback) -def create_insecure_channel(host, port): +def insecure_channel(host, port): """Creates an insecure Channel to a remote host. Args: @@ -159,7 +135,7 @@ def create_insecure_channel(host, port): return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access -def create_secure_channel(host, port, client_credentials): +def secure_channel(host, port, client_credentials): """Creates a secure Channel to a remote host. Args: @@ -171,7 +147,7 @@ def create_secure_channel(host, port, client_credentials): A secure Channel to the remote host through which RPCs may be conducted. """ intermediary_low_channel = _intermediary_low.Channel( - '%s:%d' % (host, port), client_credentials.intermediary_low_credentials) + '%s:%d' % (host, port), client_credentials._intermediary_low_credentials) return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access @@ -313,86 +289,6 @@ def ssl_server_credentials( intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access -class Server(object): - """Services RPCs.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def add_insecure_port(self, address): - """Reserves a port for insecure RPC service once this Server becomes active. - - This method may only be called before calling this Server's start method is - called. - - Args: - address: The address for which to open a port. - - Returns: - An integer port on which RPCs will be serviced after this link has been - started. This is typically the same number as the port number contained - in the passed address, but will likely be different if the port number - contained in the passed address was zero. - """ - raise NotImplementedError() - - @abc.abstractmethod - def add_secure_port(self, address, server_credentials): - """Reserves a port for secure RPC service after this Server becomes active. - - This method may only be called before calling this Server's start method is - called. - - Args: - address: The address for which to open a port. - server_credentials: A ServerCredentials. - - Returns: - An integer port on which RPCs will be serviced after this link has been - started. This is typically the same number as the port number contained - in the passed address, but will likely be different if the port number - contained in the passed address was zero. - """ - raise NotImplementedError() - - @abc.abstractmethod - def start(self): - """Starts this Server's service of RPCs. - - This method may only be called while the server is not serving RPCs (i.e. it - is not idempotent). - """ - raise NotImplementedError() - - @abc.abstractmethod - def stop(self, grace): - """Stops this Server's service of RPCs. - - All calls to this method immediately stop service of new RPCs. When existing - RPCs are aborted is controlled by the grace period parameter passed to this - method. - - This method may be called at any time and is idempotent. Passing a smaller - grace value than has been passed in a previous call will have the effect of - stopping the Server sooner. Passing a larger grace value than has been - passed in a previous call will not have the effect of stopping the sooner - later. - - Args: - grace: A duration of time in seconds to allow existing RPCs to complete - before being aborted by this Server's stopping. May be zero for - immediate abortion of all in-progress RPCs. - - Returns: - A threading.Event that will be set when this Server has completely - stopped. The returned event may not be set until after the full grace - period (if some ongoing RPC continues for the full length of the period) - of it may be set much sooner (such as if this Server had no RPCs underway - at the time it was stopped or if all RPCs that it had underway completed - very early in the grace period). - """ - raise NotImplementedError() - - class ServerOptions(object): """A value encapsulating the various options for creation of a Server. @@ -450,27 +346,8 @@ def server_options( thread_pool, thread_pool_size, default_timeout, maximum_timeout) -class _Server(Server): - - def __init__(self, underserver): - self._underserver = underserver - - def add_insecure_port(self, address): - return self._underserver.add_insecure_port(address) - - def add_secure_port(self, address, server_credentials): - return self._underserver.add_secure_port( - address, server_credentials._intermediary_low_credentials) # pylint: disable=protected-access - - def start(self): - self._underserver.start() - - def stop(self, grace): - return self._underserver.stop(grace) - - def server(service_implementations, options=None): - """Creates a Server with which RPCs can be serviced. + """Creates an interfaces.Server with which RPCs can be serviced. Args: service_implementations: A dictionary from service name-method name pair to @@ -479,13 +356,12 @@ def server(service_implementations, options=None): functionality of the returned Server. Returns: - A Server with which RPCs can be serviced. + An interfaces.Server with which RPCs can be serviced. """ effective_options = _EMPTY_SERVER_OPTIONS if options is None else options - underserver = _server.server( + return _server.server( service_implementations, effective_options.multi_method_implementation, effective_options.request_deserializers, effective_options.response_serializers, effective_options.thread_pool, effective_options.thread_pool_size, effective_options.default_timeout, effective_options.maximum_timeout) - return _Server(underserver) diff --git a/src/python/grpcio/grpc/beta/interfaces.py b/src/python/grpcio/grpc/beta/interfaces.py new file mode 100644 index 0000000000..07c8618f70 --- /dev/null +++ b/src/python/grpcio/grpc/beta/interfaces.py @@ -0,0 +1,214 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Constants and interfaces of the Beta API of gRPC Python.""" + +import abc +import enum + +from grpc._adapter import _types + + +@enum.unique +class ChannelConnectivity(enum.Enum): + """Mirrors grpc_connectivity_state in the gRPC Core. + + Attributes: + IDLE: The channel is idle. + CONNECTING: The channel is connecting. + READY: The channel is ready to conduct RPCs. + TRANSIENT_FAILURE: The channel has seen a failure from which it expects to + recover. + FATAL_FAILURE: The channel has seen a failure from which it cannot recover. + """ + IDLE = (_types.ConnectivityState.IDLE, 'idle',) + CONNECTING = (_types.ConnectivityState.CONNECTING, 'connecting',) + READY = (_types.ConnectivityState.READY, 'ready',) + TRANSIENT_FAILURE = ( + _types.ConnectivityState.TRANSIENT_FAILURE, 'transient failure',) + FATAL_FAILURE = (_types.ConnectivityState.FATAL_FAILURE, 'fatal failure',) + + +@enum.unique +class StatusCode(enum.Enum): + """Mirrors grpc_status_code in the C core.""" + OK = 0 + CANCELLED = 1 + UNKNOWN = 2 + INVALID_ARGUMENT = 3 + DEADLINE_EXCEEDED = 4 + NOT_FOUND = 5 + ALREADY_EXISTS = 6 + PERMISSION_DENIED = 7 + RESOURCE_EXHAUSTED = 8 + FAILED_PRECONDITION = 9 + ABORTED = 10 + OUT_OF_RANGE = 11 + UNIMPLEMENTED = 12 + INTERNAL = 13 + UNAVAILABLE = 14 + DATA_LOSS = 15 + UNAUTHENTICATED = 16 + + +class GRPCCallOptions(object): + """A value encapsulating gRPC-specific options passed on RPC invocation. + + This class and its instances have no supported interface - it exists to + define the type of its instances and its instances exist to be passed to + other functions. + """ + + def __init__(self, disable_compression, subcall_of, credentials): + self.disable_compression = disable_compression + self.subcall_of = subcall_of + self.credentials = credentials + + +def grpc_call_options(disable_compression=False, credentials=None): + """Creates a GRPCCallOptions value to be passed at RPC invocation. + + All parameters are optional and should always be passed by keyword. + + Args: + disable_compression: A boolean indicating whether or not compression should + be disabled for the request object of the RPC. Only valid for + request-unary RPCs. + credentials: A ClientCredentials object to use for the invoked RPC. + """ + return GRPCCallOptions(disable_compression, None, credentials) + + +class GRPCServicerContext(object): + """Exposes gRPC-specific options and behaviors to code servicing RPCs.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def peer(self): + """Identifies the peer that invoked the RPC being serviced. + + Returns: + A string identifying the peer that invoked the RPC being serviced. + """ + raise NotImplementedError() + + @abc.abstractmethod + def disable_next_response_compression(self): + """Disables compression of the next response passed by the application.""" + raise NotImplementedError() + + +class GRPCInvocationContext(object): + """Exposes gRPC-specific options and behaviors to code invoking RPCs.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def disable_next_request_compression(self): + """Disables compression of the next request passed by the application.""" + raise NotImplementedError() + + +class Server(object): + """Services RPCs.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def add_insecure_port(self, address): + """Reserves a port for insecure RPC service once this Server becomes active. + + This method may only be called before calling this Server's start method is + called. + + Args: + address: The address for which to open a port. + + Returns: + An integer port on which RPCs will be serviced after this link has been + started. This is typically the same number as the port number contained + in the passed address, but will likely be different if the port number + contained in the passed address was zero. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_secure_port(self, address, server_credentials): + """Reserves a port for secure RPC service after this Server becomes active. + + This method may only be called before calling this Server's start method is + called. + + Args: + address: The address for which to open a port. + server_credentials: A ServerCredentials. + + Returns: + An integer port on which RPCs will be serviced after this link has been + started. This is typically the same number as the port number contained + in the passed address, but will likely be different if the port number + contained in the passed address was zero. + """ + raise NotImplementedError() + + @abc.abstractmethod + def start(self): + """Starts this Server's service of RPCs. + + This method may only be called while the server is not serving RPCs (i.e. it + is not idempotent). + """ + raise NotImplementedError() + + @abc.abstractmethod + def stop(self, grace): + """Stops this Server's service of RPCs. + + All calls to this method immediately stop service of new RPCs. When existing + RPCs are aborted is controlled by the grace period parameter passed to this + method. + + This method may be called at any time and is idempotent. Passing a smaller + grace value than has been passed in a previous call will have the effect of + stopping the Server sooner. Passing a larger grace value than has been + passed in a previous call will not have the effect of stopping the sooner + later. + + Args: + grace: A duration of time in seconds to allow existing RPCs to complete + before being aborted by this Server's stopping. May be zero for + immediate abortion of all in-progress RPCs. + + Returns: + A threading.Event that will be set when this Server has completely + stopped. The returned event may not be set until after the full grace + period (if some ongoing RPC continues for the full length of the period) + of it may be set much sooner (such as if this Server had no RPCs underway + at the time it was stopped or if all RPCs that it had underway completed + very early in the grace period). + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/beta/utilities.py b/src/python/grpcio/grpc/beta/utilities.py index 1b5356e3ad..fb07a76579 100644 --- a/src/python/grpcio/grpc/beta/utilities.py +++ b/src/python/grpcio/grpc/beta/utilities.py @@ -32,7 +32,9 @@ import threading import time -from grpc.beta import beta +# implementations is referenced from specification in this module. +from grpc.beta import implementations # pylint: disable=unused-import +from grpc.beta import interfaces from grpc.framework.foundation import callable_util from grpc.framework.foundation import future @@ -70,7 +72,8 @@ class _ChannelReadyFuture(future.Future): def _update(self, connectivity): with self._condition: - if not self._cancelled and connectivity is beta.ChannelConnectivity.READY: + if (not self._cancelled and + connectivity is interfaces.ChannelConnectivity.READY): self._matured = True self._channel.unsubscribe(self._update) self._condition.notify_all() @@ -141,19 +144,19 @@ class _ChannelReadyFuture(future.Future): def channel_ready_future(channel): - """Creates a future.Future that matures when a beta.Channel is ready. + """Creates a future.Future tracking when an implementations.Channel is ready. - Cancelling the returned future.Future does not tell the given beta.Channel to - abandon attempts it may have been making to connect; cancelling merely - deactivates the return future.Future's subscription to the given - beta.Channel's connectivity. + Cancelling the returned future.Future does not tell the given + implementations.Channel to abandon attempts it may have been making to + connect; cancelling merely deactivates the return future.Future's + subscription to the given implementations.Channel's connectivity. Args: - channel: A beta.Channel. + channel: An implementations.Channel. Returns: A future.Future that matures when the given Channel has connectivity - beta.ChannelConnectivity.READY. + interfaces.ChannelConnectivity.READY. """ ready_future = _ChannelReadyFuture(channel) ready_future.start() diff --git a/src/python/grpcio/grpc/early_adopter/__init__.py b/src/python/grpcio/grpc/early_adopter/__init__.py index 7086519106..bff74be2c7 100644 --- a/src/python/grpcio/grpc/early_adopter/__init__.py +++ b/src/python/grpcio/grpc/early_adopter/__init__.py @@ -27,4 +27,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import warnings +warnings.simplefilter('always', DeprecationWarning) +warnings.warn('the alpha API (includes this package) is deprecated, ' + 'unmaintained, and no longer tested. Please migrate to the beta ' + 'API.', DeprecationWarning, stacklevel=2) diff --git a/src/python/grpcio/grpc/framework/alpha/__init__.py b/src/python/grpcio/grpc/framework/alpha/__init__.py index b89398809f..bff74be2c7 100644 --- a/src/python/grpcio/grpc/framework/alpha/__init__.py +++ b/src/python/grpcio/grpc/framework/alpha/__init__.py @@ -26,3 +26,10 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import warnings + +warnings.simplefilter('always', DeprecationWarning) +warnings.warn('the alpha API (includes this package) is deprecated, ' + 'unmaintained, and no longer tested. Please migrate to the beta ' + 'API.', DeprecationWarning, stacklevel=2) diff --git a/src/python/grpcio/grpc/framework/base/__init__.py b/src/python/grpcio/grpc/framework/base/__init__.py index 7086519106..bff74be2c7 100644 --- a/src/python/grpcio/grpc/framework/base/__init__.py +++ b/src/python/grpcio/grpc/framework/base/__init__.py @@ -27,4 +27,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import warnings +warnings.simplefilter('always', DeprecationWarning) +warnings.warn('the alpha API (includes this package) is deprecated, ' + 'unmaintained, and no longer tested. Please migrate to the beta ' + 'API.', DeprecationWarning, stacklevel=2) diff --git a/src/python/grpcio/grpc/framework/core/_constants.py b/src/python/grpcio/grpc/framework/core/_constants.py index d3be3a4c4a..0f47cb48e0 100644 --- a/src/python/grpcio/grpc/framework/core/_constants.py +++ b/src/python/grpcio/grpc/framework/core/_constants.py @@ -44,14 +44,15 @@ TICKET_SUBSCRIPTION_FOR_BASE_SUBSCRIPTION_KIND = { # ticket should be sent to the other side in the event of such an # outcome. ABORTION_OUTCOME_TO_TICKET_TERMINATION = { - base.Outcome.CANCELLED: links.Ticket.Termination.CANCELLATION, - base.Outcome.EXPIRED: links.Ticket.Termination.EXPIRATION, - base.Outcome.LOCAL_SHUTDOWN: links.Ticket.Termination.SHUTDOWN, - base.Outcome.REMOTE_SHUTDOWN: None, - base.Outcome.RECEPTION_FAILURE: links.Ticket.Termination.RECEPTION_FAILURE, - base.Outcome.TRANSMISSION_FAILURE: None, - base.Outcome.LOCAL_FAILURE: links.Ticket.Termination.LOCAL_FAILURE, - base.Outcome.REMOTE_FAILURE: links.Ticket.Termination.REMOTE_FAILURE, + base.Outcome.Kind.CANCELLED: links.Ticket.Termination.CANCELLATION, + base.Outcome.Kind.EXPIRED: links.Ticket.Termination.EXPIRATION, + base.Outcome.Kind.LOCAL_SHUTDOWN: links.Ticket.Termination.SHUTDOWN, + base.Outcome.Kind.REMOTE_SHUTDOWN: None, + base.Outcome.Kind.RECEPTION_FAILURE: + links.Ticket.Termination.RECEPTION_FAILURE, + base.Outcome.Kind.TRANSMISSION_FAILURE: None, + base.Outcome.Kind.LOCAL_FAILURE: links.Ticket.Termination.LOCAL_FAILURE, + base.Outcome.Kind.REMOTE_FAILURE: links.Ticket.Termination.REMOTE_FAILURE, } INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Core) internal error! )-:' diff --git a/src/python/grpcio/grpc/framework/core/_context.py b/src/python/grpcio/grpc/framework/core/_context.py index 76b3534530..a346e9d478 100644 --- a/src/python/grpcio/grpc/framework/core/_context.py +++ b/src/python/grpcio/grpc/framework/core/_context.py @@ -33,6 +33,7 @@ import time # _interfaces is referenced from specification in this module. from grpc.framework.core import _interfaces # pylint: disable=unused-import +from grpc.framework.core import _utilities from grpc.framework.interfaces.base import base @@ -56,11 +57,12 @@ class OperationContext(base.OperationContext): self._transmission_manager = transmission_manager self._expiration_manager = expiration_manager - def _abort(self, outcome): + def _abort(self, outcome_kind): with self._lock: if self._termination_manager.outcome is None: + outcome = _utilities.Outcome(outcome_kind, None, None) self._termination_manager.abort(outcome) - self._transmission_manager.abort(outcome, None, None) + self._transmission_manager.abort(outcome) self._expiration_manager.terminate() def outcome(self): @@ -85,8 +87,8 @@ class OperationContext(base.OperationContext): def cancel(self): """See base.OperationContext.cancel for specification.""" - self._abort(base.Outcome.CANCELLED) + self._abort(base.Outcome.Kind.CANCELLED) def fail(self, exception): """See base.OperationContext.fail for specification.""" - self._abort(base.Outcome.LOCAL_FAILURE) + self._abort(base.Outcome.Kind.LOCAL_FAILURE) diff --git a/src/python/grpcio/grpc/framework/core/_emission.py b/src/python/grpcio/grpc/framework/core/_emission.py index 2d7b2e2f10..8ab59dc3e5 100644 --- a/src/python/grpcio/grpc/framework/core/_emission.py +++ b/src/python/grpcio/grpc/framework/core/_emission.py @@ -30,6 +30,7 @@ """State and behavior for handling emitted values.""" from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities from grpc.framework.interfaces.base import base @@ -81,9 +82,10 @@ class EmissionManager(_interfaces.EmissionManager): payload_present and self._completion_seen or completion_present and self._completion_seen or allowance_present and allowance <= 0): - self._termination_manager.abort(base.Outcome.LOCAL_FAILURE) - self._transmission_manager.abort( - base.Outcome.LOCAL_FAILURE, None, None) + outcome = _utilities.Outcome( + base.Outcome.Kind.LOCAL_FAILURE, None, None) + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) self._expiration_manager.terminate() else: self._initial_metadata_seen |= initial_metadata_present diff --git a/src/python/grpcio/grpc/framework/core/_end.py b/src/python/grpcio/grpc/framework/core/_end.py index f57cde4e58..8e07d9061e 100644 --- a/src/python/grpcio/grpc/framework/core/_end.py +++ b/src/python/grpcio/grpc/framework/core/_end.py @@ -69,7 +69,7 @@ class _Cycle(object): def _abort(operations): for operation in operations: - operation.abort(base.Outcome.LOCAL_SHUTDOWN) + operation.abort(base.Outcome.Kind.LOCAL_SHUTDOWN) def _cancel_futures(futures): @@ -90,19 +90,19 @@ def _termination_action(lock, stats, operation_id, cycle): Args: lock: A lock to hold during the termination action. - states: A mapping from base.Outcome values to integers to increment with - the outcome given to the termination action. + stats: A mapping from base.Outcome.Kind values to integers to increment + with the outcome kind given to the termination action. operation_id: The operation ID for the termination action. cycle: A _Cycle value to be updated during the termination action. Returns: - A callable that takes an operation outcome as its sole parameter and that - should be used as the termination action for the operation associated - with the given operation ID. + A callable that takes an operation outcome kind as its sole parameter and + that should be used as the termination action for the operation + associated with the given operation ID. """ - def termination_action(outcome): + def termination_action(outcome_kind): with lock: - stats[outcome] += 1 + stats[outcome_kind] += 1 cycle.operations.pop(operation_id, None) if not cycle.operations: for action in cycle.idle_actions: @@ -127,7 +127,7 @@ class _End(End): self._lock = threading.Condition() self._servicer_package = servicer_package - self._stats = {outcome: 0 for outcome in base.Outcome} + self._stats = {outcome_kind: 0 for outcome_kind in base.Outcome.Kind} self._mate = None @@ -168,7 +168,7 @@ class _End(End): def operate( self, group, method, subscription, timeout, initial_metadata=None, - payload=None, completion=None): + payload=None, completion=None, protocol_options=None): """See base.End.operate for specification.""" operation_id = uuid.uuid4() with self._lock: @@ -177,9 +177,9 @@ class _End(End): termination_action = _termination_action( self._lock, self._stats, operation_id, self._cycle) operation = _operation.invocation_operate( - operation_id, group, method, subscription, timeout, initial_metadata, - payload, completion, self._mate.accept_ticket, termination_action, - self._cycle.pool) + operation_id, group, method, subscription, timeout, protocol_options, + initial_metadata, payload, completion, self._mate.accept_ticket, + termination_action, self._cycle.pool) self._cycle.operations[operation_id] = operation return operation.context, operation.operator diff --git a/src/python/grpcio/grpc/framework/core/_expiration.py b/src/python/grpcio/grpc/framework/core/_expiration.py index d8690b3a02..ded0ab6bce 100644 --- a/src/python/grpcio/grpc/framework/core/_expiration.py +++ b/src/python/grpcio/grpc/framework/core/_expiration.py @@ -32,6 +32,7 @@ import time from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities from grpc.framework.foundation import later from grpc.framework.interfaces.base import base @@ -73,7 +74,8 @@ class _ExpirationManager(_interfaces.ExpirationManager): if self._future is not None and index == self._index: self._future = None self._termination_manager.expire() - self._transmission_manager.abort(base.Outcome.EXPIRED, None, None) + self._transmission_manager.abort( + _utilities.Outcome(base.Outcome.Kind.EXPIRED, None, None)) return expire def start(self): diff --git a/src/python/grpcio/grpc/framework/core/_ingestion.py b/src/python/grpcio/grpc/framework/core/_ingestion.py index 766d57f931..4129a8ce43 100644 --- a/src/python/grpcio/grpc/framework/core/_ingestion.py +++ b/src/python/grpcio/grpc/framework/core/_ingestion.py @@ -35,6 +35,7 @@ import enum from grpc.framework.core import _constants from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities from grpc.framework.foundation import abandonment from grpc.framework.foundation import callable_util from grpc.framework.interfaces.base import base @@ -46,7 +47,7 @@ _INGESTION_EXCEPTION_LOG_MESSAGE = 'Exception during ingestion!' class _SubscriptionCreation( collections.namedtuple( '_SubscriptionCreation', - ('kind', 'subscription', 'code', 'message',))): + ('kind', 'subscription', 'code', 'details',))): """A sum type for the outcome of ingestion initialization. Attributes: @@ -56,7 +57,7 @@ class _SubscriptionCreation( code: A code value to be sent to the other side of the operation along with an indication that the operation is being aborted due to an error on the remote side of the operation. Only present if kind is Kind.REMOTE_ERROR. - message: A message value to be sent to the other side of the operation + details: A details value to be sent to the other side of the operation along with an indication that the operation is being aborted due to an error on the remote side of the operation. Only present if kind is Kind.REMOTE_ERROR. @@ -139,7 +140,7 @@ class _IngestionManager(_interfaces.IngestionManager): def __init__( self, lock, pool, subscription, subscription_creator, termination_manager, - transmission_manager, expiration_manager): + transmission_manager, expiration_manager, protocol_manager): """Constructor. Args: @@ -156,12 +157,14 @@ class _IngestionManager(_interfaces.IngestionManager): transmission_manager: The _interfaces.TransmissionManager for the operation. expiration_manager: The _interfaces.ExpirationManager for the operation. + protocol_manager: The _interfaces.ProtocolManager for the operation. """ self._lock = lock self._pool = pool self._termination_manager = termination_manager self._transmission_manager = transmission_manager self._expiration_manager = expiration_manager + self._protocol_manager = protocol_manager if subscription is None: self._subscription_creator = subscription_creator @@ -190,11 +193,13 @@ class _IngestionManager(_interfaces.IngestionManager): self._pending_payloads = None self._pending_completion = None - def _abort_and_notify(self, outcome, code, message): + def _abort_and_notify(self, outcome_kind, code, details): self._abort_internal_only() - self._termination_manager.abort(outcome) - self._transmission_manager.abort(outcome, code, message) - self._expiration_manager.terminate() + if self._termination_manager.outcome is None: + outcome = _utilities.Outcome(outcome_kind, code, details) + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.terminate() def _operator_next(self): """Computes the next step for full-subscription ingestion. @@ -250,12 +255,13 @@ class _IngestionManager(_interfaces.IngestionManager): else: with self._lock: if self._termination_manager.outcome is None: - self._abort_and_notify(base.Outcome.LOCAL_FAILURE, None, None) + self._abort_and_notify( + base.Outcome.Kind.LOCAL_FAILURE, None, None) return else: with self._lock: if self._termination_manager.outcome is None: - self._abort_and_notify(base.Outcome.LOCAL_FAILURE, None, None) + self._abort_and_notify(base.Outcome.Kind.LOCAL_FAILURE, None, None) return def _operator_post_create(self, subscription): @@ -279,18 +285,21 @@ class _IngestionManager(_interfaces.IngestionManager): if outcome.return_value is None: with self._lock: if self._termination_manager.outcome is None: - self._abort_and_notify(base.Outcome.LOCAL_FAILURE, None, None) + self._abort_and_notify(base.Outcome.Kind.LOCAL_FAILURE, None, None) elif outcome.return_value.kind is _SubscriptionCreation.Kind.ABANDONED: with self._lock: if self._termination_manager.outcome is None: - self._abort_and_notify(base.Outcome.LOCAL_FAILURE, None, None) + self._abort_and_notify(base.Outcome.Kind.LOCAL_FAILURE, None, None) elif outcome.return_value.kind is _SubscriptionCreation.Kind.REMOTE_ERROR: code = outcome.return_value.code - message = outcome.return_value.message + details = outcome.return_value.details with self._lock: if self._termination_manager.outcome is None: - self._abort_and_notify(base.Outcome.REMOTE_FAILURE, code, message) + self._abort_and_notify( + base.Outcome.Kind.REMOTE_FAILURE, code, details) elif outcome.return_value.subscription.kind is base.Subscription.Kind.FULL: + self._protocol_manager.set_protocol_receiver( + outcome.return_value.subscription.protocol_receiver) self._operator_post_create(outcome.return_value.subscription) else: # TODO(nathaniel): Support other subscriptions. @@ -373,7 +382,7 @@ class _IngestionManager(_interfaces.IngestionManager): def invocation_ingestion_manager( subscription, lock, pool, termination_manager, transmission_manager, - expiration_manager): + expiration_manager, protocol_manager): """Creates an IngestionManager appropriate for invocation-side use. Args: @@ -385,18 +394,20 @@ def invocation_ingestion_manager( transmission_manager: The _interfaces.TransmissionManager for the operation. expiration_manager: The _interfaces.ExpirationManager for the operation. + protocol_manager: The _interfaces.ProtocolManager for the operation. Returns: An IngestionManager appropriate for invocation-side use. """ return _IngestionManager( lock, pool, subscription, None, termination_manager, transmission_manager, - expiration_manager) + expiration_manager, protocol_manager) def service_ingestion_manager( servicer, operation_context, output_operator, lock, pool, - termination_manager, transmission_manager, expiration_manager): + termination_manager, transmission_manager, expiration_manager, + protocol_manager): """Creates an IngestionManager appropriate for service-side use. The returned IngestionManager will require its set_group_and_name method to be @@ -415,6 +426,7 @@ def service_ingestion_manager( transmission_manager: The _interfaces.TransmissionManager for the operation. expiration_manager: The _interfaces.ExpirationManager for the operation. + protocol_manager: The _interfaces.ProtocolManager for the operation. Returns: An IngestionManager appropriate for service-side use. @@ -423,4 +435,4 @@ def service_ingestion_manager( servicer, operation_context, output_operator) return _IngestionManager( lock, pool, None, subscription_creator, termination_manager, - transmission_manager, expiration_manager) + transmission_manager, expiration_manager, protocol_manager) diff --git a/src/python/grpcio/grpc/framework/core/_interfaces.py b/src/python/grpcio/grpc/framework/core/_interfaces.py index deb5f34f9b..ffa686b2b7 100644 --- a/src/python/grpcio/grpc/framework/core/_interfaces.py +++ b/src/python/grpcio/grpc/framework/core/_interfaces.py @@ -50,13 +50,13 @@ class TerminationManager(object): If the operation has already terminated the callback will not be called. Args: - callback: A callable that will be passed an interfaces.Outcome value. + callback: A callable that will be passed a base.Outcome value. Returns: None if the operation has not yet terminated and the passed callback will - be called when it does, or a base.Outcome value describing the operation - termination if the operation has terminated and the callback will not be - called as a result of this method call. + be called when it does, or a base.Outcome value describing the + operation termination if the operation has terminated and the callback + will not be called as a result of this method call. """ raise NotImplementedError() @@ -76,8 +76,13 @@ class TerminationManager(object): raise NotImplementedError() @abc.abstractmethod - def reception_complete(self): - """Indicates that reception from the other side is complete.""" + def reception_complete(self, code, details): + """Indicates that reception from the other side is complete. + + Args: + code: An application-specific code value. + details: An application-specific details value. + """ raise NotImplementedError() @abc.abstractmethod @@ -95,7 +100,7 @@ class TerminationManager(object): """Indicates that the operation must abort for the indicated reason. Args: - outcome: An interfaces.Outcome indicating operation abortion. + outcome: A base.Outcome indicating operation abortion. """ raise NotImplementedError() @@ -106,8 +111,8 @@ class TransmissionManager(object): @abc.abstractmethod def kick_off( - self, group, method, timeout, initial_metadata, payload, completion, - allowance): + self, group, method, timeout, protocol_options, initial_metadata, + payload, completion, allowance): """Transmits the values associated with operation invocation.""" raise NotImplementedError() @@ -155,19 +160,13 @@ class TransmissionManager(object): raise NotImplementedError() @abc.abstractmethod - def abort(self, outcome, code, message): + def abort(self, outcome): """Indicates that the operation has aborted. Args: - outcome: An interfaces.Outcome for the operation. If None, indicates that - the operation abortion should not be communicated to the other side of - the operation. - code: A code value to communicate to the other side of the operation - along with indication of operation abortion. May be None, and has no - effect if outcome is None. - message: A message value to communicate to the other side of the - operation along with indication of operation abortion. May be None, and - has no effect if outcome is None. + outcome: A base.Outcome for the operation. If None, indicates that the + operation abortion should not be communicated to the other side of the + operation. """ raise NotImplementedError() @@ -204,6 +203,31 @@ class ExpirationManager(object): raise NotImplementedError() +class ProtocolManager(object): + """A manager of protocol-specific values passing through an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_protocol_receiver(self, protocol_receiver): + """Registers the customer object that will receive protocol objects. + + Args: + protocol_receiver: A base.ProtocolReceiver to which protocol objects for + the operation should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def accept_protocol_context(self, protocol_context): + """Accepts the protocol context object for the operation. + + Args: + protocol_context: An object designated for use as the protocol context + of the operation, with further semantics implementation-determined. + """ + raise NotImplementedError() + + class EmissionManager(base.Operator): """A manager of values emitted by customer code.""" __metaclass__ = abc.ABCMeta @@ -279,8 +303,7 @@ class ReceptionManager(object): """Handle a ticket from the other side of the operation. Args: - ticket: An interfaces.BackToFrontTicket or interfaces.FrontToBackTicket - appropriate to this end of the operation and this object. + ticket: A links.Ticket for the operation. """ raise NotImplementedError() @@ -305,10 +328,10 @@ class Operation(object): raise NotImplementedError() @abc.abstractmethod - def abort(self, outcome): + def abort(self, outcome_kind): """Aborts the operation. Args: - outcome: A base.Outcome value indicating operation abortion. + outcome_kind: A base.Outcome.Kind value indicating operation abortion. """ raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/core/_operation.py b/src/python/grpcio/grpc/framework/core/_operation.py index cc873c03f9..020c0c9ed9 100644 --- a/src/python/grpcio/grpc/framework/core/_operation.py +++ b/src/python/grpcio/grpc/framework/core/_operation.py @@ -31,16 +31,16 @@ import threading -# _utilities is referenced from specification in this module. from grpc.framework.core import _context from grpc.framework.core import _emission from grpc.framework.core import _expiration from grpc.framework.core import _ingestion from grpc.framework.core import _interfaces +from grpc.framework.core import _protocol from grpc.framework.core import _reception from grpc.framework.core import _termination from grpc.framework.core import _transmission -from grpc.framework.core import _utilities # pylint: disable=unused-import +from grpc.framework.core import _utilities class _EasyOperation(_interfaces.Operation): @@ -75,17 +75,19 @@ class _EasyOperation(_interfaces.Operation): with self._lock: self._reception_manager.receive_ticket(ticket) - def abort(self, outcome): + def abort(self, outcome_kind): with self._lock: if self._termination_manager.outcome is None: + outcome = _utilities.Outcome(outcome_kind, None, None) self._termination_manager.abort(outcome) - self._transmission_manager.abort(outcome, None, None) + self._transmission_manager.abort(outcome) self._expiration_manager.terminate() def invocation_operate( - operation_id, group, method, subscription, timeout, initial_metadata, - payload, completion, ticket_sink, termination_action, pool): + operation_id, group, method, subscription, timeout, protocol_options, + initial_metadata, payload, completion, ticket_sink, termination_action, + pool): """Constructs objects necessary for front-side operation management. Args: @@ -95,6 +97,8 @@ def invocation_operate( subscription: A base.Subscription describing the customer's interest in the results of the operation. timeout: A length of time in seconds to allow for the operation. + protocol_options: A transport-specific, application-specific, and/or + protocol-specific value relating to the invocation. May be None. initial_metadata: An initial metadata value to be sent to the other side of the operation. May be None if the initial metadata will be passed later or if there will be no initial metadata passed at all. @@ -120,23 +124,27 @@ def invocation_operate( operation_id, ticket_sink, lock, pool, termination_manager) expiration_manager = _expiration.invocation_expiration_manager( timeout, lock, termination_manager, transmission_manager) + protocol_manager = _protocol.invocation_protocol_manager( + subscription, lock, pool, termination_manager, transmission_manager, + expiration_manager) operation_context = _context.OperationContext( lock, termination_manager, transmission_manager, expiration_manager) emission_manager = _emission.EmissionManager( lock, termination_manager, transmission_manager, expiration_manager) ingestion_manager = _ingestion.invocation_ingestion_manager( subscription, lock, pool, termination_manager, transmission_manager, - expiration_manager) + expiration_manager, protocol_manager) reception_manager = _reception.ReceptionManager( termination_manager, transmission_manager, expiration_manager, - ingestion_manager) + protocol_manager, ingestion_manager) termination_manager.set_expiration_manager(expiration_manager) transmission_manager.set_expiration_manager(expiration_manager) emission_manager.set_ingestion_manager(ingestion_manager) transmission_manager.kick_off( - group, method, timeout, initial_metadata, payload, completion, None) + group, method, timeout, protocol_options, initial_metadata, payload, + completion, None) return _EasyOperation( lock, termination_manager, transmission_manager, expiration_manager, @@ -170,16 +178,20 @@ def service_operate( ticket.timeout, servicer_package.default_timeout, servicer_package.maximum_timeout, lock, termination_manager, transmission_manager) + protocol_manager = _protocol.service_protocol_manager( + lock, pool, termination_manager, transmission_manager, + expiration_manager) operation_context = _context.OperationContext( lock, termination_manager, transmission_manager, expiration_manager) emission_manager = _emission.EmissionManager( lock, termination_manager, transmission_manager, expiration_manager) ingestion_manager = _ingestion.service_ingestion_manager( servicer_package.servicer, operation_context, emission_manager, lock, - pool, termination_manager, transmission_manager, expiration_manager) + pool, termination_manager, transmission_manager, expiration_manager, + protocol_manager) reception_manager = _reception.ReceptionManager( termination_manager, transmission_manager, expiration_manager, - ingestion_manager) + protocol_manager, ingestion_manager) termination_manager.set_expiration_manager(expiration_manager) transmission_manager.set_expiration_manager(expiration_manager) diff --git a/src/python/grpcio/grpc/framework/core/_protocol.py b/src/python/grpcio/grpc/framework/core/_protocol.py new file mode 100644 index 0000000000..3177b5e302 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_protocol.py @@ -0,0 +1,176 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""State and behavior for passing protocol objects in an operation.""" + +import collections +import enum + +from grpc.framework.core import _constants +from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities +from grpc.framework.foundation import callable_util +from grpc.framework.interfaces.base import base + +_EXCEPTION_LOG_MESSAGE = 'Exception delivering protocol object!' + +_LOCAL_FAILURE_OUTCOME = _utilities.Outcome( + base.Outcome.Kind.LOCAL_FAILURE, None, None) + + +class _Awaited( + collections.namedtuple('_Awaited', ('kind', 'value',))): + + @enum.unique + class Kind(enum.Enum): + NOT_YET_ARRIVED = 'not yet arrived' + ARRIVED = 'arrived' + +_NOT_YET_ARRIVED = _Awaited(_Awaited.Kind.NOT_YET_ARRIVED, None) +_ARRIVED_AND_NONE = _Awaited(_Awaited.Kind.ARRIVED, None) + + +class _Transitory( + collections.namedtuple('_Transitory', ('kind', 'value',))): + + @enum.unique + class Kind(enum.Enum): + NOT_YET_SEEN = 'not yet seen' + PRESENT = 'present' + GONE = 'gone' + +_NOT_YET_SEEN = _Transitory(_Transitory.Kind.NOT_YET_SEEN, None) +_GONE = _Transitory(_Transitory.Kind.GONE, None) + + +class _ProtocolManager(_interfaces.ProtocolManager): + """An implementation of _interfaces.ExpirationManager.""" + + def __init__( + self, protocol_receiver, lock, pool, termination_manager, + transmission_manager, expiration_manager): + """Constructor. + + Args: + protocol_receiver: An _Awaited wrapping of the base.ProtocolReceiver to + which protocol objects should be passed during the operation. May be + of kind _Awaited.Kind.NOT_YET_ARRIVED if the customer's subscription is + not yet known and may be of kind _Awaited.Kind.ARRIVED but with a value + of None if the customer's subscription did not include a + ProtocolReceiver. + lock: The operation-wide lock. + pool: A thread pool. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._lock = lock + self._pool = pool + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = expiration_manager + + self._protocol_receiver = protocol_receiver + self._context = _NOT_YET_SEEN + + def _abort_and_notify(self, outcome): + if self._termination_manager.outcome is None: + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.terminate() + + def _deliver(self, behavior, value): + def deliver(): + delivery_outcome = callable_util.call_logging_exceptions( + behavior, _EXCEPTION_LOG_MESSAGE, value) + if delivery_outcome.kind is callable_util.Outcome.Kind.RAISED: + with self._lock: + self._abort_and_notify(_LOCAL_FAILURE_OUTCOME) + self._pool.submit( + callable_util.with_exceptions_logged( + deliver, _constants.INTERNAL_ERROR_LOG_MESSAGE)) + + def set_protocol_receiver(self, protocol_receiver): + """See _interfaces.ProtocolManager.set_protocol_receiver for spec.""" + self._protocol_receiver = _Awaited(_Awaited.Kind.ARRIVED, protocol_receiver) + if (self._context.kind is _Transitory.Kind.PRESENT and + protocol_receiver is not None): + self._deliver(protocol_receiver.context, self._context.value) + self._context = _GONE + + def accept_protocol_context(self, protocol_context): + """See _interfaces.ProtocolManager.accept_protocol_context for spec.""" + if self._protocol_receiver.kind is _Awaited.Kind.ARRIVED: + if self._protocol_receiver.value is not None: + self._deliver(self._protocol_receiver.value.context, protocol_context) + self._context = _GONE + else: + self._context = _Transitory(_Transitory.Kind.PRESENT, protocol_context) + + +def invocation_protocol_manager( + subscription, lock, pool, termination_manager, transmission_manager, + expiration_manager): + """Creates an _interfaces.ProtocolManager for invocation-side use. + + Args: + subscription: The local customer's subscription to the operation. + lock: The operation-wide lock. + pool: A thread pool. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + if subscription.kind is base.Subscription.Kind.FULL: + awaited_protocol_receiver = _Awaited( + _Awaited.Kind.ARRIVED, subscription.protocol_receiver) + else: + awaited_protocol_receiver = _ARRIVED_AND_NONE + return _ProtocolManager( + awaited_protocol_receiver, lock, pool, termination_manager, + transmission_manager, expiration_manager) + + +def service_protocol_manager( + lock, pool, termination_manager, transmission_manager, expiration_manager): + """Creates an _interfaces.ProtocolManager for service-side use. + + Args: + lock: The operation-wide lock. + pool: A thread pool. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + return _ProtocolManager( + _NOT_YET_ARRIVED, lock, pool, termination_manager, transmission_manager, + expiration_manager) diff --git a/src/python/grpcio/grpc/framework/core/_reception.py b/src/python/grpcio/grpc/framework/core/_reception.py index 1cebe3874b..ff81450dee 100644 --- a/src/python/grpcio/grpc/framework/core/_reception.py +++ b/src/python/grpcio/grpc/framework/core/_reception.py @@ -30,39 +30,52 @@ """State and behavior for ticket reception.""" from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities from grpc.framework.interfaces.base import base from grpc.framework.interfaces.base import utilities from grpc.framework.interfaces.links import links -_REMOTE_TICKET_TERMINATION_TO_LOCAL_OUTCOME = { - links.Ticket.Termination.CANCELLATION: base.Outcome.CANCELLED, - links.Ticket.Termination.EXPIRATION: base.Outcome.EXPIRED, - links.Ticket.Termination.SHUTDOWN: base.Outcome.REMOTE_SHUTDOWN, - links.Ticket.Termination.RECEPTION_FAILURE: base.Outcome.RECEPTION_FAILURE, +_REMOTE_TICKET_TERMINATION_TO_LOCAL_OUTCOME_KIND = { + links.Ticket.Termination.CANCELLATION: base.Outcome.Kind.CANCELLED, + links.Ticket.Termination.EXPIRATION: base.Outcome.Kind.EXPIRED, + links.Ticket.Termination.SHUTDOWN: base.Outcome.Kind.REMOTE_SHUTDOWN, + links.Ticket.Termination.RECEPTION_FAILURE: + base.Outcome.Kind.RECEPTION_FAILURE, links.Ticket.Termination.TRANSMISSION_FAILURE: - base.Outcome.TRANSMISSION_FAILURE, - links.Ticket.Termination.LOCAL_FAILURE: base.Outcome.REMOTE_FAILURE, - links.Ticket.Termination.REMOTE_FAILURE: base.Outcome.LOCAL_FAILURE, + base.Outcome.Kind.TRANSMISSION_FAILURE, + links.Ticket.Termination.LOCAL_FAILURE: base.Outcome.Kind.REMOTE_FAILURE, + links.Ticket.Termination.REMOTE_FAILURE: base.Outcome.Kind.LOCAL_FAILURE, } +_RECEPTION_FAILURE_OUTCOME = _utilities.Outcome( + base.Outcome.Kind.RECEPTION_FAILURE, None, None) + + +def _carrying_protocol_context(ticket): + return ticket.protocol is not None and ticket.protocol.kind in ( + links.Protocol.Kind.INVOCATION_CONTEXT, + links.Protocol.Kind.SERVICER_CONTEXT,) + class ReceptionManager(_interfaces.ReceptionManager): """A ReceptionManager based around a _Receiver passed to it.""" def __init__( self, termination_manager, transmission_manager, expiration_manager, - ingestion_manager): + protocol_manager, ingestion_manager): """Constructor. Args: termination_manager: The operation's _interfaces.TerminationManager. transmission_manager: The operation's _interfaces.TransmissionManager. expiration_manager: The operation's _interfaces.ExpirationManager. + protocol_manager: The operation's _interfaces.ProtocolManager. ingestion_manager: The operation's _interfaces.IngestionManager. """ self._termination_manager = termination_manager self._transmission_manager = transmission_manager self._expiration_manager = expiration_manager + self._protocol_manager = protocol_manager self._ingestion_manager = ingestion_manager self._lowest_unseen_sequence_number = 0 @@ -73,7 +86,7 @@ class ReceptionManager(_interfaces.ReceptionManager): self._aborted = True if self._termination_manager.outcome is None: self._termination_manager.abort(outcome) - self._transmission_manager.abort(None, None, None) + self._transmission_manager.abort(None) self._expiration_manager.terminate() def _sequence_failure(self, ticket): @@ -95,6 +108,10 @@ class ReceptionManager(_interfaces.ReceptionManager): def _process_one(self, ticket): if ticket.sequence_number == 0: self._ingestion_manager.set_group_and_method(ticket.group, ticket.method) + if _carrying_protocol_context(ticket): + self._protocol_manager.accept_protocol_context(ticket.protocol.value) + else: + self._protocol_manager.accept_protocol_context(None) if ticket.timeout is not None: self._expiration_manager.change_timeout(ticket.timeout) if ticket.termination is None: @@ -102,6 +119,7 @@ class ReceptionManager(_interfaces.ReceptionManager): else: completion = utilities.completion( ticket.terminal_metadata, ticket.code, ticket.message) + self._termination_manager.reception_complete(ticket.code, ticket.message) self._ingestion_manager.advance( ticket.initial_metadata, ticket.payload, completion, ticket.allowance) if ticket.allowance is not None: @@ -129,10 +147,12 @@ class ReceptionManager(_interfaces.ReceptionManager): if self._aborted: return elif self._sequence_failure(ticket): - self._abort(base.Outcome.RECEPTION_FAILURE) + self._abort(_RECEPTION_FAILURE_OUTCOME) elif ticket.termination not in (None, links.Ticket.Termination.COMPLETION): - outcome = _REMOTE_TICKET_TERMINATION_TO_LOCAL_OUTCOME[ticket.termination] - self._abort(outcome) + outcome_kind = _REMOTE_TICKET_TERMINATION_TO_LOCAL_OUTCOME_KIND[ + ticket.termination] + self._abort( + _utilities.Outcome(outcome_kind, ticket.code, ticket.message)) elif ticket.sequence_number == self._lowest_unseen_sequence_number: self._process(ticket) else: diff --git a/src/python/grpcio/grpc/framework/core/_termination.py b/src/python/grpcio/grpc/framework/core/_termination.py index ad9f6123d8..bdb9147e5b 100644 --- a/src/python/grpcio/grpc/framework/core/_termination.py +++ b/src/python/grpcio/grpc/framework/core/_termination.py @@ -33,6 +33,7 @@ import abc from grpc.framework.core import _constants from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities from grpc.framework.foundation import callable_util from grpc.framework.interfaces.base import base @@ -74,7 +75,8 @@ class _TerminationManager(TerminationManager): predicate: One of _invocation_completion_predicate or _service_completion_predicate to be used to determine when the operation has completed. - action: A behavior to pass the operation outcome on operation termination. + action: A behavior to pass the operation outcome's kind on operation + termination. pool: A thread pool. """ self._predicate = predicate @@ -82,14 +84,19 @@ class _TerminationManager(TerminationManager): self._pool = pool self._expiration_manager = None - self.outcome = None self._callbacks = [] + self._code = None + self._details = None self._emission_complete = False self._transmission_complete = False self._reception_complete = False self._ingestion_complete = False + # The None-ness of outcome is the operation-wide record of whether and how + # the operation has terminated. + self.outcome = None + def set_expiration_manager(self, expiration_manager): self._expiration_manager = expiration_manager @@ -106,8 +113,10 @@ class _TerminationManager(TerminationManager): act = callable_util.with_exceptions_logged( self._action, _constants.INTERNAL_ERROR_LOG_MESSAGE) - if outcome is base.Outcome.LOCAL_FAILURE: - self._pool.submit(act, outcome) + # TODO(issue 3202): Don't call the local application's callbacks if it has + # previously shown a programming defect. + if False and outcome.kind is base.Outcome.Kind.LOCAL_FAILURE: + self._pool.submit(act, base.Outcome.Kind.LOCAL_FAILURE) else: def call_callbacks_and_act(callbacks, outcome): for callback in callbacks: @@ -115,9 +124,11 @@ class _TerminationManager(TerminationManager): callback, _constants.TERMINATION_CALLBACK_EXCEPTION_LOG_MESSAGE, outcome) if callback_outcome.exception is not None: - outcome = base.Outcome.LOCAL_FAILURE + act_outcome_kind = base.Outcome.Kind.LOCAL_FAILURE break - act(outcome) + else: + act_outcome_kind = outcome.kind + act(act_outcome_kind) self._pool.submit( callable_util.with_exceptions_logged( @@ -132,7 +143,9 @@ class _TerminationManager(TerminationManager): if self._predicate( self._emission_complete, self._transmission_complete, self._reception_complete, self._ingestion_complete): - self._terminate_and_notify(base.Outcome.COMPLETED) + self._terminate_and_notify( + _utilities.Outcome( + base.Outcome.Kind.COMPLETED, self._code, self._details)) return True else: return False @@ -163,10 +176,12 @@ class _TerminationManager(TerminationManager): else: return False - def reception_complete(self): + def reception_complete(self, code, details): """See superclass method for specification.""" if self.outcome is None: self._reception_complete = True + self._code = code + self._details = details self._perhaps_complete() def ingestion_complete(self): @@ -177,7 +192,8 @@ class _TerminationManager(TerminationManager): def expire(self): """See _interfaces.TerminationManager.expire for specification.""" - self._terminate_internal_only(base.Outcome.EXPIRED) + self._terminate_internal_only( + _utilities.Outcome(base.Outcome.Kind.EXPIRED, None, None)) def abort(self, outcome): """See _interfaces.TerminationManager.abort for specification.""" diff --git a/src/python/grpcio/grpc/framework/core/_transmission.py b/src/python/grpcio/grpc/framework/core/_transmission.py index 202a71dd71..65b12c4160 100644 --- a/src/python/grpcio/grpc/framework/core/_transmission.py +++ b/src/python/grpcio/grpc/framework/core/_transmission.py @@ -34,12 +34,16 @@ import enum from grpc.framework.core import _constants from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities from grpc.framework.foundation import callable_util from grpc.framework.interfaces.base import base from grpc.framework.interfaces.links import links _TRANSMISSION_EXCEPTION_LOG_MESSAGE = 'Exception during transmission!' +_TRANSMISSION_FAILURE_OUTCOME = _utilities.Outcome( + base.Outcome.Kind.TRANSMISSION_FAILURE, None, None) + def _explode_completion(completion): if completion is None: @@ -194,7 +198,7 @@ class TransmissionManager(_interfaces.TransmissionManager): with self._lock: self._abort = _ABORTED_NO_NOTIFY if self._termination_manager.outcome is None: - self._termination_manager.abort(base.Outcome.TRANSMISSION_FAILURE) + self._termination_manager.abort(_TRANSMISSION_FAILURE_OUTCOME) self._expiration_manager.terminate() return @@ -203,18 +207,19 @@ class TransmissionManager(_interfaces.TransmissionManager): self._transmitting = True def kick_off( - self, group, method, timeout, initial_metadata, payload, completion, - allowance): + self, group, method, timeout, protocol_options, initial_metadata, + payload, completion, allowance): """See _interfaces.TransmissionManager.kickoff for specification.""" # TODO(nathaniel): Support other subscriptions. subscription = links.Ticket.Subscription.FULL terminal_metadata, code, message, termination = _explode_completion( completion) self._remote_allowance = 1 if payload is None else 0 + protocol = links.Protocol(links.Protocol.Kind.CALL_OPTION, protocol_options) ticket = links.Ticket( self._operation_id, 0, group, method, subscription, timeout, allowance, initial_metadata, payload, terminal_metadata, code, message, - termination, None) + termination, protocol) self._lowest_unused_sequence_number = 1 self._transmit(ticket) @@ -307,19 +312,24 @@ class TransmissionManager(_interfaces.TransmissionManager): self._remote_complete = True self._local_allowance = 0 - def abort(self, outcome, code, message): + def abort(self, outcome): """See _interfaces.TransmissionManager.abort for specification.""" if self._abort.kind is _Abort.Kind.NOT_ABORTED: - termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION.get( - outcome) - if termination is None: + if outcome is None: self._abort = _ABORTED_NO_NOTIFY - elif self._transmitting: - self._abort = _Abort( - _Abort.Kind.ABORTED_NOTIFY_NEEDED, termination, code, message) else: - ticket = links.Ticket( - self._operation_id, self._lowest_unused_sequence_number, None, - None, None, None, None, None, None, None, code, message, - termination, None) - self._transmit(ticket) + termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION.get( + outcome.kind) + if termination is None: + self._abort = _ABORTED_NO_NOTIFY + elif self._transmitting: + self._abort = _Abort( + _Abort.Kind.ABORTED_NOTIFY_NEEDED, termination, outcome.code, + outcome.details) + else: + ticket = links.Ticket( + self._operation_id, self._lowest_unused_sequence_number, None, + None, None, None, None, None, None, None, outcome.code, + outcome.details, termination, None) + self._transmit(ticket) + self._abort = _ABORTED_NO_NOTIFY diff --git a/src/python/grpcio/grpc/framework/core/_utilities.py b/src/python/grpcio/grpc/framework/core/_utilities.py index 5b0d798751..abedc727e4 100644 --- a/src/python/grpcio/grpc/framework/core/_utilities.py +++ b/src/python/grpcio/grpc/framework/core/_utilities.py @@ -31,6 +31,8 @@ import collections +from grpc.framework.interfaces.base import base + class ServicerPackage( collections.namedtuple( @@ -44,3 +46,9 @@ class ServicerPackage( maximum_timeout: A float indicating the maximum length of time in seconds to allow for an operation. """ + + +class Outcome( + base.Outcome, + collections.namedtuple('Outcome', ('kind', 'code', 'details',))): + """A trivial implementation of base.Outcome.""" diff --git a/src/python/grpcio/grpc/framework/crust/_calls.py b/src/python/grpcio/grpc/framework/crust/_calls.py index 4c6bf16f43..bff940d747 100644 --- a/src/python/grpcio/grpc/framework/crust/_calls.py +++ b/src/python/grpcio/grpc/framework/crust/_calls.py @@ -38,10 +38,14 @@ _ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!' _EMPTY_COMPLETION = utilities.completion(None, None, None) -def _invoke(end, group, method, timeout, initial_metadata, payload, complete): +def _invoke( + end, group, method, timeout, protocol_options, initial_metadata, payload, + complete): rendezvous = _control.Rendezvous(None, None) + subscription = utilities.full_subscription( + rendezvous, _control.protocol_receiver(rendezvous)) operation_context, operator = end.operate( - group, method, utilities.full_subscription(rendezvous), timeout, + group, method, subscription, timeout, protocol_options=protocol_options, initial_metadata=initial_metadata, payload=payload, completion=_EMPTY_COMPLETION if complete else None) rendezvous.set_operator_and_context(operator, operation_context) @@ -93,36 +97,43 @@ def _event_return_stream( def blocking_unary_unary( - end, group, method, timeout, with_call, initial_metadata, payload): + end, group, method, timeout, with_call, protocol_options, initial_metadata, + payload): """Services in a blocking fashion a unary-unary servicer method.""" rendezvous, unused_operation_context, unused_outcome = _invoke( - end, group, method, timeout, initial_metadata, payload, True) + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) if with_call: return next(rendezvous), rendezvous else: return next(rendezvous) -def future_unary_unary(end, group, method, timeout, initial_metadata, payload): +def future_unary_unary( + end, group, method, timeout, protocol_options, initial_metadata, payload): """Services a value-in value-out servicer method by returning a Future.""" rendezvous, unused_operation_context, unused_outcome = _invoke( - end, group, method, timeout, initial_metadata, payload, True) + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) return rendezvous -def inline_unary_stream(end, group, method, timeout, initial_metadata, payload): +def inline_unary_stream( + end, group, method, timeout, protocol_options, initial_metadata, payload): """Services a value-in stream-out servicer method.""" rendezvous, unused_operation_context, unused_outcome = _invoke( - end, group, method, timeout, initial_metadata, payload, True) + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) return rendezvous def blocking_stream_unary( - end, group, method, timeout, with_call, initial_metadata, payload_iterator, - pool): + end, group, method, timeout, with_call, protocol_options, initial_metadata, + payload_iterator, pool): """Services in a blocking fashion a stream-in value-out servicer method.""" rendezvous, operation_context, outcome = _invoke( - end, group, method, timeout, initial_metadata, None, False) + end, group, method, timeout, protocol_options, initial_metadata, None, + False) if outcome is None: def in_pool(): for payload in payload_iterator: @@ -141,10 +152,12 @@ def blocking_stream_unary( def future_stream_unary( - end, group, method, timeout, initial_metadata, payload_iterator, pool): + end, group, method, timeout, protocol_options, initial_metadata, + payload_iterator, pool): """Services a stream-in value-out servicer method by returning a Future.""" rendezvous, operation_context, outcome = _invoke( - end, group, method, timeout, initial_metadata, None, False) + end, group, method, timeout, protocol_options, initial_metadata, None, + False) if outcome is None: def in_pool(): for payload in payload_iterator: @@ -155,10 +168,12 @@ def future_stream_unary( def inline_stream_stream( - end, group, method, timeout, initial_metadata, payload_iterator, pool): + end, group, method, timeout, protocol_options, initial_metadata, + payload_iterator, pool): """Services a stream-in stream-out servicer method.""" rendezvous, operation_context, outcome = _invoke( - end, group, method, timeout, initial_metadata, None, False) + end, group, method, timeout, protocol_options, initial_metadata, None, + False) if outcome is None: def in_pool(): for payload in payload_iterator: @@ -169,36 +184,40 @@ def inline_stream_stream( def event_unary_unary( - end, group, method, timeout, initial_metadata, payload, receiver, - abortion_callback, pool): + end, group, method, timeout, protocol_options, initial_metadata, payload, + receiver, abortion_callback, pool): rendezvous, operation_context, outcome = _invoke( - end, group, method, timeout, initial_metadata, payload, True) + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) return _event_return_unary( receiver, abortion_callback, rendezvous, operation_context, outcome, pool) def event_unary_stream( - end, group, method, timeout, initial_metadata, payload, + end, group, method, timeout, protocol_options, initial_metadata, payload, receiver, abortion_callback, pool): rendezvous, operation_context, outcome = _invoke( - end, group, method, timeout, initial_metadata, payload, True) + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) return _event_return_stream( receiver, abortion_callback, rendezvous, operation_context, outcome, pool) def event_stream_unary( - end, group, method, timeout, initial_metadata, receiver, abortion_callback, - pool): + end, group, method, timeout, protocol_options, initial_metadata, receiver, + abortion_callback, pool): rendezvous, operation_context, outcome = _invoke( - end, group, method, timeout, initial_metadata, None, False) + end, group, method, timeout, protocol_options, initial_metadata, None, + False) return _event_return_unary( receiver, abortion_callback, rendezvous, operation_context, outcome, pool) def event_stream_stream( - end, group, method, timeout, initial_metadata, receiver, abortion_callback, - pool): + end, group, method, timeout, protocol_options, initial_metadata, receiver, + abortion_callback, pool): rendezvous, operation_context, outcome = _invoke( - end, group, method, timeout, initial_metadata, None, False) + end, group, method, timeout, protocol_options, initial_metadata, None, + False) return _event_return_stream( receiver, abortion_callback, rendezvous, operation_context, outcome, pool) diff --git a/src/python/grpcio/grpc/framework/crust/_control.py b/src/python/grpcio/grpc/framework/crust/_control.py index 01de3c15bd..5e9efdf732 100644 --- a/src/python/grpcio/grpc/framework/crust/_control.py +++ b/src/python/grpcio/grpc/framework/crust/_control.py @@ -110,30 +110,31 @@ class _Termination( _NOT_TERMINATED = _Termination(False, None, None) -_OPERATION_OUTCOME_TO_TERMINATION_CONSTRUCTOR = { - base.Outcome.COMPLETED: lambda *unused_args: _Termination(True, None, None), - base.Outcome.CANCELLED: lambda *args: _Termination( +_OPERATION_OUTCOME_KIND_TO_TERMINATION_CONSTRUCTOR = { + base.Outcome.Kind.COMPLETED: lambda *unused_args: _Termination( + True, None, None), + base.Outcome.Kind.CANCELLED: lambda *args: _Termination( True, face.Abortion(face.Abortion.Kind.CANCELLED, *args), face.CancellationError(*args)), - base.Outcome.EXPIRED: lambda *args: _Termination( + base.Outcome.Kind.EXPIRED: lambda *args: _Termination( True, face.Abortion(face.Abortion.Kind.EXPIRED, *args), face.ExpirationError(*args)), - base.Outcome.LOCAL_SHUTDOWN: lambda *args: _Termination( + base.Outcome.Kind.LOCAL_SHUTDOWN: lambda *args: _Termination( True, face.Abortion(face.Abortion.Kind.LOCAL_SHUTDOWN, *args), face.LocalShutdownError(*args)), - base.Outcome.REMOTE_SHUTDOWN: lambda *args: _Termination( + base.Outcome.Kind.REMOTE_SHUTDOWN: lambda *args: _Termination( True, face.Abortion(face.Abortion.Kind.REMOTE_SHUTDOWN, *args), face.RemoteShutdownError(*args)), - base.Outcome.RECEPTION_FAILURE: lambda *args: _Termination( + base.Outcome.Kind.RECEPTION_FAILURE: lambda *args: _Termination( True, face.Abortion(face.Abortion.Kind.NETWORK_FAILURE, *args), face.NetworkError(*args)), - base.Outcome.TRANSMISSION_FAILURE: lambda *args: _Termination( + base.Outcome.Kind.TRANSMISSION_FAILURE: lambda *args: _Termination( True, face.Abortion(face.Abortion.Kind.NETWORK_FAILURE, *args), face.NetworkError(*args)), - base.Outcome.LOCAL_FAILURE: lambda *args: _Termination( + base.Outcome.Kind.LOCAL_FAILURE: lambda *args: _Termination( True, face.Abortion(face.Abortion.Kind.LOCAL_FAILURE, *args), face.LocalError(*args)), - base.Outcome.REMOTE_FAILURE: lambda *args: _Termination( + base.Outcome.Kind.REMOTE_FAILURE: lambda *args: _Termination( True, face.Abortion(face.Abortion.Kind.REMOTE_FAILURE, *args), face.RemoteError(*args)), } @@ -181,6 +182,8 @@ class Rendezvous(base.Operator, future.Future, stream.Consumer, face.Call): self._operator = operator self._operation_context = operation_context + self._protocol_context = _NOT_YET_ARRIVED + self._up_initial_metadata = _NOT_YET_ARRIVED self._up_payload = None self._up_allowance = 1 @@ -247,13 +250,17 @@ class Rendezvous(base.Operator, future.Future, stream.Consumer, face.Call): else: initial_metadata = self._up_initial_metadata.value if self._up_completion.kind is _Awaited.Kind.NOT_YET_ARRIVED: - terminal_metadata, code, details = None, None, None + terminal_metadata = None else: terminal_metadata = self._up_completion.value.terminal_metadata + if outcome.kind is base.Outcome.Kind.COMPLETED: code = self._up_completion.value.code details = self._up_completion.value.message - self._termination = _OPERATION_OUTCOME_TO_TERMINATION_CONSTRUCTOR[ - outcome](initial_metadata, terminal_metadata, code, details) + else: + code = outcome.code + details = outcome.details + self._termination = _OPERATION_OUTCOME_KIND_TO_TERMINATION_CONSTRUCTOR[ + outcome.kind](initial_metadata, terminal_metadata, code, details) self._condition.notify_all() @@ -437,6 +444,16 @@ class Rendezvous(base.Operator, future.Future, stream.Consumer, face.Call): else: return self._termination.abortion + def protocol_context(self): + with self._condition: + while True: + if self._protocol_context.kind is _Awaited.Kind.ARRIVED: + return self._protocol_context.value + elif self._termination.abortion_error is not None: + raise self._termination.abortion_error + else: + self._condition.wait() + def initial_metadata(self): with self._condition: while True: @@ -509,11 +526,30 @@ class Rendezvous(base.Operator, future.Future, stream.Consumer, face.Call): else: self._down_details = _Transitory(_Transitory.Kind.PRESENT, details) + def set_protocol_context(self, protocol_context): + with self._condition: + self._protocol_context = _Awaited( + _Awaited.Kind.ARRIVED, protocol_context) + self._condition.notify_all() + def set_outcome(self, outcome): with self._condition: return self._set_outcome(outcome) +class _ProtocolReceiver(base.ProtocolReceiver): + + def __init__(self, rendezvous): + self._rendezvous = rendezvous + + def context(self, protocol_context): + self._rendezvous.set_protocol_context(protocol_context) + + +def protocol_receiver(rendezvous): + return _ProtocolReceiver(rendezvous) + + def pool_wrap(behavior, operation_context): """Wraps an operation-related behavior so that it may be called in a pool. diff --git a/src/python/grpcio/grpc/framework/crust/_service.py b/src/python/grpcio/grpc/framework/crust/_service.py index 6ff7249e75..9903415c09 100644 --- a/src/python/grpcio/grpc/framework/crust/_service.py +++ b/src/python/grpcio/grpc/framework/crust/_service.py @@ -52,6 +52,9 @@ class _ServicerContext(face.ServicerContext): def cancel(self): self._rendezvous.cancel() + def protocol_context(self): + return self._rendezvous.protocol_context() + def invocation_metadata(self): return self._rendezvous.initial_metadata() @@ -71,10 +74,12 @@ class _ServicerContext(face.ServicerContext): def _adaptation(pool, in_pool): def adaptation(operator, operation_context): rendezvous = _control.Rendezvous(operator, operation_context) + subscription = utilities.full_subscription( + rendezvous, _control.protocol_receiver(rendezvous)) outcome = operation_context.add_termination_callback(rendezvous.set_outcome) if outcome is None: pool.submit(_control.pool_wrap(in_pool, operation_context), rendezvous) - return utilities.full_subscription(rendezvous) + return subscription else: raise abandonment.Abandoned() return adaptation @@ -151,6 +156,8 @@ def adapt_event_stream_stream(method, pool): def adapt_multi_method(multi_method, pool): def adaptation(group, method, operator, operation_context): rendezvous = _control.Rendezvous(operator, operation_context) + subscription = utilities.full_subscription( + rendezvous, _control.protocol_receiver(rendezvous)) outcome = operation_context.add_termination_callback(rendezvous.set_outcome) if outcome is None: def in_pool(): @@ -160,7 +167,7 @@ def adapt_multi_method(multi_method, pool): request_consumer.consume(request) request_consumer.terminate() pool.submit(_control.pool_wrap(in_pool, operation_context), rendezvous) - return utilities.full_subscription(rendezvous) + return subscription else: raise abandonment.Abandoned() return adaptation diff --git a/src/python/grpcio/grpc/framework/crust/implementations.py b/src/python/grpcio/grpc/framework/crust/implementations.py index d38fab8ba0..4ebc4e9ae8 100644 --- a/src/python/grpcio/grpc/framework/crust/implementations.py +++ b/src/python/grpcio/grpc/framework/crust/implementations.py @@ -66,22 +66,23 @@ class _UnaryUnaryMultiCallable(face.UnaryUnaryMultiCallable): self._pool = pool def __call__( - self, request, timeout, metadata=None, with_call=False): + self, request, timeout, metadata=None, with_call=False, + protocol_options=None): return _calls.blocking_unary_unary( self._end, self._group, self._method, timeout, with_call, - metadata, request) + protocol_options, metadata, request) - def future(self, request, timeout, metadata=None): + def future(self, request, timeout, metadata=None, protocol_options=None): return _calls.future_unary_unary( - self._end, self._group, self._method, timeout, metadata, - request) + self._end, self._group, self._method, timeout, protocol_options, + metadata, request) def event( self, request, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): return _calls.event_unary_unary( - self._end, self._group, self._method, timeout, metadata, - request, receiver, abortion_callback, self._pool) + self._end, self._group, self._method, timeout, protocol_options, + metadata, request, receiver, abortion_callback, self._pool) class _UnaryStreamMultiCallable(face.UnaryStreamMultiCallable): @@ -92,17 +93,17 @@ class _UnaryStreamMultiCallable(face.UnaryStreamMultiCallable): self._method = method self._pool = pool - def __call__(self, request, timeout, metadata=None): + def __call__(self, request, timeout, metadata=None, protocol_options=None): return _calls.inline_unary_stream( - self._end, self._group, self._method, timeout, metadata, - request) + self._end, self._group, self._method, timeout, protocol_options, + metadata, request) def event( self, request, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): return _calls.event_unary_stream( - self._end, self._group, self._method, timeout, metadata, - request, receiver, abortion_callback, self._pool) + self._end, self._group, self._method, timeout, protocol_options, + metadata, request, receiver, abortion_callback, self._pool) class _StreamUnaryMultiCallable(face.StreamUnaryMultiCallable): @@ -115,21 +116,23 @@ class _StreamUnaryMultiCallable(face.StreamUnaryMultiCallable): def __call__( self, request_iterator, timeout, metadata=None, - with_call=False): + with_call=False, protocol_options=None): return _calls.blocking_stream_unary( self._end, self._group, self._method, timeout, with_call, - metadata, request_iterator, self._pool) + protocol_options, metadata, request_iterator, self._pool) - def future(self, request_iterator, timeout, metadata=None): + def future( + self, request_iterator, timeout, metadata=None, protocol_options=None): return _calls.future_stream_unary( - self._end, self._group, self._method, timeout, metadata, - request_iterator, self._pool) + self._end, self._group, self._method, timeout, protocol_options, + metadata, request_iterator, self._pool) def event( - self, receiver, abortion_callback, timeout, metadata=None): + self, receiver, abortion_callback, timeout, metadata=None, + protocol_options=None): return _calls.event_stream_unary( - self._end, self._group, self._method, timeout, metadata, - receiver, abortion_callback, self._pool) + self._end, self._group, self._method, timeout, protocol_options, + metadata, receiver, abortion_callback, self._pool) class _StreamStreamMultiCallable(face.StreamStreamMultiCallable): @@ -140,16 +143,18 @@ class _StreamStreamMultiCallable(face.StreamStreamMultiCallable): self._method = method self._pool = pool - def __call__(self, request_iterator, timeout, metadata=None): + def __call__( + self, request_iterator, timeout, metadata=None, protocol_options=None): return _calls.inline_stream_stream( - self._end, self._group, self._method, timeout, metadata, - request_iterator, self._pool) + self._end, self._group, self._method, timeout, protocol_options, + metadata, request_iterator, self._pool) def event( - self, receiver, abortion_callback, timeout, metadata=None): + self, receiver, abortion_callback, timeout, metadata=None, + protocol_options=None): return _calls.event_stream_stream( - self._end, self._group, self._method, timeout, metadata, - receiver, abortion_callback, self._pool) + self._end, self._group, self._method, timeout, protocol_options, + metadata, receiver, abortion_callback, self._pool) class _GenericStub(face.GenericStub): @@ -161,66 +166,70 @@ class _GenericStub(face.GenericStub): def blocking_unary_unary( self, group, method, request, timeout, metadata=None, - with_call=None): + with_call=None, protocol_options=None): return _calls.blocking_unary_unary( - self._end, group, method, timeout, with_call, metadata, - request) + self._end, group, method, timeout, with_call, protocol_options, + metadata, request) def future_unary_unary( - self, group, method, request, timeout, metadata=None): + self, group, method, request, timeout, metadata=None, + protocol_options=None): return _calls.future_unary_unary( - self._end, group, method, timeout, metadata, request) + self._end, group, method, timeout, protocol_options, metadata, request) def inline_unary_stream( - self, group, method, request, timeout, metadata=None): + self, group, method, request, timeout, metadata=None, + protocol_options=None): return _calls.inline_unary_stream( - self._end, group, method, timeout, metadata, request) + self._end, group, method, timeout, protocol_options, metadata, request) def blocking_stream_unary( self, group, method, request_iterator, timeout, metadata=None, - with_call=None): + with_call=None, protocol_options=None): return _calls.blocking_stream_unary( - self._end, group, method, timeout, with_call, metadata, - request_iterator, self._pool) + self._end, group, method, timeout, with_call, protocol_options, + metadata, request_iterator, self._pool) def future_stream_unary( - self, group, method, request_iterator, timeout, metadata=None): + self, group, method, request_iterator, timeout, metadata=None, + protocol_options=None): return _calls.future_stream_unary( - self._end, group, method, timeout, metadata, + self._end, group, method, timeout, protocol_options, metadata, request_iterator, self._pool) def inline_stream_stream( - self, group, method, request_iterator, timeout, metadata=None): + self, group, method, request_iterator, timeout, metadata=None, + protocol_options=None): return _calls.inline_stream_stream( - self._end, group, method, timeout, metadata, + self._end, group, method, timeout, protocol_options, metadata, request_iterator, self._pool) def event_unary_unary( self, group, method, request, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): return _calls.event_unary_unary( - self._end, group, method, timeout, metadata, request, + self._end, group, method, timeout, protocol_options, metadata, request, receiver, abortion_callback, self._pool) def event_unary_stream( self, group, method, request, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): return _calls.event_unary_stream( - self._end, group, method, timeout, metadata, request, + self._end, group, method, timeout, protocol_options, metadata, request, receiver, abortion_callback, self._pool) def event_stream_unary( self, group, method, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): return _calls.event_stream_unary( - self._end, group, method, timeout, metadata, receiver, + self._end, group, method, timeout, protocol_options, metadata, receiver, abortion_callback, self._pool) def event_stream_stream( self, group, method, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): return _calls.event_stream_stream( - self._end, group, method, timeout, metadata, receiver, + self._end, group, method, timeout, protocol_options, metadata, receiver, abortion_callback, self._pool) def unary_unary(self, group, method): diff --git a/src/python/grpcio/grpc/framework/face/__init__.py b/src/python/grpcio/grpc/framework/face/__init__.py index 7086519106..bff74be2c7 100644 --- a/src/python/grpcio/grpc/framework/face/__init__.py +++ b/src/python/grpcio/grpc/framework/face/__init__.py @@ -27,4 +27,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import warnings +warnings.simplefilter('always', DeprecationWarning) +warnings.warn('the alpha API (includes this package) is deprecated, ' + 'unmaintained, and no longer tested. Please migrate to the beta ' + 'API.', DeprecationWarning, stacklevel=2) diff --git a/src/python/grpcio/grpc/framework/interfaces/base/base.py b/src/python/grpcio/grpc/framework/interfaces/base/base.py index bc52efb4c5..a1e70be5e8 100644 --- a/src/python/grpcio/grpc/framework/interfaces/base/base.py +++ b/src/python/grpcio/grpc/framework/interfaces/base/base.py @@ -40,7 +40,7 @@ applications choose. # threading is referenced from specification in this module. import abc import enum -import threading +import threading # pylint: disable=unused-import # abandonment is referenced from specification in this module. from grpc.framework.foundation import abandonment # pylint: disable=unused-import @@ -69,19 +69,30 @@ class NoSuchMethodError(Exception): self.details = details -@enum.unique -class Outcome(enum.Enum): - """Operation outcomes.""" +class Outcome(object): + """The outcome of an operation. - COMPLETED = 'completed' - CANCELLED = 'cancelled' - EXPIRED = 'expired' - LOCAL_SHUTDOWN = 'local shutdown' - REMOTE_SHUTDOWN = 'remote shutdown' - RECEPTION_FAILURE = 'reception failure' - TRANSMISSION_FAILURE = 'transmission failure' - LOCAL_FAILURE = 'local failure' - REMOTE_FAILURE = 'remote failure' + Attributes: + kind: A Kind value coarsely identifying how the operation terminated. + code: An application-specific code value or None if no such value was + provided. + details: An application-specific details value or None if no such value was + provided. + """ + + @enum.unique + class Kind(enum.Enum): + """Ways in which an operation can terminate.""" + + COMPLETED = 'completed' + CANCELLED = 'cancelled' + EXPIRED = 'expired' + LOCAL_SHUTDOWN = 'local shutdown' + REMOTE_SHUTDOWN = 'remote shutdown' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + LOCAL_FAILURE = 'local failure' + REMOTE_FAILURE = 'remote failure' class Completion(object): @@ -173,6 +184,19 @@ class Operator(object): """ raise NotImplementedError() +class ProtocolReceiver(object): + """A means of receiving protocol values during an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def context(self, protocol_context): + """Accepts the protocol context object for the operation. + + Args: + protocol_context: The protocol context object for the operation. + """ + raise NotImplementedError() + class Subscription(object): """Describes customer code's interest in values from the other side. @@ -188,7 +212,11 @@ class Subscription(object): otherwise. operator: An Operator to be passed values from the other side of the operation. Must be non-None if kind is Kind.FULL. Must be None otherwise. + protocol_receiver: A ProtocolReceiver to be passed protocol objects as they + become available during the operation. Must be non-None if kind is + Kind.FULL. """ + __metaclass__ = abc.ABCMeta @enum.unique class Kind(enum.Enum): @@ -263,7 +291,7 @@ class End(object): @abc.abstractmethod def operate( self, group, method, subscription, timeout, initial_metadata=None, - payload=None, completion=None): + payload=None, completion=None, protocol_options=None): """Commences an operation. Args: @@ -279,6 +307,8 @@ class End(object): payload: An initial payload for the operation. completion: A Completion value indicating the end of transmission to the other side of the operation. + protocol_options: A value specified by the provider of a Base interface + implementation affording custom state and behavior. Returns: A pair of objects affording information about the operation and action @@ -294,8 +324,8 @@ class End(object): """Reports the number of terminated operations broken down by outcome. Returns: - A dictionary from Outcome value to an integer identifying the number - of operations that terminated with that outcome. + A dictionary from Outcome.Kind value to an integer identifying the number + of operations that terminated with that outcome kind. """ raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/interfaces/base/utilities.py b/src/python/grpcio/grpc/framework/interfaces/base/utilities.py index a9ee1a0981..87a85018f5 100644 --- a/src/python/grpcio/grpc/framework/interfaces/base/utilities.py +++ b/src/python/grpcio/grpc/framework/interfaces/base/utilities.py @@ -45,11 +45,12 @@ class _Subscription( base.Subscription, collections.namedtuple( '_Subscription', - ('kind', 'termination_callback', 'allowance', 'operator',))): + ('kind', 'termination_callback', 'allowance', 'operator', + 'protocol_receiver',))): """A trivial implementation of base.Subscription.""" _NONE_SUBSCRIPTION = _Subscription( - base.Subscription.Kind.NONE, None, None, None) + base.Subscription.Kind.NONE, None, None, None, None) def completion(terminal_metadata, code, message): @@ -66,14 +67,16 @@ def completion(terminal_metadata, code, message): return _Completion(terminal_metadata, code, message) -def full_subscription(operator): +def full_subscription(operator, protocol_receiver): """Creates a "full" base.Subscription for the given base.Operator. Args: operator: A base.Operator to be used in an operation. + protocol_receiver: A base.ProtocolReceiver to be used in an operation. Returns: A base.Subscription of kind base.Subscription.Kind.FULL wrapping the given - base.Operator. + base.Operator and base.ProtocolReceiver. """ - return _Subscription(base.Subscription.Kind.FULL, None, None, operator) + return _Subscription( + base.Subscription.Kind.FULL, None, None, operator, protocol_receiver) diff --git a/src/python/grpcio/grpc/framework/interfaces/face/face.py b/src/python/grpcio/grpc/framework/interfaces/face/face.py index 948e7505b6..bc9a434a76 100644 --- a/src/python/grpcio/grpc/framework/interfaces/face/face.py +++ b/src/python/grpcio/grpc/framework/interfaces/face/face.py @@ -184,6 +184,16 @@ class RpcContext(object): """ raise NotImplementedError() + @abc.abstractmethod + def protocol_context(self): + """Accesses a custom object specified by an implementation provider. + + Returns: + A value specified by the provider of a Face interface implementation + affording custom state and behavior. + """ + raise NotImplementedError() + class Call(RpcContext): """Invocation-side utility object for an RPC.""" @@ -354,7 +364,8 @@ class UnaryUnaryMultiCallable(object): @abc.abstractmethod def __call__( - self, request, timeout, metadata=None, with_call=False): + self, request, timeout, metadata=None, with_call=False, + protocol_options=None): """Synchronously invokes the underlying RPC. Args: @@ -364,6 +375,8 @@ class UnaryUnaryMultiCallable(object): the RPC. with_call: Whether or not to include return a Call for the RPC in addition to the reponse. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: The response value for the RPC, and a Call for the RPC if with_call was @@ -375,7 +388,7 @@ class UnaryUnaryMultiCallable(object): raise NotImplementedError() @abc.abstractmethod - def future(self, request, timeout, metadata=None): + def future(self, request, timeout, metadata=None, protocol_options=None): """Asynchronously invokes the underlying RPC. Args: @@ -383,6 +396,8 @@ class UnaryUnaryMultiCallable(object): timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: An object that is both a Call for the RPC and a future.Future. In the @@ -395,7 +410,7 @@ class UnaryUnaryMultiCallable(object): @abc.abstractmethod def event( self, request, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): """Asynchronously invokes the underlying RPC. Args: @@ -406,6 +421,8 @@ class UnaryUnaryMultiCallable(object): timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: A Call for the RPC. @@ -418,7 +435,7 @@ class UnaryStreamMultiCallable(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod - def __call__(self, request, timeout, metadata=None): + def __call__(self, request, timeout, metadata=None, protocol_options=None): """Invokes the underlying RPC. Args: @@ -426,6 +443,8 @@ class UnaryStreamMultiCallable(object): timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: An object that is both a Call for the RPC and an iterator of response @@ -437,7 +456,7 @@ class UnaryStreamMultiCallable(object): @abc.abstractmethod def event( self, request, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): """Asynchronously invokes the underlying RPC. Args: @@ -448,6 +467,8 @@ class UnaryStreamMultiCallable(object): timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: A Call object for the RPC. @@ -462,7 +483,7 @@ class StreamUnaryMultiCallable(object): @abc.abstractmethod def __call__( self, request_iterator, timeout, metadata=None, - with_call=False): + with_call=False, protocol_options=None): """Synchronously invokes the underlying RPC. Args: @@ -472,6 +493,8 @@ class StreamUnaryMultiCallable(object): the RPC. with_call: Whether or not to include return a Call for the RPC in addition to the reponse. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: The response value for the RPC, and a Call for the RPC if with_call was @@ -483,7 +506,8 @@ class StreamUnaryMultiCallable(object): raise NotImplementedError() @abc.abstractmethod - def future(self, request_iterator, timeout, metadata=None): + def future( + self, request_iterator, timeout, metadata=None, protocol_options=None): """Asynchronously invokes the underlying RPC. Args: @@ -491,6 +515,8 @@ class StreamUnaryMultiCallable(object): timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: An object that is both a Call for the RPC and a future.Future. In the @@ -502,7 +528,8 @@ class StreamUnaryMultiCallable(object): @abc.abstractmethod def event( - self, receiver, abortion_callback, timeout, metadata=None): + self, receiver, abortion_callback, timeout, metadata=None, + protocol_options=None): """Asynchronously invokes the underlying RPC. Args: @@ -512,6 +539,8 @@ class StreamUnaryMultiCallable(object): timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: A single object that is both a Call object for the RPC and a @@ -525,7 +554,8 @@ class StreamStreamMultiCallable(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod - def __call__(self, request_iterator, timeout, metadata=None): + def __call__( + self, request_iterator, timeout, metadata=None, protocol_options=None): """Invokes the underlying RPC. Args: @@ -533,6 +563,8 @@ class StreamStreamMultiCallable(object): timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: An object that is both a Call for the RPC and an iterator of response @@ -543,7 +575,8 @@ class StreamStreamMultiCallable(object): @abc.abstractmethod def event( - self, receiver, abortion_callback, timeout, metadata=None): + self, receiver, abortion_callback, timeout, metadata=None, + protocol_options=None): """Asynchronously invokes the underlying RPC. Args: @@ -553,6 +586,8 @@ class StreamStreamMultiCallable(object): timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: A single object that is both a Call object for the RPC and a @@ -646,7 +681,7 @@ class GenericStub(object): @abc.abstractmethod def blocking_unary_unary( self, group, method, request, timeout, metadata=None, - with_call=False): + with_call=False, protocol_options=None): """Invokes a unary-request-unary-response method. This method blocks until either returning the response value of the RPC @@ -661,6 +696,8 @@ class GenericStub(object): metadata: A metadata value to be passed to the service-side of the RPC. with_call: Whether or not to include return a Call for the RPC in addition to the reponse. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: The response value for the RPC, and a Call for the RPC if with_call was @@ -673,7 +710,8 @@ class GenericStub(object): @abc.abstractmethod def future_unary_unary( - self, group, method, request, timeout, metadata=None): + self, group, method, request, timeout, metadata=None, + protocol_options=None): """Invokes a unary-request-unary-response method. Args: @@ -682,6 +720,8 @@ class GenericStub(object): request: The request value for the RPC. timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: An object that is both a Call for the RPC and a future.Future. In the @@ -693,7 +733,8 @@ class GenericStub(object): @abc.abstractmethod def inline_unary_stream( - self, group, method, request, timeout, metadata=None): + self, group, method, request, timeout, metadata=None, + protocol_options=None): """Invokes a unary-request-stream-response method. Args: @@ -702,6 +743,8 @@ class GenericStub(object): request: The request value for the RPC. timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: An object that is both a Call for the RPC and an iterator of response @@ -713,7 +756,7 @@ class GenericStub(object): @abc.abstractmethod def blocking_stream_unary( self, group, method, request_iterator, timeout, metadata=None, - with_call=False): + with_call=False, protocol_options=None): """Invokes a stream-request-unary-response method. This method blocks until either returning the response value of the RPC @@ -728,6 +771,8 @@ class GenericStub(object): metadata: A metadata value to be passed to the service-side of the RPC. with_call: Whether or not to include return a Call for the RPC in addition to the reponse. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: The response value for the RPC, and a Call for the RPC if with_call was @@ -740,7 +785,8 @@ class GenericStub(object): @abc.abstractmethod def future_stream_unary( - self, group, method, request_iterator, timeout, metadata=None): + self, group, method, request_iterator, timeout, metadata=None, + protocol_options=None): """Invokes a stream-request-unary-response method. Args: @@ -749,6 +795,8 @@ class GenericStub(object): request_iterator: An iterator that yields request values for the RPC. timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: An object that is both a Call for the RPC and a future.Future. In the @@ -760,7 +808,8 @@ class GenericStub(object): @abc.abstractmethod def inline_stream_stream( - self, group, method, request_iterator, timeout, metadata=None): + self, group, method, request_iterator, timeout, metadata=None, + protocol_options=None): """Invokes a stream-request-stream-response method. Args: @@ -769,6 +818,8 @@ class GenericStub(object): request_iterator: An iterator that yields request values for the RPC. timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: An object that is both a Call for the RPC and an iterator of response @@ -780,7 +831,7 @@ class GenericStub(object): @abc.abstractmethod def event_unary_unary( self, group, method, request, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): """Event-driven invocation of a unary-request-unary-response method. Args: @@ -792,6 +843,8 @@ class GenericStub(object): in the event of RPC abortion. timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: A Call for the RPC. @@ -801,7 +854,7 @@ class GenericStub(object): @abc.abstractmethod def event_unary_stream( self, group, method, request, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): """Event-driven invocation of a unary-request-stream-response method. Args: @@ -813,6 +866,8 @@ class GenericStub(object): in the event of RPC abortion. timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: A Call for the RPC. @@ -822,7 +877,7 @@ class GenericStub(object): @abc.abstractmethod def event_stream_unary( self, group, method, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): """Event-driven invocation of a unary-request-unary-response method. Args: @@ -833,6 +888,8 @@ class GenericStub(object): in the event of RPC abortion. timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: A pair of a Call object for the RPC and a stream.Consumer to which the @@ -843,7 +900,7 @@ class GenericStub(object): @abc.abstractmethod def event_stream_stream( self, group, method, receiver, abortion_callback, timeout, - metadata=None): + metadata=None, protocol_options=None): """Event-driven invocation of a unary-request-stream-response method. Args: @@ -854,6 +911,8 @@ class GenericStub(object): in the event of RPC abortion. timeout: A duration of time in seconds to allow for the RPC. metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. Returns: A pair of a Call object for the RPC and a stream.Consumer to which the diff --git a/src/python/grpcio/grpc/framework/interfaces/links/links.py b/src/python/grpcio/grpc/framework/interfaces/links/links.py index b98a30a399..24f0e3b354 100644 --- a/src/python/grpcio/grpc/framework/interfaces/links/links.py +++ b/src/python/grpcio/grpc/framework/interfaces/links/links.py @@ -34,14 +34,13 @@ import collections import enum -class Transport(collections.namedtuple('Transport', ('kind', 'value',))): - """A sum type for handles to an underlying transport system. +class Protocol(collections.namedtuple('Protocol', ('kind', 'value',))): + """A sum type for handles to a system that transmits tickets. Attributes: - kind: A Kind value identifying the kind of value being passed to or from - the underlying transport. - value: The value being passed through RPC Framework between the high-level - application and the underlying transport. + kind: A Kind value identifying the kind of value being passed. + value: The value being passed between the high-level application and the + system affording ticket transport. """ @enum.unique @@ -56,8 +55,7 @@ class Ticket( 'Ticket', ('operation_id', 'sequence_number', 'group', 'method', 'subscription', 'timeout', 'allowance', 'initial_metadata', 'payload', - 'terminal_metadata', 'code', 'message', 'termination', - 'transport',))): + 'terminal_metadata', 'code', 'message', 'termination', 'protocol',))): """A sum type for all values sent from a front to a back. Attributes: @@ -99,8 +97,8 @@ class Ticket( termination: A Termination value describing the end of the operation, or None if the operation has not yet terminated. If set, no further tickets may be sent in the same direction. - transport: A Transport value or None, with further semantics being a matter - between high-level application and underlying transport. + protocol: A Protocol value or None, with further semantics being a matter + between high-level application and underlying ticket transport. """ @enum.unique diff --git a/src/python/grpcio/requirements.txt b/src/python/grpcio/requirements.txt index 43395df03b..ee8568120b 100644 --- a/src/python/grpcio/requirements.txt +++ b/src/python/grpcio/requirements.txt @@ -1,3 +1,3 @@ -enum34==1.0.4 -futures==2.2.0 -protobuf==3.0.0a3 +enum34>=1.0.4 +futures>=2.2.0 +cython>=0.23 diff --git a/src/python/grpcio/setup.py b/src/python/grpcio/setup.py index caa71a4f7c..97fa4fe6b3 100644 --- a/src/python/grpcio/setup.py +++ b/src/python/grpcio/setup.py @@ -34,6 +34,7 @@ import os.path import sys from distutils import core as _core +from distutils import extension as _extension import setuptools # Ensure we're in the proper directory whether or not we're being used by pip. @@ -59,6 +60,18 @@ _C_EXTENSION_SOURCES = ( 'grpc/_adapter/_c/types/server.c', ) +_CYTHON_EXTENSION_PACKAGE_NAMES = () + +_CYTHON_EXTENSION_MODULE_NAMES = ( + 'grpc._cython.cygrpc', + 'grpc._cython._cygrpc.call', + 'grpc._cython._cygrpc.channel', + 'grpc._cython._cygrpc.completion_queue', + 'grpc._cython._cygrpc.credentials', + 'grpc._cython._cygrpc.records', + 'grpc._cython._cygrpc.server', +) + _EXTENSION_INCLUDE_DIRECTORIES = ( '.', ) @@ -78,18 +91,38 @@ _C_EXTENSION_MODULE = _core.Extension( ) _EXTENSION_MODULES = [_C_EXTENSION_MODULE] -_PACKAGES = ( - setuptools.find_packages('.', exclude=['*._cython', '*._cython.*']) -) + +def cython_extensions(package_names, module_names, include_dirs, libraries, + build_with_cython=False): + file_extension = 'pyx' if build_with_cython else 'c' + module_files = [name.replace('.', '/') + '.' + file_extension + for name in module_names] + extensions = [ + _extension.Extension( + name=module_name, sources=[module_file], + include_dirs=include_dirs, libraries=libraries + ) for (module_name, module_file) in zip(module_names, module_files) + ] + if build_with_cython: + import Cython.Build + return Cython.Build.cythonize(extensions) + else: + return extensions + +_CYTHON_EXTENSION_MODULES = cython_extensions( + list(_CYTHON_EXTENSION_PACKAGE_NAMES), list(_CYTHON_EXTENSION_MODULE_NAMES), + list(_EXTENSION_INCLUDE_DIRECTORIES), list(_EXTENSION_LIBRARIES), + bool(_BUILD_WITH_CYTHON)) + +_PACKAGES = setuptools.find_packages('.') _PACKAGE_DIRECTORIES = { '': '.', } _INSTALL_REQUIRES = ( - 'enum34==1.0.4', - 'futures==2.2.0', - 'protobuf==3.0.0a3', + 'enum34>=1.0.4', + 'futures>=2.2.0', ) _SETUP_REQUIRES = ( @@ -104,8 +137,8 @@ _COMMAND_CLASS = { setuptools.setup( name='grpcio', - version='0.10.0a0', - ext_modules=_EXTENSION_MODULES, + version='0.11.0b1', + ext_modules=_EXTENSION_MODULES + _CYTHON_EXTENSION_MODULES, packages=list(_PACKAGES), package_dir=_PACKAGE_DIRECTORIES, install_requires=_INSTALL_REQUIRES, diff --git a/src/python/grpcio_health_checking/commands.py b/src/python/grpcio_health_checking/commands.py index 6a95e679c4..3f4ea6e22f 100644 --- a/src/python/grpcio_health_checking/commands.py +++ b/src/python/grpcio_health_checking/commands.py @@ -50,7 +50,7 @@ class BuildProtoModules(setuptools.Command): pass def finalize_options(self): - self.protoc_command = 'protoc' + self.protoc_command = distutils.spawn.find_executable('protoc') self.grpc_python_plugin_command = distutils.spawn.find_executable( 'grpc_python_plugin') @@ -69,7 +69,11 @@ class BuildProtoModules(setuptools.Command): '--python_out={}'.format(root_directory), '--python-grpc_out={}'.format(root_directory), ] + paths - subprocess.check_call(' '.join(command), cwd=root_directory, shell=True) + try: + subprocess.check_output(' '.join(command), cwd=root_directory, shell=True, + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + raise Exception('{}\nOutput:\n{}'.format(e.message, e.output)) class BuildPy(build_py.build_py): diff --git a/src/python/grpcio_health_checking/setup.py b/src/python/grpcio_health_checking/setup.py index fcde0dab8c..35253ba312 100644 --- a/src/python/grpcio_health_checking/setup.py +++ b/src/python/grpcio_health_checking/setup.py @@ -51,7 +51,7 @@ _PACKAGE_DIRECTORIES = { } _INSTALL_REQUIRES = ( - 'grpcio>=0.10.0a0', + 'grpcio>=0.11.0b0', ) _SETUP_REQUIRES = _INSTALL_REQUIRES @@ -63,7 +63,7 @@ _COMMAND_CLASS = { setuptools.setup( name='grpcio_health_checking', - version='0.10.0a0', + version='0.11.0b0', packages=list(_PACKAGES), package_dir=_PACKAGE_DIRECTORIES, install_requires=_INSTALL_REQUIRES, diff --git a/src/python/grpcio_test/.gitignore b/src/python/grpcio_test/.gitignore index 218e3a15ab..6158313bde 100644 --- a/src/python/grpcio_test/.gitignore +++ b/src/python/grpcio_test/.gitignore @@ -5,6 +5,8 @@ dist/ *.egg *.egg/ *.eggs/ +*_pb2.py .coverage .coverage.* +.cache/ nosetests.xml diff --git a/src/python/grpcio_test/commands.py b/src/python/grpcio_test/commands.py index c796d94c76..edaa2aa72d 100644 --- a/src/python/grpcio_test/commands.py +++ b/src/python/grpcio_test/commands.py @@ -29,11 +29,14 @@ """Provides distutils command classes for the GRPC Python test setup process.""" +import distutils import os import os.path +import subprocess import sys import setuptools +from setuptools.command import build_py class RunTests(setuptools.Command): @@ -52,6 +55,52 @@ class RunTests(setuptools.Command): # We import here to ensure that setup.py has had a chance to install the # relevant package eggs first. import pytest + + self.run_command('build_proto_modules') result = pytest.main(self.pytest_args) if result != 0: raise SystemExit(result) + + +class BuildProtoModules(setuptools.Command): + """Command to generate project *_pb2.py modules from proto files.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + self.protoc_command = distutils.spawn.find_executable('protoc') + self.grpc_python_plugin_command = distutils.spawn.find_executable( + 'grpc_python_plugin') + + def run(self): + paths = [] + root_directory = os.getcwd() + for walk_root, directories, filenames in os.walk(root_directory): + for filename in filenames: + if filename.endswith('.proto'): + paths.append(os.path.join(walk_root, filename)) + command = [ + self.protoc_command, + '--plugin=protoc-gen-python-grpc={}'.format( + self.grpc_python_plugin_command), + '-I {}'.format(root_directory), + '--python_out={}'.format(root_directory), + '--python-grpc_out={}'.format(root_directory), + ] + paths + try: + subprocess.check_output(' '.join(command), cwd=root_directory, shell=True, + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + raise Exception('{}\nOutput:\n{}'.format(e.message, e.output)) + + +class BuildPy(build_py.build_py): + """Custom project build command.""" + + def run(self): + self.run_command('build_proto_modules') + build_py.build_py.run(self) diff --git a/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py b/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py index 825988a072..5007be28ff 100644 --- a/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py +++ b/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py @@ -31,10 +31,12 @@ import unittest -from grpc.early_adopter import implementations +from grpc.beta import implementations from grpc_interop import _interop_test_case from grpc_interop import methods +from grpc_interop import server +from grpc_interop import test_pb2 class InsecureInteropTest( @@ -42,15 +44,14 @@ class InsecureInteropTest( unittest.TestCase): def setUp(self): - self.server = implementations.server( - methods.SERVICE_NAME, methods.SERVER_METHODS, 0) + self.server = test_pb2.beta_create_TestService_server(methods.TestService()) + port = self.server.add_insecure_port('[::]:0') self.server.start() - port = self.server.port() - self.stub = implementations.stub( - methods.SERVICE_NAME, methods.CLIENT_METHODS, 'localhost', port) + self.stub = test_pb2.beta_create_TestService_stub( + implementations.insecure_channel('[::]', port)) def tearDown(self): - self.server.stop() + self.server.stop(0) if __name__ == '__main__': diff --git a/src/python/grpcio_test/grpc_interop/_secure_interop_test.py b/src/python/grpcio_test/grpc_interop/_secure_interop_test.py index a2682dee99..108e15b0f9 100644 --- a/src/python/grpcio_test/grpc_interop/_secure_interop_test.py +++ b/src/python/grpcio_test/grpc_interop/_secure_interop_test.py @@ -31,11 +31,14 @@ import unittest -from grpc.early_adopter import implementations +from grpc.beta import implementations + +from grpc_test.beta import test_utilities from grpc_interop import _interop_test_case from grpc_interop import methods from grpc_interop import resources +from grpc_interop import test_pb2 _SERVER_HOST_OVERRIDE = 'foo.test.google.fr' @@ -45,19 +48,19 @@ class SecureInteropTest( unittest.TestCase): def setUp(self): - self.server = implementations.server( - methods.SERVICE_NAME, methods.SERVER_METHODS, 0, - private_key=resources.private_key(), - certificate_chain=resources.certificate_chain()) + self.server = test_pb2.beta_create_TestService_server(methods.TestService()) + port = self.server.add_secure_port( + '[::]:0', implementations.ssl_server_credentials( + [(resources.private_key(), resources.certificate_chain())])) self.server.start() - port = self.server.port() - self.stub = implementations.stub( - methods.SERVICE_NAME, methods.CLIENT_METHODS, 'localhost', port, - secure=True, root_certificates=resources.test_root_certificates(), - server_host_override=_SERVER_HOST_OVERRIDE) + self.stub = test_pb2.beta_create_TestService_stub( + test_utilities.not_really_secure_channel( + '[::]', port, implementations.ssl_client_credentials( + resources.test_root_certificates(), None, None), + _SERVER_HOST_OVERRIDE)) def tearDown(self): - self.server.stop() + self.server.stop(0) if __name__ == '__main__': diff --git a/src/python/grpcio_test/grpc_interop/client.py b/src/python/grpcio_test/grpc_interop/client.py index 36afe6c096..b8d5047ca5 100644 --- a/src/python/grpcio_test/grpc_interop/client.py +++ b/src/python/grpcio_test/grpc_interop/client.py @@ -32,10 +32,13 @@ import argparse from oauth2client import client as oauth2client_client -from grpc.early_adopter import implementations +from grpc.beta import implementations + +from grpc_test.beta import test_utilities from grpc_interop import methods from grpc_interop import resources +from grpc_interop import test_pb2 _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -49,11 +52,11 @@ def _args(): parser.add_argument( '--test_case', help='the test case to execute', type=str) parser.add_argument( - '--use_tls', help='require a secure connection', dest='use_tls', - action='store_true') + '--use_tls', help='require a secure connection', default=False, + type=resources.parse_bool) parser.add_argument( '--use_test_ca', help='replace platform root CAs with ca.pem', - action='store_true') + default=False, type=resources.parse_bool) parser.add_argument( '--server_host_override', help='the server host to which to claim to connect', type=str) @@ -71,12 +74,17 @@ def _oauth_access_token(args): def _stub(args): if args.oauth_scope: if args.test_case == 'oauth2_auth_token': + # TODO(jtattermusch): This testcase sets the auth metadata key-value + # manually, which also means that the user would need to do the same + # thing every time he/she would like to use and out of band oauth token. + # The transformer function that produces the metadata key-value from + # the access token should be provided by gRPC auth library. access_token = _oauth_access_token(args) metadata_transformer = lambda x: [ - ('Authorization', 'Bearer %s' % access_token)] + ('authorization', 'Bearer %s' % access_token)] else: metadata_transformer = lambda x: [ - ('Authorization', 'Bearer %s' % _oauth_access_token(args))] + ('authorization', 'Bearer %s' % _oauth_access_token(args))] else: metadata_transformer = lambda x: [] if args.use_tls: @@ -85,15 +93,16 @@ def _stub(args): else: root_certificates = resources.prod_root_certificates() - stub = implementations.stub( - methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host, - args.server_port, metadata_transformer=metadata_transformer, - secure=True, root_certificates=root_certificates, - server_host_override=args.server_host_override) + channel = test_utilities.not_really_secure_channel( + args.server_host, args.server_port, + implementations.ssl_client_credentials(root_certificates, None, None), + args.server_host_override) + stub = test_pb2.beta_create_TestService_stub( + channel, metadata_transformer=metadata_transformer) else: - stub = implementations.stub( - methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host, - args.server_port, secure=False) + channel = implementations.insecure_channel( + args.server_host, args.server_port) + stub = test_pb2.beta_create_TestService_stub(channel) return stub diff --git a/src/csharp/Grpc.IntegrationTesting/proto/empty.proto b/src/python/grpcio_test/grpc_interop/empty.proto index 6d0eb937d6..6d0eb937d6 100644 --- a/src/csharp/Grpc.IntegrationTesting/proto/empty.proto +++ b/src/python/grpcio_test/grpc_interop/empty.proto diff --git a/src/python/grpcio_test/grpc_interop/empty_pb2.py b/src/python/grpcio_test/grpc_interop/empty_pb2.py deleted file mode 100644 index 8c1ce2f13e..0000000000 --- a/src/python/grpcio_test/grpc_interop/empty_pb2.py +++ /dev/null @@ -1,63 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: test/cpp/interop/empty.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='test/cpp/interop/empty.proto', - package='grpc.testing', - serialized_pb=_b('\n\x1ctest/cpp/interop/empty.proto\x12\x0cgrpc.testing\"\x07\n\x05\x45mpty') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - -_EMPTY = _descriptor.Descriptor( - name='Empty', - full_name='grpc.testing.Empty', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=46, - serialized_end=53, -) - -DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY - -Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), dict( - DESCRIPTOR = _EMPTY, - __module__ = 'test.cpp.interop.empty_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.Empty) - )) -_sym_db.RegisterMessage(Empty) - - -import abc -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -# @@protoc_insertion_point(module_scope) diff --git a/src/node/interop/messages.proto b/src/python/grpcio_test/grpc_interop/messages.proto index 7df85e3c13..193b6c4171 100644 --- a/src/node/interop/messages.proto +++ b/src/python/grpcio_test/grpc_interop/messages.proto @@ -46,6 +46,14 @@ enum PayloadType { RANDOM = 2; } +// Compression algorithms +enum CompressionType { + // No compression + NONE = 0; + GZIP = 1; + DEFLATE = 2; +} + // A block of data, to simply increase gRPC message size. message Payload { // The type of data in body. @@ -54,6 +62,13 @@ message Payload { bytes body = 2; } +// A protobuf representation for grpc status. This is used by test +// clients to specify a status that the server should attempt to return. +message EchoStatus { + int32 code = 1; + string message = 2; +} + // Unary request. message SimpleRequest { // Desired payload type in the response from the server. @@ -72,6 +87,12 @@ message SimpleRequest { // Whether SimpleResponse should include OAuth scope. bool fill_oauth_scope = 5; + + // Compression algorithm to be used by the server for the response (stream) + CompressionType response_compression = 6; + + // Whether server should return a given status + EchoStatus response_status = 7; } // Unary response, as configured by the request. @@ -123,6 +144,12 @@ message StreamingOutputCallRequest { // Optional input payload sent along with the request. Payload payload = 3; + + // Compression algorithm to be used by the server for the response (stream) + CompressionType response_compression = 6; + + // Whether server should return a given status + EchoStatus response_status = 7; } // Server-streaming response, as configured by the request and parameters. @@ -130,3 +157,11 @@ message StreamingOutputCallResponse { // Payload to increase response size. Payload payload = 1; } + +// For reconnect interop test only. +// Server tells client whether its reconnects are following the spec and the +// reconnect backoffs it saw. +message ReconnectInfo { + bool passed = 1; + repeated int32 backoff_ms = 2; +} diff --git a/src/python/grpcio_test/grpc_interop/messages_pb2.py b/src/python/grpcio_test/grpc_interop/messages_pb2.py deleted file mode 100644 index 0bf3d86a31..0000000000 --- a/src/python/grpcio_test/grpc_interop/messages_pb2.py +++ /dev/null @@ -1,447 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: test/cpp/interop/messages.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='test/cpp/interop/messages.proto', - package='grpc.testing', - serialized_pb=_b('\n\x1ftest/cpp/interop/messages.proto\x12\x0cgrpc.testing\"@\n\x07Payload\x12\'\n\x04type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x0c\n\x04\x62ody\x18\x02 \x01(\x0c\"\xb1\x01\n\rSimpleRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x15\n\rresponse_size\x18\x02 \x01(\x05\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x15\n\rfill_username\x18\x04 \x01(\x08\x12\x18\n\x10\x66ill_oauth_scope\x18\x05 \x01(\x08\"_\n\x0eSimpleResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x13\n\x0boauth_scope\x18\x03 \x01(\t\"C\n\x19StreamingInputCallRequest\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\"=\n\x1aStreamingInputCallResponse\x12\x1f\n\x17\x61ggregated_payload_size\x18\x01 \x01(\x05\"7\n\x12ResponseParameters\x12\x0c\n\x04size\x18\x01 \x01(\x05\x12\x13\n\x0binterval_us\x18\x02 \x01(\x05\"\xb5\x01\n\x1aStreamingOutputCallRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12=\n\x13response_parameters\x18\x02 \x03(\x0b\x32 .grpc.testing.ResponseParameters\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\"E\n\x1bStreamingOutputCallResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload*?\n\x0bPayloadType\x12\x10\n\x0c\x43OMPRESSABLE\x10\x00\x12\x12\n\x0eUNCOMPRESSABLE\x10\x01\x12\n\n\x06RANDOM\x10\x02') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -_PAYLOADTYPE = _descriptor.EnumDescriptor( - name='PayloadType', - full_name='grpc.testing.PayloadType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='COMPRESSABLE', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNCOMPRESSABLE', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RANDOM', index=2, number=2, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=836, - serialized_end=899, -) -_sym_db.RegisterEnumDescriptor(_PAYLOADTYPE) - -PayloadType = enum_type_wrapper.EnumTypeWrapper(_PAYLOADTYPE) -COMPRESSABLE = 0 -UNCOMPRESSABLE = 1 -RANDOM = 2 - - - -_PAYLOAD = _descriptor.Descriptor( - name='Payload', - full_name='grpc.testing.Payload', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='type', full_name='grpc.testing.Payload.type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='body', full_name='grpc.testing.Payload.body', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=49, - serialized_end=113, -) - - -_SIMPLEREQUEST = _descriptor.Descriptor( - name='SimpleRequest', - full_name='grpc.testing.SimpleRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='response_type', full_name='grpc.testing.SimpleRequest.response_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='response_size', full_name='grpc.testing.SimpleRequest.response_size', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.SimpleRequest.payload', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='fill_username', full_name='grpc.testing.SimpleRequest.fill_username', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='fill_oauth_scope', full_name='grpc.testing.SimpleRequest.fill_oauth_scope', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=116, - serialized_end=293, -) - - -_SIMPLERESPONSE = _descriptor.Descriptor( - name='SimpleResponse', - full_name='grpc.testing.SimpleResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.SimpleResponse.payload', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='username', full_name='grpc.testing.SimpleResponse.username', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='oauth_scope', full_name='grpc.testing.SimpleResponse.oauth_scope', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=295, - serialized_end=390, -) - - -_STREAMINGINPUTCALLREQUEST = _descriptor.Descriptor( - name='StreamingInputCallRequest', - full_name='grpc.testing.StreamingInputCallRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.StreamingInputCallRequest.payload', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=392, - serialized_end=459, -) - - -_STREAMINGINPUTCALLRESPONSE = _descriptor.Descriptor( - name='StreamingInputCallResponse', - full_name='grpc.testing.StreamingInputCallResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='aggregated_payload_size', full_name='grpc.testing.StreamingInputCallResponse.aggregated_payload_size', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=461, - serialized_end=522, -) - - -_RESPONSEPARAMETERS = _descriptor.Descriptor( - name='ResponseParameters', - full_name='grpc.testing.ResponseParameters', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='size', full_name='grpc.testing.ResponseParameters.size', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='interval_us', full_name='grpc.testing.ResponseParameters.interval_us', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=524, - serialized_end=579, -) - - -_STREAMINGOUTPUTCALLREQUEST = _descriptor.Descriptor( - name='StreamingOutputCallRequest', - full_name='grpc.testing.StreamingOutputCallRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='response_type', full_name='grpc.testing.StreamingOutputCallRequest.response_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='response_parameters', full_name='grpc.testing.StreamingOutputCallRequest.response_parameters', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.StreamingOutputCallRequest.payload', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=582, - serialized_end=763, -) - - -_STREAMINGOUTPUTCALLRESPONSE = _descriptor.Descriptor( - name='StreamingOutputCallResponse', - full_name='grpc.testing.StreamingOutputCallResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.StreamingOutputCallResponse.payload', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=765, - serialized_end=834, -) - -_PAYLOAD.fields_by_name['type'].enum_type = _PAYLOADTYPE -_SIMPLEREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE -_SIMPLEREQUEST.fields_by_name['payload'].message_type = _PAYLOAD -_SIMPLERESPONSE.fields_by_name['payload'].message_type = _PAYLOAD -_STREAMINGINPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD -_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE -_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_parameters'].message_type = _RESPONSEPARAMETERS -_STREAMINGOUTPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD -_STREAMINGOUTPUTCALLRESPONSE.fields_by_name['payload'].message_type = _PAYLOAD -DESCRIPTOR.message_types_by_name['Payload'] = _PAYLOAD -DESCRIPTOR.message_types_by_name['SimpleRequest'] = _SIMPLEREQUEST -DESCRIPTOR.message_types_by_name['SimpleResponse'] = _SIMPLERESPONSE -DESCRIPTOR.message_types_by_name['StreamingInputCallRequest'] = _STREAMINGINPUTCALLREQUEST -DESCRIPTOR.message_types_by_name['StreamingInputCallResponse'] = _STREAMINGINPUTCALLRESPONSE -DESCRIPTOR.message_types_by_name['ResponseParameters'] = _RESPONSEPARAMETERS -DESCRIPTOR.message_types_by_name['StreamingOutputCallRequest'] = _STREAMINGOUTPUTCALLREQUEST -DESCRIPTOR.message_types_by_name['StreamingOutputCallResponse'] = _STREAMINGOUTPUTCALLRESPONSE -DESCRIPTOR.enum_types_by_name['PayloadType'] = _PAYLOADTYPE - -Payload = _reflection.GeneratedProtocolMessageType('Payload', (_message.Message,), dict( - DESCRIPTOR = _PAYLOAD, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.Payload) - )) -_sym_db.RegisterMessage(Payload) - -SimpleRequest = _reflection.GeneratedProtocolMessageType('SimpleRequest', (_message.Message,), dict( - DESCRIPTOR = _SIMPLEREQUEST, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.SimpleRequest) - )) -_sym_db.RegisterMessage(SimpleRequest) - -SimpleResponse = _reflection.GeneratedProtocolMessageType('SimpleResponse', (_message.Message,), dict( - DESCRIPTOR = _SIMPLERESPONSE, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.SimpleResponse) - )) -_sym_db.RegisterMessage(SimpleResponse) - -StreamingInputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingInputCallRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMINGINPUTCALLREQUEST, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallRequest) - )) -_sym_db.RegisterMessage(StreamingInputCallRequest) - -StreamingInputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingInputCallResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMINGINPUTCALLRESPONSE, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallResponse) - )) -_sym_db.RegisterMessage(StreamingInputCallResponse) - -ResponseParameters = _reflection.GeneratedProtocolMessageType('ResponseParameters', (_message.Message,), dict( - DESCRIPTOR = _RESPONSEPARAMETERS, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.ResponseParameters) - )) -_sym_db.RegisterMessage(ResponseParameters) - -StreamingOutputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingOutputCallRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMINGOUTPUTCALLREQUEST, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallRequest) - )) -_sym_db.RegisterMessage(StreamingOutputCallRequest) - -StreamingOutputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingOutputCallResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMINGOUTPUTCALLRESPONSE, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallResponse) - )) -_sym_db.RegisterMessage(StreamingOutputCallResponse) - - -import abc -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_interop/methods.py b/src/python/grpcio_test/grpc_interop/methods.py index 52b800af7a..3ef8545355 100644 --- a/src/python/grpcio_test/grpc_interop/methods.py +++ b/src/python/grpcio_test/grpc_interop/methods.py @@ -37,123 +37,53 @@ import time from oauth2client import client as oauth2client_client -from grpc.framework.alpha import utilities -from grpc.framework.alpha import exceptions +from grpc.framework.common import cardinality +from grpc.framework.interfaces.face import face from grpc_interop import empty_pb2 from grpc_interop import messages_pb2 +from grpc_interop import test_pb2 _TIMEOUT = 7 -def _empty_call(request, unused_context): - return empty_pb2.Empty() +class TestService(test_pb2.BetaTestServiceServicer): -_CLIENT_EMPTY_CALL = utilities.unary_unary_invocation_description( - empty_pb2.Empty.SerializeToString, empty_pb2.Empty.FromString) -_SERVER_EMPTY_CALL = utilities.unary_unary_service_description( - _empty_call, empty_pb2.Empty.FromString, - empty_pb2.Empty.SerializeToString) + def EmptyCall(self, request, context): + return empty_pb2.Empty() - -def _unary_call(request, unused_context): - return messages_pb2.SimpleResponse( - payload=messages_pb2.Payload( - type=messages_pb2.COMPRESSABLE, - body=b'\x00' * request.response_size)) - -_CLIENT_UNARY_CALL = utilities.unary_unary_invocation_description( - messages_pb2.SimpleRequest.SerializeToString, - messages_pb2.SimpleResponse.FromString) -_SERVER_UNARY_CALL = utilities.unary_unary_service_description( - _unary_call, messages_pb2.SimpleRequest.FromString, - messages_pb2.SimpleResponse.SerializeToString) - - -def _streaming_output_call(request, unused_context): - for response_parameters in request.response_parameters: - yield messages_pb2.StreamingOutputCallResponse( - payload=messages_pb2.Payload( - type=request.response_type, - body=b'\x00' * response_parameters.size)) - -_CLIENT_STREAMING_OUTPUT_CALL = utilities.unary_stream_invocation_description( - messages_pb2.StreamingOutputCallRequest.SerializeToString, - messages_pb2.StreamingOutputCallResponse.FromString) -_SERVER_STREAMING_OUTPUT_CALL = utilities.unary_stream_service_description( - _streaming_output_call, - messages_pb2.StreamingOutputCallRequest.FromString, - messages_pb2.StreamingOutputCallResponse.SerializeToString) - - -def _streaming_input_call(request_iterator, unused_context): - aggregate_size = 0 - for request in request_iterator: - if request.payload and request.payload.body: - aggregate_size += len(request.payload.body) - return messages_pb2.StreamingInputCallResponse( - aggregated_payload_size=aggregate_size) - -_CLIENT_STREAMING_INPUT_CALL = utilities.stream_unary_invocation_description( - messages_pb2.StreamingInputCallRequest.SerializeToString, - messages_pb2.StreamingInputCallResponse.FromString) -_SERVER_STREAMING_INPUT_CALL = utilities.stream_unary_service_description( - _streaming_input_call, - messages_pb2.StreamingInputCallRequest.FromString, - messages_pb2.StreamingInputCallResponse.SerializeToString) - - -def _full_duplex_call(request_iterator, unused_context): - for request in request_iterator: - yield messages_pb2.StreamingOutputCallResponse( + def UnaryCall(self, request, context): + return messages_pb2.SimpleResponse( payload=messages_pb2.Payload( - type=request.payload.type, - body=b'\x00' * request.response_parameters[0].size)) - -_CLIENT_FULL_DUPLEX_CALL = utilities.stream_stream_invocation_description( - messages_pb2.StreamingOutputCallRequest.SerializeToString, - messages_pb2.StreamingOutputCallResponse.FromString) -_SERVER_FULL_DUPLEX_CALL = utilities.stream_stream_service_description( - _full_duplex_call, - messages_pb2.StreamingOutputCallRequest.FromString, - messages_pb2.StreamingOutputCallResponse.SerializeToString) - -# NOTE(nathaniel): Apparently this is the same as the full-duplex call? -_CLIENT_HALF_DUPLEX_CALL = utilities.stream_stream_invocation_description( - messages_pb2.StreamingOutputCallRequest.SerializeToString, - messages_pb2.StreamingOutputCallResponse.FromString) -_SERVER_HALF_DUPLEX_CALL = utilities.stream_stream_service_description( - _full_duplex_call, - messages_pb2.StreamingOutputCallRequest.FromString, - messages_pb2.StreamingOutputCallResponse.SerializeToString) - - -SERVICE_NAME = 'grpc.testing.TestService' - -_EMPTY_CALL_METHOD_NAME = 'EmptyCall' -_UNARY_CALL_METHOD_NAME = 'UnaryCall' -_STREAMING_OUTPUT_CALL_METHOD_NAME = 'StreamingOutputCall' -_STREAMING_INPUT_CALL_METHOD_NAME = 'StreamingInputCall' -_FULL_DUPLEX_CALL_METHOD_NAME = 'FullDuplexCall' -_HALF_DUPLEX_CALL_METHOD_NAME = 'HalfDuplexCall' - -CLIENT_METHODS = { - _EMPTY_CALL_METHOD_NAME: _CLIENT_EMPTY_CALL, - _UNARY_CALL_METHOD_NAME: _CLIENT_UNARY_CALL, - _STREAMING_OUTPUT_CALL_METHOD_NAME: _CLIENT_STREAMING_OUTPUT_CALL, - _STREAMING_INPUT_CALL_METHOD_NAME: _CLIENT_STREAMING_INPUT_CALL, - _FULL_DUPLEX_CALL_METHOD_NAME: _CLIENT_FULL_DUPLEX_CALL, - _HALF_DUPLEX_CALL_METHOD_NAME: _CLIENT_HALF_DUPLEX_CALL, -} - -SERVER_METHODS = { - _EMPTY_CALL_METHOD_NAME: _SERVER_EMPTY_CALL, - _UNARY_CALL_METHOD_NAME: _SERVER_UNARY_CALL, - _STREAMING_OUTPUT_CALL_METHOD_NAME: _SERVER_STREAMING_OUTPUT_CALL, - _STREAMING_INPUT_CALL_METHOD_NAME: _SERVER_STREAMING_INPUT_CALL, - _FULL_DUPLEX_CALL_METHOD_NAME: _SERVER_FULL_DUPLEX_CALL, - _HALF_DUPLEX_CALL_METHOD_NAME: _SERVER_HALF_DUPLEX_CALL, -} + type=messages_pb2.COMPRESSABLE, + body=b'\x00' * request.response_size)) + + def StreamingOutputCall(self, request, context): + for response_parameters in request.response_parameters: + yield messages_pb2.StreamingOutputCallResponse( + payload=messages_pb2.Payload( + type=request.response_type, + body=b'\x00' * response_parameters.size)) + + def StreamingInputCall(self, request_iterator, context): + aggregate_size = 0 + for request in request_iterator: + if request.payload and request.payload.body: + aggregate_size += len(request.payload.body) + return messages_pb2.StreamingInputCallResponse( + aggregated_payload_size=aggregate_size) + + def FullDuplexCall(self, request_iterator, context): + for request in request_iterator: + yield messages_pb2.StreamingOutputCallResponse( + payload=messages_pb2.Payload( + type=request.payload.type, + body=b'\x00' * request.response_parameters[0].size)) + + # NOTE(nathaniel): Apparently this is the same as the full-duplex call? + # NOTE(atash): It isn't even called in the interop spec (Oct 22 2015)... + def HalfDuplexCall(self, request_iterator, context): + return self.FullDuplexCall(request_iterator, context) def _large_unary_common_behavior(stub, fill_username, fill_oauth_scope): @@ -162,7 +92,7 @@ def _large_unary_common_behavior(stub, fill_username, fill_oauth_scope): response_type=messages_pb2.COMPRESSABLE, response_size=314159, payload=messages_pb2.Payload(body=b'\x00' * 271828), fill_username=fill_username, fill_oauth_scope=fill_oauth_scope) - response_future = stub.UnaryCall.async(request, _TIMEOUT) + response_future = stub.UnaryCall.future(request, _TIMEOUT) response = response_future.result() if response.payload.type is not messages_pb2.COMPRESSABLE: raise ValueError( @@ -227,7 +157,7 @@ def _cancel_after_begin(stub): payloads = [messages_pb2.Payload(body=b'\x00' * size) for size in sizes] requests = [messages_pb2.StreamingInputCallRequest(payload=payload) for payload in payloads] - responses = stub.StreamingInputCall.async(requests, _TIMEOUT) + responses = stub.StreamingInputCall.future(requests, _TIMEOUT) responses.cancel() if not responses.cancelled(): raise ValueError('expected call to be cancelled') @@ -332,12 +262,23 @@ def _timeout_on_sleeping_server(stub): time.sleep(0.1) try: next(response_iterator) - except exceptions.ExpirationError: + except face.ExpirationError: pass else: raise ValueError('expected call to exceed deadline') +def _empty_stream(stub): + with stub, _Pipe() as pipe: + response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT) + pipe.close() + try: + next(response_iterator) + raise ValueError('expected exactly 0 responses') + except StopIteration: + pass + + def _compute_engine_creds(stub, args): response = _large_unary_common_behavior(stub, True, True) if args.default_service_account != response.username: @@ -368,6 +309,7 @@ class TestCase(enum.Enum): PING_PONG = 'ping_pong' CANCEL_AFTER_BEGIN = 'cancel_after_begin' CANCEL_AFTER_FIRST_RESPONSE = 'cancel_after_first_response' + EMPTY_STREAM = 'empty_stream' COMPUTE_ENGINE_CREDS = 'compute_engine_creds' OAUTH2_AUTH_TOKEN = 'oauth2_auth_token' TIMEOUT_ON_SLEEPING_SERVER = 'timeout_on_sleeping_server' @@ -389,6 +331,8 @@ class TestCase(enum.Enum): _cancel_after_first_response(stub) elif self is TestCase.TIMEOUT_ON_SLEEPING_SERVER: _timeout_on_sleeping_server(stub) + elif self is TestCase.EMPTY_STREAM: + _empty_stream(stub) elif self is TestCase.COMPUTE_ENGINE_CREDS: _compute_engine_creds(stub, args) elif self is TestCase.OAUTH2_AUTH_TOKEN: diff --git a/src/python/grpcio_test/grpc_interop/resources.py b/src/python/grpcio_test/grpc_interop/resources.py index 2c3045313d..1122499418 100644 --- a/src/python/grpcio_test/grpc_interop/resources.py +++ b/src/python/grpcio_test/grpc_interop/resources.py @@ -29,6 +29,7 @@ """Constants and functions for data used in interoperability testing.""" +import argparse import os import pkg_resources @@ -54,3 +55,11 @@ def private_key(): def certificate_chain(): return pkg_resources.resource_string( __name__, _CERTIFICATE_CHAIN_RESOURCE_PATH) + + +def parse_bool(value): + if value == 'true': + return True + if value == 'false': + return False + raise argparse.ArgumentTypeError('Only true/false allowed') diff --git a/src/python/grpcio_test/grpc_interop/server.py b/src/python/grpcio_test/grpc_interop/server.py index 60f630a6be..b541087663 100644 --- a/src/python/grpcio_test/grpc_interop/server.py +++ b/src/python/grpcio_test/grpc_interop/server.py @@ -33,10 +33,11 @@ import argparse import logging import time -from grpc.early_adopter import implementations +from grpc.beta import implementations from grpc_interop import methods from grpc_interop import resources +from grpc_interop import test_pb2 _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -46,19 +47,19 @@ def serve(): parser.add_argument( '--port', help='the port on which to serve', type=int) parser.add_argument( - '--use_tls', help='require a secure connection', dest='use_tls', - action='store_true') + '--use_tls', help='require a secure connection', + default=False, type=resources.parse_bool) args = parser.parse_args() + server = test_pb2.beta_create_TestService_server(methods.TestService()) if args.use_tls: private_key = resources.private_key() certificate_chain = resources.certificate_chain() - server = implementations.server( - methods.SERVICE_NAME, methods.SERVER_METHODS, args.port, - private_key=private_key, certificate_chain=certificate_chain) + credentials = implementations.ssl_server_credentials( + [(private_key, certificate_chain)]) + server.add_secure_port('[::]:{}'.format(args.port), credentials) else: - server = implementations.server( - methods.SERVICE_NAME, methods.SERVER_METHODS, args.port) + server.add_insecure_port('[::]:{}'.format(args.port)) server.start() logging.info('Server serving.') diff --git a/src/node/interop/test.proto b/src/python/grpcio_test/grpc_interop/test.proto index d2c3f9befe..b499813e56 100644 --- a/src/node/interop/test.proto +++ b/src/python/grpcio_test/grpc_interop/test.proto @@ -33,8 +33,8 @@ syntax = "proto3"; -import "empty.proto"; -import "messages.proto"; +import "grpc_interop/empty.proto"; +import "grpc_interop/messages.proto"; package grpc.testing; @@ -45,7 +45,6 @@ service TestService { rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); // One request followed by one response. - // TODO(Issue 527): Describe required server behavior. rpc UnaryCall(SimpleRequest) returns (SimpleResponse); // One request followed by a sequence of responses (streamed download). @@ -71,3 +70,17 @@ service TestService { rpc HalfDuplexCall(stream StreamingOutputCallRequest) returns (stream StreamingOutputCallResponse); } + + +// A simple service NOT implemented at servers so clients can test for +// that case. +service UnimplementedService { + // A call that no server should implement + rpc UnimplementedCall(grpc.testing.Empty) returns(grpc.testing.Empty); +} + +// A service used to control reconnect server. +service ReconnectService { + rpc Start(grpc.testing.Empty) returns (grpc.testing.Empty); + rpc Stop(grpc.testing.Empty) returns (grpc.testing.ReconnectInfo); +} diff --git a/src/python/grpcio_test/grpc_interop/test_pb2.py b/src/python/grpcio_test/grpc_interop/test_pb2.py deleted file mode 100644 index 71325d5a9f..0000000000 --- a/src/python/grpcio_test/grpc_interop/test_pb2.py +++ /dev/null @@ -1,178 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: test/cpp/interop/test.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from test.cpp.interop import empty_pb2 as test_dot_cpp_dot_interop_dot_empty__pb2 -from test.cpp.interop import messages_pb2 as test_dot_cpp_dot_interop_dot_messages__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='test/cpp/interop/test.proto', - package='grpc.testing', - serialized_pb=_b('\n\x1btest/cpp/interop/test.proto\x12\x0cgrpc.testing\x1a\x1ctest/cpp/interop/empty.proto\x1a\x1ftest/cpp/interop/messages.proto2\xbb\x04\n\x0bTestService\x12\x35\n\tEmptyCall\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x12\x46\n\tUnaryCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse\x12l\n\x13StreamingOutputCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse0\x01\x12i\n\x12StreamingInputCall\x12\'.grpc.testing.StreamingInputCallRequest\x1a(.grpc.testing.StreamingInputCallResponse(\x01\x12i\n\x0e\x46ullDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01\x12i\n\x0eHalfDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01') - , - dependencies=[test_dot_cpp_dot_interop_dot_empty__pb2.DESCRIPTOR,test_dot_cpp_dot_interop_dot_messages__pb2.DESCRIPTOR,]) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - - -import abc -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -class EarlyAdopterTestServiceServicer(object): - """<fill me in later!>""" - __metaclass__ = abc.ABCMeta - @abc.abstractmethod - def EmptyCall(self, request, context): - raise NotImplementedError() - @abc.abstractmethod - def UnaryCall(self, request, context): - raise NotImplementedError() - @abc.abstractmethod - def StreamingOutputCall(self, request, context): - raise NotImplementedError() - @abc.abstractmethod - def StreamingInputCall(self, request_iterator, context): - raise NotImplementedError() - @abc.abstractmethod - def FullDuplexCall(self, request_iterator, context): - raise NotImplementedError() - @abc.abstractmethod - def HalfDuplexCall(self, request_iterator, context): - raise NotImplementedError() -class EarlyAdopterTestServiceServer(object): - """<fill me in later!>""" - __metaclass__ = abc.ABCMeta - @abc.abstractmethod - def start(self): - raise NotImplementedError() - @abc.abstractmethod - def stop(self): - raise NotImplementedError() -class EarlyAdopterTestServiceStub(object): - """<fill me in later!>""" - __metaclass__ = abc.ABCMeta - @abc.abstractmethod - def EmptyCall(self, request): - raise NotImplementedError() - EmptyCall.async = None - @abc.abstractmethod - def UnaryCall(self, request): - raise NotImplementedError() - UnaryCall.async = None - @abc.abstractmethod - def StreamingOutputCall(self, request): - raise NotImplementedError() - StreamingOutputCall.async = None - @abc.abstractmethod - def StreamingInputCall(self, request_iterator): - raise NotImplementedError() - StreamingInputCall.async = None - @abc.abstractmethod - def FullDuplexCall(self, request_iterator): - raise NotImplementedError() - FullDuplexCall.async = None - @abc.abstractmethod - def HalfDuplexCall(self, request_iterator): - raise NotImplementedError() - HalfDuplexCall.async = None -def early_adopter_create_TestService_server(servicer, port, private_key=None, certificate_chain=None): - import test.cpp.interop.empty_pb2 - import test.cpp.interop.empty_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - method_service_descriptions = { - "EmptyCall": utilities.unary_unary_service_description( - servicer.EmptyCall, - test.cpp.interop.empty_pb2.Empty.FromString, - test.cpp.interop.empty_pb2.Empty.SerializeToString, - ), - "FullDuplexCall": utilities.stream_stream_service_description( - servicer.FullDuplexCall, - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, - ), - "HalfDuplexCall": utilities.stream_stream_service_description( - servicer.HalfDuplexCall, - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, - ), - "StreamingInputCall": utilities.stream_unary_service_description( - servicer.StreamingInputCall, - test.cpp.interop.messages_pb2.StreamingInputCallRequest.FromString, - test.cpp.interop.messages_pb2.StreamingInputCallResponse.SerializeToString, - ), - "StreamingOutputCall": utilities.unary_stream_service_description( - servicer.StreamingOutputCall, - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, - ), - "UnaryCall": utilities.unary_unary_service_description( - servicer.UnaryCall, - test.cpp.interop.messages_pb2.SimpleRequest.FromString, - test.cpp.interop.messages_pb2.SimpleResponse.SerializeToString, - ), - } - return implementations.server("grpc.testing.TestService", method_service_descriptions, port, private_key=private_key, certificate_chain=certificate_chain) -def early_adopter_create_TestService_stub(host, port, metadata_transformer=None, secure=False, root_certificates=None, private_key=None, certificate_chain=None, server_host_override=None): - import test.cpp.interop.empty_pb2 - import test.cpp.interop.empty_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - method_invocation_descriptions = { - "EmptyCall": utilities.unary_unary_invocation_description( - test.cpp.interop.empty_pb2.Empty.SerializeToString, - test.cpp.interop.empty_pb2.Empty.FromString, - ), - "FullDuplexCall": utilities.stream_stream_invocation_description( - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, - ), - "HalfDuplexCall": utilities.stream_stream_invocation_description( - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, - ), - "StreamingInputCall": utilities.stream_unary_invocation_description( - test.cpp.interop.messages_pb2.StreamingInputCallRequest.SerializeToString, - test.cpp.interop.messages_pb2.StreamingInputCallResponse.FromString, - ), - "StreamingOutputCall": utilities.unary_stream_invocation_description( - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, - ), - "UnaryCall": utilities.unary_unary_invocation_description( - test.cpp.interop.messages_pb2.SimpleRequest.SerializeToString, - test.cpp.interop.messages_pb2.SimpleResponse.FromString, - ), - } - return implementations.stub("grpc.testing.TestService", method_invocation_descriptions, host, port, metadata_transformer=metadata_transformer, secure=secure, root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain, server_host_override=server_host_override) -# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_protoc_plugin/alpha_python_plugin_test.py b/src/python/grpcio_test/grpc_protoc_plugin/alpha_python_plugin_test.py deleted file mode 100644 index b200d129a9..0000000000 --- a/src/python/grpcio_test/grpc_protoc_plugin/alpha_python_plugin_test.py +++ /dev/null @@ -1,541 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import argparse -import contextlib -import distutils.spawn -import errno -import itertools -import os -import pkg_resources -import shutil -import subprocess -import sys -import tempfile -import threading -import time -import unittest - -from grpc.framework.alpha import exceptions -from grpc.framework.foundation import future - -# Identifiers of entities we expect to find in the generated module. -SERVICER_IDENTIFIER = 'EarlyAdopterTestServiceServicer' -SERVER_IDENTIFIER = 'EarlyAdopterTestServiceServer' -STUB_IDENTIFIER = 'EarlyAdopterTestServiceStub' -SERVER_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_server' -STUB_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_stub' - -# The timeout used in tests of RPCs that are supposed to expire. -SHORT_TIMEOUT = 2 -# The timeout used in tests of RPCs that are not supposed to expire. The -# absurdly large value doesn't matter since no passing execution of this test -# module will ever wait the duration. -LONG_TIMEOUT = 600 -NO_DELAY = 0 - - -class _ServicerMethods(object): - - def __init__(self, test_pb2, delay): - self._condition = threading.Condition() - self._delay = delay - self._paused = False - self._fail = False - self._test_pb2 = test_pb2 - - @contextlib.contextmanager - def pause(self): # pylint: disable=invalid-name - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - @contextlib.contextmanager - def fail(self): # pylint: disable=invalid-name - with self._condition: - self._fail = True - yield - with self._condition: - self._fail = False - - def _control(self): # pylint: disable=invalid-name - with self._condition: - if self._fail: - raise ValueError() - while self._paused: - self._condition.wait() - time.sleep(self._delay) - - def UnaryCall(self, request, unused_rpc_context): - response = self._test_pb2.SimpleResponse() - response.payload.payload_type = self._test_pb2.COMPRESSABLE - response.payload.payload_compressable = 'a' * request.response_size - self._control() - return response - - def StreamingOutputCall(self, request, unused_rpc_context): - for parameter in request.response_parameters: - response = self._test_pb2.StreamingOutputCallResponse() - response.payload.payload_type = self._test_pb2.COMPRESSABLE - response.payload.payload_compressable = 'a' * parameter.size - self._control() - yield response - - def StreamingInputCall(self, request_iter, unused_rpc_context): - response = self._test_pb2.StreamingInputCallResponse() - aggregated_payload_size = 0 - for request in request_iter: - aggregated_payload_size += len(request.payload.payload_compressable) - response.aggregated_payload_size = aggregated_payload_size - self._control() - return response - - def FullDuplexCall(self, request_iter, unused_rpc_context): - for request in request_iter: - for parameter in request.response_parameters: - response = self._test_pb2.StreamingOutputCallResponse() - response.payload.payload_type = self._test_pb2.COMPRESSABLE - response.payload.payload_compressable = 'a' * parameter.size - self._control() - yield response - - def HalfDuplexCall(self, request_iter, unused_rpc_context): - responses = [] - for request in request_iter: - for parameter in request.response_parameters: - response = self._test_pb2.StreamingOutputCallResponse() - response.payload.payload_type = self._test_pb2.COMPRESSABLE - response.payload.payload_compressable = 'a' * parameter.size - self._control() - responses.append(response) - for response in responses: - yield response - - -@contextlib.contextmanager -def _CreateService(test_pb2, delay): - """Provides a servicer backend and a stub. - - The servicer is just the implementation - of the actual servicer passed to the face player of the python RPC - implementation; the two are detached. - - Non-zero delay puts a delay on each call to the servicer, representative of - communication latency. Timeout is the default timeout for the stub while - waiting for the service. - - Args: - test_pb2: The test_pb2 module generated by this test. - delay: Delay in seconds per response from the servicer. - - Yields: - A (servicer_methods, servicer, stub) three-tuple where servicer_methods is - the back-end of the service bound to the stub and the server and stub - are both activated and ready for use. - """ - servicer_methods = _ServicerMethods(test_pb2, delay) - - class Servicer(getattr(test_pb2, SERVICER_IDENTIFIER)): - - def UnaryCall(self, request, context): - return servicer_methods.UnaryCall(request, context) - - def StreamingOutputCall(self, request, context): - return servicer_methods.StreamingOutputCall(request, context) - - def StreamingInputCall(self, request_iter, context): - return servicer_methods.StreamingInputCall(request_iter, context) - - def FullDuplexCall(self, request_iter, context): - return servicer_methods.FullDuplexCall(request_iter, context) - - def HalfDuplexCall(self, request_iter, context): - return servicer_methods.HalfDuplexCall(request_iter, context) - - servicer = Servicer() - server = getattr( - test_pb2, SERVER_FACTORY_IDENTIFIER)(servicer, 0) - with server: - port = server.port() - stub = getattr(test_pb2, STUB_FACTORY_IDENTIFIER)('localhost', port) - with stub: - yield servicer_methods, stub, server - - -def _streaming_input_request_iterator(test_pb2): - for _ in range(3): - request = test_pb2.StreamingInputCallRequest() - request.payload.payload_type = test_pb2.COMPRESSABLE - request.payload.payload_compressable = 'a' - yield request - - -def _streaming_output_request(test_pb2): - request = test_pb2.StreamingOutputCallRequest() - sizes = [1, 2, 3] - request.response_parameters.add(size=sizes[0], interval_us=0) - request.response_parameters.add(size=sizes[1], interval_us=0) - request.response_parameters.add(size=sizes[2], interval_us=0) - return request - - -def _full_duplex_request_iterator(test_pb2): - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=1, interval_us=0) - yield request - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=2, interval_us=0) - request.response_parameters.add(size=3, interval_us=0) - yield request - - -class PythonPluginTest(unittest.TestCase): - """Test case for the gRPC Python protoc-plugin. - - While reading these tests, remember that the futures API - (`stub.method.async()`) only gives futures for the *non-streaming* responses, - else it behaves like its blocking cousin. - """ - - def setUp(self): - # Assume that the appropriate protoc and grpc_python_plugins are on the - # path. - protoc_command = 'protoc' - protoc_plugin_filename = distutils.spawn.find_executable( - 'grpc_python_plugin') - test_proto_filename = pkg_resources.resource_filename( - 'grpc_protoc_plugin', 'test.proto') - if not os.path.isfile(protoc_command): - # Assume that if we haven't built protoc that it's on the system. - protoc_command = 'protoc' - - # Ensure that the output directory exists. - self.outdir = tempfile.mkdtemp() - - # Invoke protoc with the plugin. - cmd = [ - protoc_command, - '--plugin=protoc-gen-python-grpc=%s' % protoc_plugin_filename, - '-I .', - '--python_out=%s' % self.outdir, - '--python-grpc_out=%s' % self.outdir, - os.path.basename(test_proto_filename), - ] - subprocess.check_call(' '.join(cmd), shell=True, env=os.environ, - cwd=os.path.dirname(test_proto_filename)) - sys.path.append(self.outdir) - - def tearDown(self): - try: - shutil.rmtree(self.outdir) - except OSError as exc: - if exc.errno != errno.ENOENT: - raise - - # TODO(atash): Figure out which of these tests is hanging flakily with small - # probability. - - def testImportAttributes(self): - # check that we can access the generated module and its members. - import test_pb2 # pylint: disable=g-import-not-at-top - self.assertIsNotNone(getattr(test_pb2, SERVICER_IDENTIFIER, None)) - self.assertIsNotNone(getattr(test_pb2, SERVER_IDENTIFIER, None)) - self.assertIsNotNone(getattr(test_pb2, STUB_IDENTIFIER, None)) - self.assertIsNotNone(getattr(test_pb2, SERVER_FACTORY_IDENTIFIER, None)) - self.assertIsNotNone(getattr(test_pb2, STUB_FACTORY_IDENTIFIER, None)) - - def testUpDown(self): - import test_pb2 - with _CreateService( - test_pb2, NO_DELAY) as (servicer, stub, unused_server): - request = test_pb2.SimpleRequest(response_size=13) - - def testUnaryCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods. - request = test_pb2.SimpleRequest(response_size=13) - response = stub.UnaryCall(request, timeout) - expected_response = methods.UnaryCall(request, 'not a real RpcContext!') - self.assertEqual(expected_response, response) - - def testUnaryCallAsync(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = test_pb2.SimpleRequest(response_size=13) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - # Check that the call does not block waiting for the server to respond. - with methods.pause(): - response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) - response = response_future.result() - expected_response = methods.UnaryCall(request, 'not a real RpcContext!') - self.assertEqual(expected_response, response) - - def testUnaryCallAsyncExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - request = test_pb2.SimpleRequest(response_size=13) - with methods.pause(): - response_future = stub.UnaryCall.async(request, SHORT_TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testUnaryCallAsyncCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = test_pb2.SimpleRequest(response_size=13) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - response_future = stub.UnaryCall.async(request, 1) - response_future.cancel() - self.assertTrue(response_future.cancelled()) - - def testUnaryCallAsyncFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = test_pb2.SimpleRequest(response_size=13) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.fail(): - response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) - self.assertIsNotNone(response_future.exception()) - - def testStreamingOutputCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = _streaming_output_request(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - responses = stub.StreamingOutputCall(request, LONG_TIMEOUT) - expected_responses = methods.StreamingOutputCall( - request, 'not a real RpcContext!') - for expected_response, response in itertools.izip_longest( - expected_responses, responses): - self.assertEqual(expected_response, response) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testStreamingOutputCallExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = _streaming_output_request(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - list(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testStreamingOutputCallCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = _streaming_output_request(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - unused_methods, stub, unused_server): - responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) - next(responses) - responses.cancel() - with self.assertRaises(future.CancelledError): - next(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this times out ' - 'instead of raising the proper error.') - def testStreamingOutputCallFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = _streaming_output_request(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.fail(): - responses = stub.StreamingOutputCall(request, 1) - self.assertIsNotNone(responses) - with self.assertRaises(exceptions.ServicerError): - next(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testStreamingInputCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - response = stub.StreamingInputCall( - _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) - expected_response = methods.StreamingInputCall( - _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') - self.assertEqual(expected_response, response) - - def testStreamingInputCallAsync(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - response_future = stub.StreamingInputCall.async( - _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) - response = response_future.result() - expected_response = methods.StreamingInputCall( - _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') - self.assertEqual(expected_response, response) - - def testStreamingInputCallAsyncExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - response_future = stub.StreamingInputCall.async( - _streaming_input_request_iterator(test_pb2), SHORT_TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - - def testStreamingInputCallAsyncCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods. - response_future = stub.StreamingInputCall.async( - _streaming_input_request_iterator(test_pb2), timeout) - response_future.cancel() - self.assertTrue(response_future.cancelled()) - with self.assertRaises(future.CancelledError): - response_future.result() - - def testStreamingInputCallAsyncFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.fail(): - response_future = stub.StreamingInputCall.async( - _streaming_input_request_iterator(test_pb2), SHORT_TIMEOUT) - self.assertIsNotNone(response_future.exception()) - - def testFullDuplexCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - responses = stub.FullDuplexCall( - _full_duplex_request_iterator(test_pb2), LONG_TIMEOUT) - expected_responses = methods.FullDuplexCall( - _full_duplex_request_iterator(test_pb2), 'not a real RpcContext!') - for expected_response, response in itertools.izip_longest( - expected_responses, responses): - self.assertEqual(expected_response, response) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testFullDuplexCallExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request_iterator = _full_duplex_request_iterator(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - responses = stub.FullDuplexCall(request_iterator, SHORT_TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - list(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testFullDuplexCallCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - request_iterator = _full_duplex_request_iterator(test_pb2) - responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT) - next(responses) - responses.cancel() - with self.assertRaises(future.CancelledError): - next(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this hangs forever ' - 'and fix.') - def testFullDuplexCallFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request_iterator = _full_duplex_request_iterator(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.fail(): - responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT) - self.assertIsNotNone(responses) - with self.assertRaises(exceptions.ServicerError): - next(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testHalfDuplexCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - def half_duplex_request_iterator(): - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=1, interval_us=0) - yield request - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=2, interval_us=0) - request.response_parameters.add(size=3, interval_us=0) - yield request - responses = stub.HalfDuplexCall( - half_duplex_request_iterator(), LONG_TIMEOUT) - expected_responses = methods.HalfDuplexCall( - half_duplex_request_iterator(), 'not a real RpcContext!') - for check in itertools.izip_longest(expected_responses, responses): - expected_response, response = check - self.assertEqual(expected_response, response) - - def testHalfDuplexCallWedged(self): - import test_pb2 # pylint: disable=g-import-not-at-top - condition = threading.Condition() - wait_cell = [False] - @contextlib.contextmanager - def wait(): # pylint: disable=invalid-name - # Where's Python 3's 'nonlocal' statement when you need it? - with condition: - wait_cell[0] = True - yield - with condition: - wait_cell[0] = False - condition.notify_all() - def half_duplex_request_iterator(): - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=1, interval_us=0) - yield request - with condition: - while wait_cell[0]: - condition.wait() - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - with wait(): - responses = stub.HalfDuplexCall( - half_duplex_request_iterator(), SHORT_TIMEOUT) - # half-duplex waits for the client to send all info - with self.assertRaises(exceptions.ExpirationError): - next(responses) - - -if __name__ == '__main__': - os.chdir(os.path.dirname(sys.argv[0])) - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py b/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py index 4c8c64b06d..259b978de2 100644 --- a/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py +++ b/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py @@ -42,7 +42,7 @@ import threading import time import unittest -from grpc.beta import beta +from grpc.beta import implementations from grpc.framework.foundation import future from grpc.framework.interfaces.face import face from grpc_test.framework.common import test_constants @@ -170,7 +170,7 @@ def _CreateService(test_pb2): server = getattr(test_pb2, SERVER_FACTORY_IDENTIFIER)(servicer) port = server.add_insecure_port('[::]:0') server.start() - channel = beta.create_insecure_channel('localhost', port) + channel = implementations.insecure_channel('localhost', port) stub = getattr(test_pb2, STUB_FACTORY_IDENTIFIER)(channel) yield servicer_methods, stub server.stop(0) diff --git a/src/python/grpcio_test/grpc_protoc_plugin/python_plugin_test.py b/src/python/grpcio_test/grpc_protoc_plugin/python_plugin_test.py deleted file mode 100644 index b200d129a9..0000000000 --- a/src/python/grpcio_test/grpc_protoc_plugin/python_plugin_test.py +++ /dev/null @@ -1,541 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import argparse -import contextlib -import distutils.spawn -import errno -import itertools -import os -import pkg_resources -import shutil -import subprocess -import sys -import tempfile -import threading -import time -import unittest - -from grpc.framework.alpha import exceptions -from grpc.framework.foundation import future - -# Identifiers of entities we expect to find in the generated module. -SERVICER_IDENTIFIER = 'EarlyAdopterTestServiceServicer' -SERVER_IDENTIFIER = 'EarlyAdopterTestServiceServer' -STUB_IDENTIFIER = 'EarlyAdopterTestServiceStub' -SERVER_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_server' -STUB_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_stub' - -# The timeout used in tests of RPCs that are supposed to expire. -SHORT_TIMEOUT = 2 -# The timeout used in tests of RPCs that are not supposed to expire. The -# absurdly large value doesn't matter since no passing execution of this test -# module will ever wait the duration. -LONG_TIMEOUT = 600 -NO_DELAY = 0 - - -class _ServicerMethods(object): - - def __init__(self, test_pb2, delay): - self._condition = threading.Condition() - self._delay = delay - self._paused = False - self._fail = False - self._test_pb2 = test_pb2 - - @contextlib.contextmanager - def pause(self): # pylint: disable=invalid-name - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - @contextlib.contextmanager - def fail(self): # pylint: disable=invalid-name - with self._condition: - self._fail = True - yield - with self._condition: - self._fail = False - - def _control(self): # pylint: disable=invalid-name - with self._condition: - if self._fail: - raise ValueError() - while self._paused: - self._condition.wait() - time.sleep(self._delay) - - def UnaryCall(self, request, unused_rpc_context): - response = self._test_pb2.SimpleResponse() - response.payload.payload_type = self._test_pb2.COMPRESSABLE - response.payload.payload_compressable = 'a' * request.response_size - self._control() - return response - - def StreamingOutputCall(self, request, unused_rpc_context): - for parameter in request.response_parameters: - response = self._test_pb2.StreamingOutputCallResponse() - response.payload.payload_type = self._test_pb2.COMPRESSABLE - response.payload.payload_compressable = 'a' * parameter.size - self._control() - yield response - - def StreamingInputCall(self, request_iter, unused_rpc_context): - response = self._test_pb2.StreamingInputCallResponse() - aggregated_payload_size = 0 - for request in request_iter: - aggregated_payload_size += len(request.payload.payload_compressable) - response.aggregated_payload_size = aggregated_payload_size - self._control() - return response - - def FullDuplexCall(self, request_iter, unused_rpc_context): - for request in request_iter: - for parameter in request.response_parameters: - response = self._test_pb2.StreamingOutputCallResponse() - response.payload.payload_type = self._test_pb2.COMPRESSABLE - response.payload.payload_compressable = 'a' * parameter.size - self._control() - yield response - - def HalfDuplexCall(self, request_iter, unused_rpc_context): - responses = [] - for request in request_iter: - for parameter in request.response_parameters: - response = self._test_pb2.StreamingOutputCallResponse() - response.payload.payload_type = self._test_pb2.COMPRESSABLE - response.payload.payload_compressable = 'a' * parameter.size - self._control() - responses.append(response) - for response in responses: - yield response - - -@contextlib.contextmanager -def _CreateService(test_pb2, delay): - """Provides a servicer backend and a stub. - - The servicer is just the implementation - of the actual servicer passed to the face player of the python RPC - implementation; the two are detached. - - Non-zero delay puts a delay on each call to the servicer, representative of - communication latency. Timeout is the default timeout for the stub while - waiting for the service. - - Args: - test_pb2: The test_pb2 module generated by this test. - delay: Delay in seconds per response from the servicer. - - Yields: - A (servicer_methods, servicer, stub) three-tuple where servicer_methods is - the back-end of the service bound to the stub and the server and stub - are both activated and ready for use. - """ - servicer_methods = _ServicerMethods(test_pb2, delay) - - class Servicer(getattr(test_pb2, SERVICER_IDENTIFIER)): - - def UnaryCall(self, request, context): - return servicer_methods.UnaryCall(request, context) - - def StreamingOutputCall(self, request, context): - return servicer_methods.StreamingOutputCall(request, context) - - def StreamingInputCall(self, request_iter, context): - return servicer_methods.StreamingInputCall(request_iter, context) - - def FullDuplexCall(self, request_iter, context): - return servicer_methods.FullDuplexCall(request_iter, context) - - def HalfDuplexCall(self, request_iter, context): - return servicer_methods.HalfDuplexCall(request_iter, context) - - servicer = Servicer() - server = getattr( - test_pb2, SERVER_FACTORY_IDENTIFIER)(servicer, 0) - with server: - port = server.port() - stub = getattr(test_pb2, STUB_FACTORY_IDENTIFIER)('localhost', port) - with stub: - yield servicer_methods, stub, server - - -def _streaming_input_request_iterator(test_pb2): - for _ in range(3): - request = test_pb2.StreamingInputCallRequest() - request.payload.payload_type = test_pb2.COMPRESSABLE - request.payload.payload_compressable = 'a' - yield request - - -def _streaming_output_request(test_pb2): - request = test_pb2.StreamingOutputCallRequest() - sizes = [1, 2, 3] - request.response_parameters.add(size=sizes[0], interval_us=0) - request.response_parameters.add(size=sizes[1], interval_us=0) - request.response_parameters.add(size=sizes[2], interval_us=0) - return request - - -def _full_duplex_request_iterator(test_pb2): - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=1, interval_us=0) - yield request - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=2, interval_us=0) - request.response_parameters.add(size=3, interval_us=0) - yield request - - -class PythonPluginTest(unittest.TestCase): - """Test case for the gRPC Python protoc-plugin. - - While reading these tests, remember that the futures API - (`stub.method.async()`) only gives futures for the *non-streaming* responses, - else it behaves like its blocking cousin. - """ - - def setUp(self): - # Assume that the appropriate protoc and grpc_python_plugins are on the - # path. - protoc_command = 'protoc' - protoc_plugin_filename = distutils.spawn.find_executable( - 'grpc_python_plugin') - test_proto_filename = pkg_resources.resource_filename( - 'grpc_protoc_plugin', 'test.proto') - if not os.path.isfile(protoc_command): - # Assume that if we haven't built protoc that it's on the system. - protoc_command = 'protoc' - - # Ensure that the output directory exists. - self.outdir = tempfile.mkdtemp() - - # Invoke protoc with the plugin. - cmd = [ - protoc_command, - '--plugin=protoc-gen-python-grpc=%s' % protoc_plugin_filename, - '-I .', - '--python_out=%s' % self.outdir, - '--python-grpc_out=%s' % self.outdir, - os.path.basename(test_proto_filename), - ] - subprocess.check_call(' '.join(cmd), shell=True, env=os.environ, - cwd=os.path.dirname(test_proto_filename)) - sys.path.append(self.outdir) - - def tearDown(self): - try: - shutil.rmtree(self.outdir) - except OSError as exc: - if exc.errno != errno.ENOENT: - raise - - # TODO(atash): Figure out which of these tests is hanging flakily with small - # probability. - - def testImportAttributes(self): - # check that we can access the generated module and its members. - import test_pb2 # pylint: disable=g-import-not-at-top - self.assertIsNotNone(getattr(test_pb2, SERVICER_IDENTIFIER, None)) - self.assertIsNotNone(getattr(test_pb2, SERVER_IDENTIFIER, None)) - self.assertIsNotNone(getattr(test_pb2, STUB_IDENTIFIER, None)) - self.assertIsNotNone(getattr(test_pb2, SERVER_FACTORY_IDENTIFIER, None)) - self.assertIsNotNone(getattr(test_pb2, STUB_FACTORY_IDENTIFIER, None)) - - def testUpDown(self): - import test_pb2 - with _CreateService( - test_pb2, NO_DELAY) as (servicer, stub, unused_server): - request = test_pb2.SimpleRequest(response_size=13) - - def testUnaryCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods. - request = test_pb2.SimpleRequest(response_size=13) - response = stub.UnaryCall(request, timeout) - expected_response = methods.UnaryCall(request, 'not a real RpcContext!') - self.assertEqual(expected_response, response) - - def testUnaryCallAsync(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = test_pb2.SimpleRequest(response_size=13) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - # Check that the call does not block waiting for the server to respond. - with methods.pause(): - response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) - response = response_future.result() - expected_response = methods.UnaryCall(request, 'not a real RpcContext!') - self.assertEqual(expected_response, response) - - def testUnaryCallAsyncExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - request = test_pb2.SimpleRequest(response_size=13) - with methods.pause(): - response_future = stub.UnaryCall.async(request, SHORT_TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testUnaryCallAsyncCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = test_pb2.SimpleRequest(response_size=13) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - response_future = stub.UnaryCall.async(request, 1) - response_future.cancel() - self.assertTrue(response_future.cancelled()) - - def testUnaryCallAsyncFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = test_pb2.SimpleRequest(response_size=13) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.fail(): - response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) - self.assertIsNotNone(response_future.exception()) - - def testStreamingOutputCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = _streaming_output_request(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - responses = stub.StreamingOutputCall(request, LONG_TIMEOUT) - expected_responses = methods.StreamingOutputCall( - request, 'not a real RpcContext!') - for expected_response, response in itertools.izip_longest( - expected_responses, responses): - self.assertEqual(expected_response, response) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testStreamingOutputCallExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = _streaming_output_request(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - list(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testStreamingOutputCallCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = _streaming_output_request(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - unused_methods, stub, unused_server): - responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) - next(responses) - responses.cancel() - with self.assertRaises(future.CancelledError): - next(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this times out ' - 'instead of raising the proper error.') - def testStreamingOutputCallFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request = _streaming_output_request(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.fail(): - responses = stub.StreamingOutputCall(request, 1) - self.assertIsNotNone(responses) - with self.assertRaises(exceptions.ServicerError): - next(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testStreamingInputCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - response = stub.StreamingInputCall( - _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) - expected_response = methods.StreamingInputCall( - _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') - self.assertEqual(expected_response, response) - - def testStreamingInputCallAsync(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - response_future = stub.StreamingInputCall.async( - _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) - response = response_future.result() - expected_response = methods.StreamingInputCall( - _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') - self.assertEqual(expected_response, response) - - def testStreamingInputCallAsyncExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - response_future = stub.StreamingInputCall.async( - _streaming_input_request_iterator(test_pb2), SHORT_TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - - def testStreamingInputCallAsyncCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods. - response_future = stub.StreamingInputCall.async( - _streaming_input_request_iterator(test_pb2), timeout) - response_future.cancel() - self.assertTrue(response_future.cancelled()) - with self.assertRaises(future.CancelledError): - response_future.result() - - def testStreamingInputCallAsyncFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.fail(): - response_future = stub.StreamingInputCall.async( - _streaming_input_request_iterator(test_pb2), SHORT_TIMEOUT) - self.assertIsNotNone(response_future.exception()) - - def testFullDuplexCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - responses = stub.FullDuplexCall( - _full_duplex_request_iterator(test_pb2), LONG_TIMEOUT) - expected_responses = methods.FullDuplexCall( - _full_duplex_request_iterator(test_pb2), 'not a real RpcContext!') - for expected_response, response in itertools.izip_longest( - expected_responses, responses): - self.assertEqual(expected_response, response) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testFullDuplexCallExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request_iterator = _full_duplex_request_iterator(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.pause(): - responses = stub.FullDuplexCall(request_iterator, SHORT_TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - list(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testFullDuplexCallCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - request_iterator = _full_duplex_request_iterator(test_pb2) - responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT) - next(responses) - responses.cancel() - with self.assertRaises(future.CancelledError): - next(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this hangs forever ' - 'and fix.') - def testFullDuplexCallFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top - request_iterator = _full_duplex_request_iterator(test_pb2) - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - with methods.fail(): - responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT) - self.assertIsNotNone(responses) - with self.assertRaises(exceptions.ServicerError): - next(responses) - - @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' - 'forever and fix.') - def testHalfDuplexCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top - with _CreateService(test_pb2, NO_DELAY) as ( - methods, stub, unused_server): - def half_duplex_request_iterator(): - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=1, interval_us=0) - yield request - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=2, interval_us=0) - request.response_parameters.add(size=3, interval_us=0) - yield request - responses = stub.HalfDuplexCall( - half_duplex_request_iterator(), LONG_TIMEOUT) - expected_responses = methods.HalfDuplexCall( - half_duplex_request_iterator(), 'not a real RpcContext!') - for check in itertools.izip_longest(expected_responses, responses): - expected_response, response = check - self.assertEqual(expected_response, response) - - def testHalfDuplexCallWedged(self): - import test_pb2 # pylint: disable=g-import-not-at-top - condition = threading.Condition() - wait_cell = [False] - @contextlib.contextmanager - def wait(): # pylint: disable=invalid-name - # Where's Python 3's 'nonlocal' statement when you need it? - with condition: - wait_cell[0] = True - yield - with condition: - wait_cell[0] = False - condition.notify_all() - def half_duplex_request_iterator(): - request = test_pb2.StreamingOutputCallRequest() - request.response_parameters.add(size=1, interval_us=0) - yield request - with condition: - while wait_cell[0]: - condition.wait() - with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): - with wait(): - responses = stub.HalfDuplexCall( - half_duplex_request_iterator(), SHORT_TIMEOUT) - # half-duplex waits for the client to send all info - with self.assertRaises(exceptions.ExpirationError): - next(responses) - - -if __name__ == '__main__': - os.chdir(os.path.dirname(sys.argv[0])) - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_protoc_plugin/test.proto b/src/python/grpcio_test/grpc_protoc_plugin/test.proto index ed7c6a7b79..6762a8e7f3 100644 --- a/src/python/grpcio_test/grpc_protoc_plugin/test.proto +++ b/src/python/grpcio_test/grpc_protoc_plugin/test.proto @@ -32,7 +32,7 @@ // This file is duplicated around the code base. See GitHub issue #526. syntax = "proto2"; -package grpc.testing; +package grpc_protoc_plugin; enum PayloadType { // Compressable text format. diff --git a/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py deleted file mode 100644 index 0d01ebc8dc..0000000000 --- a/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc_test._adapter import _face_test_case -from grpc_test.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case - - -class EventInvocationSynchronousEventServiceTest( - _face_test_case.FaceTestCase, - test_case.EventInvocationSynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py b/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py deleted file mode 100644 index dfbd0b60af..0000000000 --- a/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Common construction and destruction for GRPC-backed Face-layer tests.""" - -import unittest - -from grpc._adapter import fore -from grpc._adapter import rear -from grpc.framework.base import util -from grpc.framework.base import implementations as base_implementations -from grpc.framework.face import implementations as face_implementations -from grpc.framework.foundation import logging_pool -from grpc_test.framework.face.testing import coverage -from grpc_test.framework.face.testing import serial -from grpc_test.framework.face.testing import test_case - -_TIMEOUT = 3 -_MAXIMUM_TIMEOUT = 90 -_MAXIMUM_POOL_SIZE = 4 - - -class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage): - """Provides abstract Face-layer tests a GRPC-backed implementation.""" - - def set_up_implementation( - self, name, methods, method_implementations, - multi_method_implementation): - pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - - servicer = face_implementations.servicer( - pool, method_implementations, multi_method_implementation) - - serialization = serial.serialization(methods) - - fore_link = fore.ForeLink( - pool, serialization.request_deserializers, - serialization.response_serializers, None, ()) - fore_link.start() - port = fore_link.port() - rear_link = rear.RearLink( - 'localhost', port, pool, - serialization.request_serializers, - serialization.response_deserializers, False, None, None, None) - rear_link.start() - front = base_implementations.front_link(pool, pool, pool) - back = base_implementations.back_link( - servicer, pool, pool, pool, _TIMEOUT, _MAXIMUM_TIMEOUT) - fore_link.join_rear_link(back) - back.join_fore_link(fore_link) - rear_link.join_fore_link(front) - front.join_rear_link(rear_link) - - stub = face_implementations.generic_stub(front, pool) - return stub, (rear_link, fore_link, front, back) - - def tear_down_implementation(self, memo): - rear_link, fore_link, front, back = memo - # TODO(nathaniel): Waiting for the front and back to idle possibly should - # not be necessary - investigate as part of graceful shutdown work. - util.wait_for_idle(front) - util.wait_for_idle(back) - rear_link.stop() - fore_link.stop() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedStreamRequestStreamResponse(self): - raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py deleted file mode 100644 index ea4a6a0bae..0000000000 --- a/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc_test._adapter import _face_test_case -from grpc_test.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case - - -class FutureInvocationAsynchronousEventServiceTest( - _face_test_case.FaceTestCase, - test_case.FutureInvocationAsynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py b/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py index 27a5b82e9c..90ad0b9bcb 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py +++ b/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py @@ -191,7 +191,7 @@ class EchoTest(unittest.TestCase): metadata[server_leading_binary_metadata_key]) for datum in test_data: - client_call.write(datum, write_tag) + client_call.write(datum, write_tag, _low.WriteFlags.WRITE_NO_COMPRESS) write_accepted = self.client_events.get() self.assertIsNotNone(write_accepted) self.assertIs(write_accepted.kind, _low.Event.Kind.WRITE_ACCEPTED) @@ -206,7 +206,7 @@ class EchoTest(unittest.TestCase): self.assertIsNotNone(read_accepted.bytes) server_data.append(read_accepted.bytes) - server_call.write(read_accepted.bytes, write_tag) + server_call.write(read_accepted.bytes, write_tag, 0) write_accepted = self.server_events.get() self.assertIsNotNone(write_accepted) self.assertEqual(_low.Event.Kind.WRITE_ACCEPTED, write_accepted.kind) @@ -370,14 +370,14 @@ class CancellationTest(unittest.TestCase): self.assertIsNotNone(metadata_accepted) for datum in test_data: - client_call.write(datum, write_tag) + client_call.write(datum, write_tag, 0) write_accepted = self.client_events.get() server_call.read(read_tag) read_accepted = self.server_events.get() server_data.append(read_accepted.bytes) - server_call.write(read_accepted.bytes, write_tag) + server_call.write(read_accepted.bytes, write_tag, 0) write_accepted = self.server_events.get() self.assertIsNotNone(write_accepted) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_links_test.py b/src/python/grpcio_test/grpc_test/_adapter/_links_test.py deleted file mode 100644 index 4077b8e350..0000000000 --- a/src/python/grpcio_test/grpc_test/_adapter/_links_test.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Test of the GRPC-backed ForeLink and RearLink.""" - -import threading -import unittest - -from grpc._adapter import fore -from grpc._adapter import rear -from grpc.framework.base import interfaces -from grpc.framework.foundation import logging_pool -from grpc_test._adapter import _proto_scenarios -from grpc_test._adapter import _test_links - -_IDENTITY = lambda x: x -_TIMEOUT = 32 - - -# TODO(nathaniel): End-to-end metadata testing. -def _transform_metadata(unused_metadata): - return ( - ('one_unused_key', 'one unused value'), - ('another_unused_key', 'another unused value'), -) - - -class RoundTripTest(unittest.TestCase): - - def setUp(self): - self.fore_link_pool = logging_pool.pool(8) - self.rear_link_pool = logging_pool.pool(8) - - def tearDown(self): - self.rear_link_pool.shutdown(wait=True) - self.fore_link_pool.shutdown(wait=True) - - def testZeroMessageRoundTrip(self): - test_operation_id = object() - test_method = 'test method' - test_fore_link = _test_links.ForeLink(None, None) - def rear_action(front_to_back_ticket, fore_link): - if front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE): - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, 0, - interfaces.BackToFrontTicket.Kind.COMPLETION, None) - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: None}, {test_method: None}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, {test_method: None}, - {test_method: None}, False, None, None, None, - metadata_transformer=_transform_metadata) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, - test_method, interfaces.ServicedSubscription.Kind.FULL, None, None, - _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is - interfaces.BackToFrontTicket.Kind.CONTINUATION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_fore_link.condition: - self.assertIs( - test_fore_link.tickets[-1].kind, - interfaces.BackToFrontTicket.Kind.COMPLETION) - - def testEntireRoundTrip(self): - test_operation_id = object() - test_method = 'test method' - test_front_to_back_datum = b'\x07' - test_back_to_front_datum = b'\x08' - test_fore_link = _test_links.ForeLink(None, None) - rear_sequence_number = [0] - def rear_action(front_to_back_ticket, fore_link): - if front_to_back_ticket.payload is None: - payload = None - else: - payload = test_back_to_front_datum - terminal = front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE) - if payload is not None or terminal: - if terminal: - kind = interfaces.BackToFrontTicket.Kind.COMPLETION - else: - kind = interfaces.BackToFrontTicket.Kind.CONTINUATION - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, rear_sequence_number[0], kind, - payload) - rear_sequence_number[0] += 1 - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: _IDENTITY}, - {test_method: _IDENTITY}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, {test_method: _IDENTITY}, - {test_method: _IDENTITY}, False, None, None, None) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, - test_method, interfaces.ServicedSubscription.Kind.FULL, None, - test_front_to_back_datum, _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.COMPLETION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_rear_link.condition: - front_to_back_payloads = tuple( - ticket.payload for ticket in test_rear_link.tickets - if ticket.payload is not None) - with test_fore_link.condition: - back_to_front_payloads = tuple( - ticket.payload for ticket in test_fore_link.tickets - if ticket.payload is not None) - self.assertTupleEqual((test_front_to_back_datum,), front_to_back_payloads) - self.assertTupleEqual((test_back_to_front_datum,), back_to_front_payloads) - - def _perform_scenario_test(self, scenario): - test_operation_id = object() - test_method = scenario.method() - test_fore_link = _test_links.ForeLink(None, None) - rear_lock = threading.Lock() - rear_sequence_number = [0] - def rear_action(front_to_back_ticket, fore_link): - with rear_lock: - if front_to_back_ticket.payload is not None: - response = scenario.response_for_request(front_to_back_ticket.payload) - else: - response = None - terminal = front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE) - if response is not None or terminal: - if terminal: - kind = interfaces.BackToFrontTicket.Kind.COMPLETION - else: - kind = interfaces.BackToFrontTicket.Kind.CONTINUATION - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, rear_sequence_number[0], kind, - response) - rear_sequence_number[0] += 1 - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: scenario.deserialize_request}, - {test_method: scenario.serialize_response}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, - {test_method: scenario.serialize_request}, - {test_method: scenario.deserialize_response}, False, None, None, None) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - commencement_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, - interfaces.FrontToBackTicket.Kind.COMMENCEMENT, test_method, - interfaces.ServicedSubscription.Kind.FULL, None, None, - _TIMEOUT) - fore_sequence_number = 1 - rear_link.accept_front_to_back_ticket(commencement_ticket) - for request in scenario.requests(): - continuation_ticket = interfaces.FrontToBackTicket( - test_operation_id, fore_sequence_number, - interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, None, - request, None) - fore_sequence_number += 1 - rear_link.accept_front_to_back_ticket(continuation_ticket) - completion_ticket = interfaces.FrontToBackTicket( - test_operation_id, fore_sequence_number, - interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, None, - None) - fore_sequence_number += 1 - rear_link.accept_front_to_back_ticket(completion_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.COMPLETION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_rear_link.condition: - requests = tuple( - ticket.payload for ticket in test_rear_link.tickets - if ticket.payload is not None) - with test_fore_link.condition: - responses = tuple( - ticket.payload for ticket in test_fore_link.tickets - if ticket.payload is not None) - self.assertTrue(scenario.verify_requests(requests)) - self.assertTrue(scenario.verify_responses(responses)) - - def testEmptyScenario(self): - self._perform_scenario_test(_proto_scenarios.EmptyScenario()) - - def testBidirectionallyUnaryScenario(self): - self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) - - def testBidirectionallyStreamingScenario(self): - self._perform_scenario_test( - _proto_scenarios.BidirectionallyStreamingScenario()) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py b/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py deleted file mode 100644 index 9b5758f60f..0000000000 --- a/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""A test of invocation-side code unconnected to an RPC server.""" - -import unittest - -from grpc._adapter import rear -from grpc.framework.base import interfaces -from grpc.framework.foundation import logging_pool -from grpc_test._adapter import _test_links - -_IDENTITY = lambda x: x -_TIMEOUT = 2 - - -class LonelyRearLinkTest(unittest.TestCase): - - def setUp(self): - self.pool = logging_pool.pool(8) - - def tearDown(self): - self.pool.shutdown(wait=True) - - def testUpAndDown(self): - rear_link = rear.RearLink( - 'nonexistent', 54321, self.pool, {}, {}, False, None, None, None) - - rear_link.start() - rear_link.stop() - - def _perform_lonely_client_test_with_ticket_kind( - self, front_to_back_ticket_kind): - test_operation_id = object() - test_method = 'test method' - fore_link = _test_links.ForeLink(None, None) - - rear_link = rear.RearLink( - 'nonexistent', 54321, self.pool, {test_method: None}, - {test_method: None}, False, None, None, None) - rear_link.join_fore_link(fore_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, front_to_back_ticket_kind, test_method, - interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with fore_link.condition: - while True: - if (fore_link.tickets and - fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.CONTINUATION): - break - fore_link.condition.wait() - - rear_link.stop() - - with fore_link.condition: - self.assertIsNot( - fore_link.tickets[-1].kind, - interfaces.BackToFrontTicket.Kind.COMPLETION) - - def testLonelyClientCommencementTicket(self): - self._perform_lonely_client_test_with_ticket_kind( - interfaces.FrontToBackTicket.Kind.COMMENCEMENT) - - def testLonelyClientEntireTicket(self): - self._perform_lonely_client_test_with_ticket_kind( - interfaces.FrontToBackTicket.Kind.ENTIRE) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_low_test.py b/src/python/grpcio_test/grpc_test/_adapter/_low_test.py index 70149127da..8115cd0e83 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/_low_test.py +++ b/src/python/grpcio_test/grpc_test/_adapter/_low_test.py @@ -34,6 +34,7 @@ import unittest from grpc import _grpcio_metadata from grpc._adapter import _types from grpc._adapter import _low +from grpc_test import test_common def wait_for_events(completion_queues, deadline): @@ -140,16 +141,16 @@ class InsecureServerInsecureClient(unittest.TestCase): self.assertIsInstance(request_event.call, _low.Call) self.assertIs(server_request_tag, request_event.tag) self.assertEqual(1, len(request_event.results)) - received_initial_metadata = dict(request_event.results[0].initial_metadata) + received_initial_metadata = request_event.results[0].initial_metadata # Check that our metadata were transmitted - self.assertEqual( - dict(client_initial_metadata), - dict((x, received_initial_metadata[x]) - for x in zip(*client_initial_metadata)[0])) + self.assertTrue(test_common.metadata_transmitted(client_initial_metadata, + received_initial_metadata)) # Check that Python's user agent string is a part of the full user agent # string + received_initial_metadata_dict = dict(received_initial_metadata) + self.assertIn('user-agent', received_initial_metadata_dict) self.assertIn('Python-gRPC-{}'.format(_grpcio_metadata.__version__), - received_initial_metadata['user-agent']) + received_initial_metadata_dict['user-agent']) self.assertEqual(method, request_event.call_details.method) self.assertEqual(host, request_event.call_details.host) self.assertLess(abs(deadline - request_event.call_details.deadline), @@ -193,13 +194,15 @@ class InsecureServerInsecureClient(unittest.TestCase): self.assertNotIn(client_result.type, found_client_op_types) found_client_op_types.add(client_result.type) if client_result.type == _types.OpType.RECV_INITIAL_METADATA: - self.assertEqual(dict(server_initial_metadata), - dict(client_result.initial_metadata)) + self.assertTrue( + test_common.metadata_transmitted(server_initial_metadata, + client_result.initial_metadata)) elif client_result.type == _types.OpType.RECV_MESSAGE: self.assertEqual(response, client_result.message) elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: - self.assertEqual(dict(server_trailing_metadata), - dict(client_result.trailing_metadata)) + self.assertTrue( + test_common.metadata_transmitted(server_trailing_metadata, + client_result.trailing_metadata)) self.assertEqual(server_status_details, client_result.status.details) self.assertEqual(server_status_code, client_result.status.code) self.assertEqual(set([ diff --git a/src/python/grpcio_test/grpc_test/_adapter/_test_links.py b/src/python/grpcio_test/grpc_test/_adapter/_test_links.py deleted file mode 100644 index 86c7e61b17..0000000000 --- a/src/python/grpcio_test/grpc_test/_adapter/_test_links.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Links suitable for use in tests.""" - -import threading - -from grpc.framework.base import interfaces - - -class ForeLink(interfaces.ForeLink): - """A ForeLink suitable for use in tests of RearLinks.""" - - def __init__(self, action, rear_link): - self.condition = threading.Condition() - self.tickets = [] - self.action = action - self.rear_link = rear_link - - def accept_back_to_front_ticket(self, ticket): - with self.condition: - self.tickets.append(ticket) - self.condition.notify_all() - action, rear_link = self.action, self.rear_link - - if action is not None: - action(ticket, rear_link) - - def join_rear_link(self, rear_link): - with self.condition: - self.rear_link = rear_link - - -class RearLink(interfaces.RearLink): - """A RearLink suitable for use in tests of ForeLinks.""" - - def __init__(self, action, fore_link): - self.condition = threading.Condition() - self.tickets = [] - self.action = action - self.fore_link = fore_link - - def accept_front_to_back_ticket(self, ticket): - with self.condition: - self.tickets.append(ticket) - self.condition.notify_all() - action, fore_link = self.action, self.fore_link - - if action is not None: - action(ticket, fore_link) - - def join_fore_link(self, fore_link): - with self.condition: - self.fore_link = fore_link diff --git a/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py b/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py index f0bd989ea6..cafb6b6eae 100644 --- a/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py +++ b/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py @@ -38,6 +38,7 @@ import unittest from grpc._adapter import _intermediary_low from grpc._links import invocation from grpc._links import service +from grpc.beta import interfaces as beta_interfaces from grpc.framework.core import implementations from grpc.framework.interfaces.base import utilities from grpc_test import test_common as grpc_test_common @@ -45,8 +46,6 @@ from grpc_test.framework.common import test_constants from grpc_test.framework.interfaces.base import test_cases from grpc_test.framework.interfaces.base import test_interfaces -_CODE = _intermediary_low.Code.OK - class _SerializationBehaviors( collections.namedtuple( @@ -124,8 +123,8 @@ class _Implementation(test_interfaces.Implementation): def service_completion(self): return utilities.completion( - grpc_test_common.SERVICE_TERMINAL_METADATA, _CODE, - grpc_test_common.DETAILS) + grpc_test_common.SERVICE_TERMINAL_METADATA, + beta_interfaces.StatusCode.OK, grpc_test_common.DETAILS) def metadata_transmitted(self, original_metadata, transmitted_metadata): return original_metadata is None or grpc_test_common.metadata_transmitted( diff --git a/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py b/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py index 28c0619f7c..a4d4dee38c 100644 --- a/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py +++ b/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py @@ -35,6 +35,7 @@ import unittest from grpc._adapter import _intermediary_low from grpc._links import invocation from grpc._links import service +from grpc.beta import interfaces as beta_interfaces from grpc.framework.core import implementations as core_implementations from grpc.framework.crust import implementations as crust_implementations from grpc.framework.foundation import logging_pool @@ -139,7 +140,7 @@ class _Implementation(test_interfaces.Implementation): return grpc_test_common.SERVICE_TERMINAL_METADATA def code(self): - return _intermediary_low.Code.OK + return beta_interfaces.StatusCode.OK def details(self): return grpc_test_common.DETAILS diff --git a/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py b/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py index 637506b42e..1307a30ca0 100644 --- a/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py +++ b/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py @@ -32,6 +32,7 @@ import unittest from grpc._cython import cygrpc from grpc_test._cython import test_utilities +from grpc_test import test_common class TypeSmokeTest(unittest.TestCase): @@ -139,7 +140,7 @@ class InsecureServerInsecureClient(unittest.TestCase): CLIENT_METADATA_BIN_VALUE = b'\0'*1000 SERVER_INITIAL_METADATA_KEY = b'init_me_me_me' SERVER_INITIAL_METADATA_VALUE = b'whodawha?' - SERVER_TRAILING_METADATA_KEY = b'California_is_in_a_drought' + SERVER_TRAILING_METADATA_KEY = b'california_is_in_a_drought' SERVER_TRAILING_METADATA_VALUE = b'zomg it is' SERVER_STATUS_CODE = cygrpc.StatusCode.ok SERVER_STATUS_DETAILS = b'our work is never over' @@ -158,8 +159,8 @@ class InsecureServerInsecureClient(unittest.TestCase): self.assertEqual(cygrpc.CallError.ok, request_call_result) client_call_tag = object() - client_call = self.client_channel.create_call(self.client_completion_queue, - METHOD, HOST, cygrpc_deadline) + client_call = self.client_channel.create_call( + None, 0, self.client_completion_queue, METHOD, HOST, cygrpc_deadline) client_initial_metadata = cygrpc.Metadata([ cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY, CLIENT_METADATA_ASCII_VALUE), @@ -182,8 +183,9 @@ class InsecureServerInsecureClient(unittest.TestCase): self.assertIsInstance(request_event.operation_call, cygrpc.Call) self.assertIs(server_request_tag, request_event.tag) self.assertEqual(0, len(request_event.batch_operations)) - self.assertEqual(dict(client_initial_metadata), - dict(request_event.request_metadata)) + self.assertTrue( + test_common.metadata_transmitted(client_initial_metadata, + request_event.request_metadata)) self.assertEqual(METHOD, request_event.request_call_details.method) self.assertEqual(HOST, request_event.request_call_details.host) self.assertLess( @@ -218,13 +220,15 @@ class InsecureServerInsecureClient(unittest.TestCase): self.assertNotIn(client_result.type, found_client_op_types) found_client_op_types.add(client_result.type) if client_result.type == cygrpc.OperationType.receive_initial_metadata: - self.assertEqual(dict(server_initial_metadata), - dict(client_result.received_metadata)) + self.assertTrue( + test_common.metadata_transmitted(server_initial_metadata, + client_result.received_metadata)) elif client_result.type == cygrpc.OperationType.receive_message: self.assertEqual(RESPONSE, client_result.received_message.bytes()) elif client_result.type == cygrpc.OperationType.receive_status_on_client: - self.assertEqual(dict(server_trailing_metadata), - dict(client_result.received_metadata)) + self.assertTrue( + test_common.metadata_transmitted(server_trailing_metadata, + client_result.received_metadata)) self.assertEqual(SERVER_STATUS_DETAILS, client_result.received_status_details) self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code) diff --git a/src/python/grpcio_test/grpc_test/_links/_transmission_test.py b/src/python/grpcio_test/grpc_test/_links/_transmission_test.py index 716323cc20..77e83d5561 100644 --- a/src/python/grpcio_test/grpc_test/_links/_transmission_test.py +++ b/src/python/grpcio_test/grpc_test/_links/_transmission_test.py @@ -34,6 +34,7 @@ import unittest from grpc._adapter import _intermediary_low from grpc._links import invocation from grpc._links import service +from grpc.beta import interfaces as beta_interfaces from grpc.framework.interfaces.links import links from grpc_test import test_common from grpc_test._links import _proto_scenarios @@ -93,7 +94,8 @@ class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase): return None, None def create_service_completion(self): - return _intermediary_low.Code.OK, 'An exuberant test "details" message!' + return ( + beta_interfaces.StatusCode.OK, b'An exuberant test "details" message!') def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): self.assertTrue( @@ -110,7 +112,7 @@ class RoundTripTest(unittest.TestCase): test_group = 'test package.Test Group' test_method = 'test method' identity_transformation = {(test_group, test_method): _IDENTITY} - test_code = _intermediary_low.Code.OK + test_code = beta_interfaces.StatusCode.OK test_message = 'a test message' service_link = service.service_link( @@ -150,11 +152,13 @@ class RoundTripTest(unittest.TestCase): self.assertIs( invocation_mate.tickets()[-1].termination, links.Ticket.Termination.COMPLETION) + self.assertIs(invocation_mate.tickets()[-1].code, test_code) + self.assertEqual(invocation_mate.tickets()[-1].message, test_message) def _perform_scenario_test(self, scenario): test_operation_id = object() test_group, test_method = scenario.group_and_method() - test_code = _intermediary_low.Code.OK + test_code = beta_interfaces.StatusCode.OK test_message = 'a scenario test message' service_link = service.service_link( diff --git a/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py b/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py new file mode 100644 index 0000000000..fad57da9d0 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py @@ -0,0 +1,232 @@ +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests Face interface compliance of the gRPC Python Beta API.""" + +import threading +import unittest + +from grpc.beta import implementations +from grpc.beta import interfaces +from grpc.framework.common import cardinality +from grpc.framework.interfaces.face import utilities +from grpc_test import resources +from grpc_test.beta import test_utilities +from grpc_test.framework.common import test_constants + +_SERVER_HOST_OVERRIDE = 'foo.test.google.fr' + +_GROUP = 'group' +_UNARY_UNARY = 'unary-unary' +_UNARY_STREAM = 'unary-stream' +_STREAM_UNARY = 'stream-unary' +_STREAM_STREAM = 'stream-stream' + +_REQUEST = b'abc' +_RESPONSE = b'123' + + +class _Servicer(object): + + def __init__(self): + self._condition = threading.Condition() + self._peer = None + self._serviced = False + + def unary_unary(self, request, context): + with self._condition: + self._request = request + self._peer = context.protocol_context().peer() + context.protocol_context().disable_next_response_compression() + self._serviced = True + self._condition.notify_all() + return _RESPONSE + + def unary_stream(self, request, context): + with self._condition: + self._request = request + self._peer = context.protocol_context().peer() + context.protocol_context().disable_next_response_compression() + self._serviced = True + self._condition.notify_all() + return + yield + + def stream_unary(self, request_iterator, context): + for request in request_iterator: + self._request = request + with self._condition: + self._peer = context.protocol_context().peer() + context.protocol_context().disable_next_response_compression() + self._serviced = True + self._condition.notify_all() + return _RESPONSE + + def stream_stream(self, request_iterator, context): + for request in request_iterator: + with self._condition: + self._peer = context.protocol_context().peer() + context.protocol_context().disable_next_response_compression() + yield _RESPONSE + with self._condition: + self._serviced = True + self._condition.notify_all() + + def peer(self): + with self._condition: + return self._peer + + def block_until_serviced(self): + with self._condition: + while not self._serviced: + self._condition.wait() + + +class _BlockingIterator(object): + + def __init__(self, upstream): + self._condition = threading.Condition() + self._upstream = upstream + self._allowed = [] + + def __iter__(self): + return self + + def next(self): + with self._condition: + while True: + if self._allowed is None: + raise StopIteration() + elif self._allowed: + return self._allowed.pop(0) + else: + self._condition.wait() + + def allow(self): + with self._condition: + try: + self._allowed.append(next(self._upstream)) + except StopIteration: + self._allowed = None + self._condition.notify_all() + + +class BetaFeaturesTest(unittest.TestCase): + + def setUp(self): + self._servicer = _Servicer() + method_implementations = { + (_GROUP, _UNARY_UNARY): + utilities.unary_unary_inline(self._servicer.unary_unary), + (_GROUP, _UNARY_STREAM): + utilities.unary_stream_inline(self._servicer.unary_stream), + (_GROUP, _STREAM_UNARY): + utilities.stream_unary_inline(self._servicer.stream_unary), + (_GROUP, _STREAM_STREAM): + utilities.stream_stream_inline(self._servicer.stream_stream), + } + + cardinalities = { + _UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY, + _UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM, + _STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY, + _STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM, + } + + server_options = implementations.server_options( + thread_pool_size=test_constants.POOL_SIZE) + self._server = implementations.server( + method_implementations, options=server_options) + server_credentials = implementations.ssl_server_credentials( + [(resources.private_key(), resources.certificate_chain(),),]) + port = self._server.add_secure_port('[::]:0', server_credentials) + self._server.start() + self._client_credentials = implementations.ssl_client_credentials( + resources.test_root_certificates(), None, None) + channel = test_utilities.not_really_secure_channel( + 'localhost', port, self._client_credentials, _SERVER_HOST_OVERRIDE) + stub_options = implementations.stub_options( + thread_pool_size=test_constants.POOL_SIZE) + self._dynamic_stub = implementations.dynamic_stub( + channel, _GROUP, cardinalities, options=stub_options) + + def tearDown(self): + self._dynamic_stub = None + self._server.stop(test_constants.SHORT_TIMEOUT).wait() + + def test_unary_unary(self): + call_options = interfaces.grpc_call_options( + disable_compression=True, credentials=self._client_credentials) + response = getattr(self._dynamic_stub, _UNARY_UNARY)( + _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options) + self.assertEqual(_RESPONSE, response) + self.assertIsNotNone(self._servicer.peer()) + + def test_unary_stream(self): + call_options = interfaces.grpc_call_options( + disable_compression=True, credentials=self._client_credentials) + response_iterator = getattr(self._dynamic_stub, _UNARY_STREAM)( + _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options) + self._servicer.block_until_serviced() + self.assertIsNotNone(self._servicer.peer()) + + def test_stream_unary(self): + call_options = interfaces.grpc_call_options( + credentials=self._client_credentials) + request_iterator = _BlockingIterator(iter((_REQUEST,))) + response_future = getattr(self._dynamic_stub, _STREAM_UNARY).future( + request_iterator, test_constants.LONG_TIMEOUT, + protocol_options=call_options) + response_future.protocol_context().disable_next_request_compression() + request_iterator.allow() + response_future.protocol_context().disable_next_request_compression() + request_iterator.allow() + self._servicer.block_until_serviced() + self.assertIsNotNone(self._servicer.peer()) + self.assertEqual(_RESPONSE, response_future.result()) + + def test_stream_stream(self): + call_options = interfaces.grpc_call_options( + credentials=self._client_credentials) + request_iterator = _BlockingIterator(iter((_REQUEST,))) + response_iterator = getattr(self._dynamic_stub, _STREAM_STREAM)( + request_iterator, test_constants.SHORT_TIMEOUT, + protocol_options=call_options) + response_iterator.protocol_context().disable_next_request_compression() + request_iterator.allow() + response = next(response_iterator) + response_iterator.protocol_context().disable_next_request_compression() + request_iterator.allow() + self._servicer.block_until_serviced() + self.assertIsNotNone(self._servicer.peer()) + self.assertEqual(_RESPONSE, response) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py b/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py index 038464889d..b3c05bdb0c 100644 --- a/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py +++ b/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py @@ -36,14 +36,9 @@ import unittest from grpc._adapter import _low from grpc._adapter import _types from grpc.beta import _connectivity_channel +from grpc.beta import interfaces from grpc_test.framework.common import test_constants -_MAPPING_FUNCTION = lambda integer: integer * 200 + 17 -_MAPPING = { - state: _MAPPING_FUNCTION(state) for state in _types.ConnectivityState} -_IDLE, _CONNECTING, _READY, _TRANSIENT_FAILURE, _FATAL_FAILURE = map( - _MAPPING_FUNCTION, _types.ConnectivityState) - def _drive_completion_queue(completion_queue): while True: @@ -84,7 +79,7 @@ class ChannelConnectivityTest(unittest.TestCase): callback = _Callback() connectivity_channel = _connectivity_channel.ConnectivityChannel( - low_channel, _MAPPING) + low_channel) connectivity_channel.subscribe(callback.update, try_to_connect=False) first_connectivities = callback.block_until_connectivities_satisfy(bool) connectivity_channel.subscribe(callback.update, try_to_connect=True) @@ -98,11 +93,16 @@ class ChannelConnectivityTest(unittest.TestCase): connectivity_channel.unsubscribe(callback.update) fifth_connectivities = callback.connectivities() - self.assertSequenceEqual((_IDLE,), first_connectivities) - self.assertNotIn(_READY, second_connectivities) - self.assertNotIn(_READY, third_connectivities) - self.assertNotIn(_READY, fourth_connectivities) - self.assertNotIn(_READY, fifth_connectivities) + self.assertSequenceEqual( + (interfaces.ChannelConnectivity.IDLE,), first_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.READY, second_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.READY, third_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.READY, fourth_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.READY, fifth_connectivities) def test_immediately_connectable_channel_connectivity(self): server_completion_queue = _low.CompletionQueue() @@ -117,7 +117,7 @@ class ChannelConnectivityTest(unittest.TestCase): second_callback = _Callback() connectivity_channel = _connectivity_channel.ConnectivityChannel( - low_channel, _MAPPING) + low_channel) connectivity_channel.subscribe(first_callback.update, try_to_connect=False) first_connectivities = first_callback.block_until_connectivities_satisfy( bool) @@ -132,9 +132,11 @@ class ChannelConnectivityTest(unittest.TestCase): bool) # Wait for a connection that will happen (or may already have happened). first_callback.block_until_connectivities_satisfy( - lambda connectivities: _READY in connectivities) + lambda connectivities: + interfaces.ChannelConnectivity.READY in connectivities) second_callback.block_until_connectivities_satisfy( - lambda connectivities: _READY in connectivities) + lambda connectivities: + interfaces.ChannelConnectivity.READY in connectivities) connectivity_channel.unsubscribe(first_callback.update) connectivity_channel.unsubscribe(second_callback.update) @@ -142,12 +144,19 @@ class ChannelConnectivityTest(unittest.TestCase): server_completion_queue.shutdown() server_completion_queue_thread.join() - self.assertSequenceEqual((_IDLE,), first_connectivities) - self.assertSequenceEqual((_IDLE,), second_connectivities) - self.assertNotIn(_TRANSIENT_FAILURE, third_connectivities) - self.assertNotIn(_FATAL_FAILURE, third_connectivities) - self.assertNotIn(_TRANSIENT_FAILURE, fourth_connectivities) - self.assertNotIn(_FATAL_FAILURE, fourth_connectivities) + self.assertSequenceEqual( + (interfaces.ChannelConnectivity.IDLE,), first_connectivities) + self.assertSequenceEqual( + (interfaces.ChannelConnectivity.IDLE,), second_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.TRANSIENT_FAILURE, third_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.FATAL_FAILURE, third_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.TRANSIENT_FAILURE, + fourth_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.FATAL_FAILURE, fourth_connectivities) def test_reachable_then_unreachable_channel_connectivity(self): server_completion_queue = _low.CompletionQueue() @@ -161,14 +170,16 @@ class ChannelConnectivityTest(unittest.TestCase): callback = _Callback() connectivity_channel = _connectivity_channel.ConnectivityChannel( - low_channel, _MAPPING) + low_channel) connectivity_channel.subscribe(callback.update, try_to_connect=True) callback.block_until_connectivities_satisfy( - lambda connectivities: _READY in connectivities) + lambda connectivities: + interfaces.ChannelConnectivity.READY in connectivities) # Now take down the server and confirm that channel readiness is repudiated. server.shutdown() callback.block_until_connectivities_satisfy( - lambda connectivities: connectivities[-1] is not _READY) + lambda connectivities: + connectivities[-1] is not interfaces.ChannelConnectivity.READY) connectivity_channel.unsubscribe(callback.update) server.shutdown() diff --git a/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py b/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py index ce4c59c0ee..aa33e1e6f8 100644 --- a/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py +++ b/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py @@ -32,8 +32,8 @@ import collections import unittest -from grpc._adapter import _intermediary_low -from grpc.beta import beta +from grpc.beta import implementations +from grpc.beta import interfaces from grpc_test import resources from grpc_test import test_common as grpc_test_common from grpc_test.beta import test_utilities @@ -81,25 +81,26 @@ class _Implementation(test_interfaces.Implementation): method: method_object.cardinality() for (group, method), method_object in methods.iteritems()} - server_options = beta.server_options( + server_options = implementations.server_options( request_deserializers=serialization_behaviors.request_deserializers, response_serializers=serialization_behaviors.response_serializers, thread_pool_size=test_constants.POOL_SIZE) - server = beta.server(method_implementations, options=server_options) - server_credentials = beta.ssl_server_credentials( + server = implementations.server( + method_implementations, options=server_options) + server_credentials = implementations.ssl_server_credentials( [(resources.private_key(), resources.certificate_chain(),),]) port = server.add_secure_port('[::]:0', server_credentials) server.start() - client_credentials = beta.ssl_client_credentials( + client_credentials = implementations.ssl_client_credentials( resources.test_root_certificates(), None, None) - channel = test_utilities.create_not_really_secure_channel( + channel = test_utilities.not_really_secure_channel( 'localhost', port, client_credentials, _SERVER_HOST_OVERRIDE) - stub_options = beta.stub_options( + stub_options = implementations.stub_options( request_serializers=serialization_behaviors.request_serializers, response_deserializers=serialization_behaviors.response_deserializers, thread_pool_size=test_constants.POOL_SIZE) - generic_stub = beta.generic_stub(channel, options=stub_options) - dynamic_stub = beta.dynamic_stub( + generic_stub = implementations.generic_stub(channel, options=stub_options) + dynamic_stub = implementations.dynamic_stub( channel, service, cardinalities, options=stub_options) return generic_stub, {service: dynamic_stub}, server @@ -116,7 +117,7 @@ class _Implementation(test_interfaces.Implementation): return grpc_test_common.SERVICE_TERMINAL_METADATA def code(self): - return _intermediary_low.Code.OK + return interfaces.StatusCode.OK def details(self): return grpc_test_common.DETAILS diff --git a/src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py b/src/python/grpcio_test/grpc_test/beta/_not_found_test.py index a1f776211c..5feb997fef 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py +++ b/src/python/grpcio_test/grpc_test/beta/_not_found_test.py @@ -27,19 +27,48 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""One of the tests of the Face layer of RPC Framework.""" +"""Tests of RPC-method-not-found behavior.""" import unittest -from grpc_test._adapter import _face_test_case -from grpc_test.framework.face.testing import blocking_invocation_inline_service_test_case as test_case +from grpc.beta import implementations +from grpc.beta import interfaces +from grpc.framework.interfaces.face import face +from grpc_test.framework.common import test_constants -class BlockingInvocationInlineServiceTest( - _face_test_case.FaceTestCase, - test_case.BlockingInvocationInlineServiceTestCase, - unittest.TestCase): - pass +class NotFoundTest(unittest.TestCase): + + def setUp(self): + self._server = implementations.server({}) + port = self._server.add_insecure_port('[::]:0') + channel = implementations.insecure_channel('localhost', port) + self._generic_stub = implementations.generic_stub(channel) + self._server.start() + + def tearDown(self): + self._server.stop(0).wait() + self._generic_stub = None + + def test_blocking_unary_unary_not_found(self): + with self.assertRaises(face.LocalError) as exception_assertion_context: + self._generic_stub.blocking_unary_unary( + 'groop', 'meffod', b'abc', test_constants.LONG_TIMEOUT, + with_call=True) + self.assertIs( + exception_assertion_context.exception.code, + interfaces.StatusCode.UNIMPLEMENTED) + + def test_future_stream_unary_not_found(self): + rpc_future = self._generic_stub.future_stream_unary( + 'grupe', 'mevvod', b'def', test_constants.LONG_TIMEOUT) + with self.assertRaises(face.LocalError) as exception_assertion_context: + rpc_future.result() + self.assertIs( + exception_assertion_context.exception.code, + interfaces.StatusCode.UNIMPLEMENTED) + self.assertIs( + rpc_future.exception().code, interfaces.StatusCode.UNIMPLEMENTED) if __name__ == '__main__': diff --git a/src/python/grpcio_test/grpc_test/beta/_utilities_test.py b/src/python/grpcio_test/grpc_test/beta/_utilities_test.py index 998e74ccf4..996cea9118 100644 --- a/src/python/grpcio_test/grpc_test/beta/_utilities_test.py +++ b/src/python/grpcio_test/grpc_test/beta/_utilities_test.py @@ -35,7 +35,7 @@ import unittest from grpc._adapter import _low from grpc._adapter import _types -from grpc.beta import beta +from grpc.beta import implementations from grpc.beta import utilities from grpc.framework.foundation import future from grpc_test.framework.common import test_constants @@ -69,7 +69,7 @@ class _Callback(object): class ChannelConnectivityTest(unittest.TestCase): def test_lonely_channel_connectivity(self): - channel = beta.create_insecure_channel('localhost', 12345) + channel = implementations.insecure_channel('localhost', 12345) callback = _Callback() ready_future = utilities.channel_ready_future(channel) @@ -94,7 +94,7 @@ class ChannelConnectivityTest(unittest.TestCase): server_completion_queue_thread = threading.Thread( target=_drive_completion_queue, args=(server_completion_queue,)) server_completion_queue_thread.start() - channel = beta.create_insecure_channel('localhost', port) + channel = implementations.insecure_channel('localhost', port) callback = _Callback() try: diff --git a/src/python/grpcio_test/grpc_test/beta/test_utilities.py b/src/python/grpcio_test/grpc_test/beta/test_utilities.py index 338670478d..24a8600e12 100644 --- a/src/python/grpcio_test/grpc_test/beta/test_utilities.py +++ b/src/python/grpcio_test/grpc_test/beta/test_utilities.py @@ -30,25 +30,27 @@ """Test-appropriate entry points into the gRPC Python Beta API.""" from grpc._adapter import _intermediary_low -from grpc.beta import beta +from grpc.beta import implementations -def create_not_really_secure_channel( +def not_really_secure_channel( host, port, client_credentials, server_host_override): """Creates an insecure Channel to a remote host. Args: host: The name of the remote host to which to connect. port: The port of the remote host to which to connect. - client_credentials: The beta.ClientCredentials with which to connect. + client_credentials: The implementations.ClientCredentials with which to + connect. server_host_override: The target name used for SSL host name checking. Returns: - A beta.Channel to the remote host through which RPCs may be conducted. + An implementations.Channel to the remote host through which RPCs may be + conducted. """ hostport = '%s:%d' % (host, port) intermediary_low_channel = _intermediary_low.Channel( hostport, client_credentials._intermediary_low_credentials, server_host_override=server_host_override) - return beta.Channel( + return implementations.Channel( intermediary_low_channel._internal, intermediary_low_channel) diff --git a/src/python/grpcio_test/grpc_test/conftest.py b/src/python/grpcio_test/grpc_test/conftest.py new file mode 100644 index 0000000000..357320ec64 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/conftest.py @@ -0,0 +1,60 @@ +import types +import unittest + +import pytest + + +class LoadTestsSuiteCollector(pytest.Collector): + + def __init__(self, name, parent, suite): + super(LoadTestsSuiteCollector, self).__init__(name, parent=parent) + self.suite = suite + self.obj = suite + + def collect(self): + collected = [] + for case in self.suite: + if isinstance(case, unittest.TestCase): + collected.append(LoadTestsCase(case.id(), self, case)) + elif isinstance(case, unittest.TestSuite): + collected.append( + LoadTestsSuiteCollector('suite_child_of_mine', self, case)) + return collected + + def reportinfo(self): + return str(self.suite) + + +class LoadTestsCase(pytest.Function): + + def __init__(self, name, parent, item): + super(LoadTestsCase, self).__init__(name, parent, callobj=self._item_run) + self.item = item + + def _item_run(self): + result = unittest.TestResult() + self.item(result) + if result.failures: + test_method, trace = result.failures[0] + pytest.fail(trace, False) + elif result.errors: + test_method, trace = result.errors[0] + pytest.fail(trace, False) + elif result.skipped: + test_method, reason = result.skipped[0] + pytest.skip(reason) + + +def pytest_pycollect_makeitem(collector, name, obj): + if name == 'load_tests' and isinstance(obj, types.FunctionType): + suite = unittest.TestSuite() + loader = unittest.TestLoader() + pattern = '*' + try: + # Check that the 'load_tests' object is actually a callable that actually + # accepts the arguments expected for the load_tests protocol. + suite = obj(loader, suite, pattern) + except Exception as e: + return None + else: + return LoadTestsSuiteCollector(name, collector, suite) diff --git a/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py b/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py deleted file mode 100644 index 611637e8b8..0000000000 --- a/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# TODO(nathaniel): Expand this test coverage. - -"""Test of the GRPC-backed ForeLink and RearLink.""" - -import unittest - -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -from grpc_test._junkdrawer import math_pb2 - -SERVICE_NAME = 'math.Math' - -DIV = 'Div' -DIV_MANY = 'DivMany' -FIB = 'Fib' -SUM = 'Sum' - -def _fibbonacci(limit): - left, right = 0, 1 - for _ in xrange(limit): - yield left - left, right = right, left + right - - -def _div(request, unused_context): - return math_pb2.DivReply( - quotient=request.dividend / request.divisor, - remainder=request.dividend % request.divisor) - - -def _div_many(request_iterator, unused_context): - for request in request_iterator: - yield math_pb2.DivReply( - quotient=request.dividend / request.divisor, - remainder=request.dividend % request.divisor) - - -def _fib(request, unused_context): - for number in _fibbonacci(request.limit): - yield math_pb2.Num(num=number) - - -def _sum(request_iterator, unused_context): - accumulation = 0 - for request in request_iterator: - accumulation += request.num - return math_pb2.Num(num=accumulation) - - -_INVOCATION_DESCRIPTIONS = { - DIV: utilities.unary_unary_invocation_description( - math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), - DIV_MANY: utilities.stream_stream_invocation_description( - math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), - FIB: utilities.unary_stream_invocation_description( - math_pb2.FibArgs.SerializeToString, math_pb2.Num.FromString), - SUM: utilities.stream_unary_invocation_description( - math_pb2.Num.SerializeToString, math_pb2.Num.FromString), -} - -_SERVICE_DESCRIPTIONS = { - DIV: utilities.unary_unary_service_description( - _div, math_pb2.DivArgs.FromString, - math_pb2.DivReply.SerializeToString), - DIV_MANY: utilities.stream_stream_service_description( - _div_many, math_pb2.DivArgs.FromString, - math_pb2.DivReply.SerializeToString), - FIB: utilities.unary_stream_service_description( - _fib, math_pb2.FibArgs.FromString, math_pb2.Num.SerializeToString), - SUM: utilities.stream_unary_service_description( - _sum, math_pb2.Num.FromString, math_pb2.Num.SerializeToString), -} - -_TIMEOUT = 3 - - -class EarlyAdopterImplementationsTest(unittest.TestCase): - - def setUp(self): - self.server = implementations.server( - SERVICE_NAME, _SERVICE_DESCRIPTIONS, 0) - self.server.start() - port = self.server.port() - self.stub = implementations.stub( - SERVICE_NAME, _INVOCATION_DESCRIPTIONS, 'localhost', port) - - def tearDown(self): - self.server.stop() - - def testUpAndDown(self): - with self.stub: - pass - - def testUnaryUnary(self): - divisor = 59 - dividend = 973 - expected_quotient = dividend / divisor - expected_remainder = dividend % divisor - - with self.stub: - response = self.stub.Div( - math_pb2.DivArgs(divisor=divisor, dividend=dividend), _TIMEOUT) - self.assertEqual(expected_quotient, response.quotient) - self.assertEqual(expected_remainder, response.remainder) - - def testUnaryStream(self): - stream_length = 43 - - with self.stub: - response_iterator = self.stub.Fib( - math_pb2.FibArgs(limit=stream_length), _TIMEOUT) - numbers = tuple(response.num for response in response_iterator) - for early, middle, later in zip(numbers, numbers[:1], numbers[:2]): - self.assertEqual(early + middle, later) - self.assertEqual(stream_length, len(numbers)) - - def testStreamUnary(self): - stream_length = 127 - - with self.stub: - response_future = self.stub.Sum.async( - (math_pb2.Num(num=index) for index in range(stream_length)), - _TIMEOUT) - self.assertEqual( - (stream_length * (stream_length - 1)) / 2, - response_future.result().num) - - def testStreamStream(self): - stream_length = 179 - divisor_offset = 71 - dividend_offset = 1763 - - with self.stub: - response_iterator = self.stub.DivMany( - (math_pb2.DivArgs( - divisor=divisor_offset + index, - dividend=dividend_offset + index) - for index in range(stream_length)), - _TIMEOUT) - for index, response in enumerate(response_iterator): - self.assertEqual( - (dividend_offset + index) / (divisor_offset + index), - response.quotient) - self.assertEqual( - (dividend_offset + index) % (divisor_offset + index), - response.remainder) - self.assertEqual(stream_length, index + 1) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py b/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py deleted file mode 100644 index 5a7d1398fd..0000000000 --- a/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Tests for grpc.framework.base.implementations.""" - -import unittest - -from grpc.framework.base import implementations -from grpc.framework.base import util -from grpc.framework.foundation import logging_pool -from grpc_test.framework.base import interfaces_test_case - -POOL_MAX_WORKERS = 10 -DEFAULT_TIMEOUT = 30 -MAXIMUM_TIMEOUT = 60 - - -class ImplementationsTest( - interfaces_test_case.FrontAndBackTest, unittest.TestCase): - - def setUp(self): - self.memory_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_work_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_work_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.test_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.test_servicer = interfaces_test_case.TestServicer(self.test_pool) - self.front = implementations.front_link( - self.front_work_pool, self.front_transmission_pool, - self.front_utility_pool) - self.back = implementations.back_link( - self.test_servicer, self.back_work_pool, self.back_transmission_pool, - self.back_utility_pool, DEFAULT_TIMEOUT, MAXIMUM_TIMEOUT) - self.front.join_rear_link(self.back) - self.back.join_fore_link(self.front) - - def tearDown(self): - util.wait_for_idle(self.back) - util.wait_for_idle(self.front) - self.memory_transmission_pool.shutdown(wait=True) - self.front_work_pool.shutdown(wait=True) - self.front_transmission_pool.shutdown(wait=True) - self.front_utility_pool.shutdown(wait=True) - self.back_work_pool.shutdown(wait=True) - self.back_transmission_pool.shutdown(wait=True) - self.back_utility_pool.shutdown(wait=True) - self.test_pool.shutdown(wait=True) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py b/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py deleted file mode 100644 index be775ad4e0..0000000000 --- a/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Abstract tests against the interfaces of the base layer of RPC Framework.""" - -import threading -import time - -from grpc.framework.base import interfaces -from grpc.framework.base import util -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_util -from grpc_test.framework.foundation import stream_testing - -TICK = 0.1 -SMALL_TIMEOUT = TICK * 50 -STREAM_LENGTH = 100 - -SYNCHRONOUS_ECHO = 'synchronous echo' -ASYNCHRONOUS_ECHO = 'asynchronous echo' -IMMEDIATE_FAILURE = 'immediate failure' -TRIGGERED_FAILURE = 'triggered failure' -WAIT_ON_CONDITION = 'wait on condition' - -EMPTY_OUTCOME_DICT = { - interfaces.Outcome.COMPLETED: 0, - interfaces.Outcome.CANCELLED: 0, - interfaces.Outcome.EXPIRED: 0, - interfaces.Outcome.RECEPTION_FAILURE: 0, - interfaces.Outcome.TRANSMISSION_FAILURE: 0, - interfaces.Outcome.SERVICER_FAILURE: 0, - interfaces.Outcome.SERVICED_FAILURE: 0, - } - - -def _synchronous_echo(output_consumer): - return stream_util.TransformingConsumer(lambda x: x, output_consumer) - - -class AsynchronousEcho(stream.Consumer): - """A stream.Consumer that echoes its input to another stream.Consumer.""" - - def __init__(self, output_consumer, pool): - self._lock = threading.Lock() - self._output_consumer = output_consumer - self._pool = pool - - self._queue = [] - self._spinning = False - - def _spin(self, value, complete): - while True: - if value: - if complete: - self._output_consumer.consume_and_terminate(value) - else: - self._output_consumer.consume(value) - elif complete: - self._output_consumer.terminate() - with self._lock: - if self._queue: - value, complete = self._queue.pop(0) - else: - self._spinning = False - return - - def consume(self, value): - with self._lock: - if self._spinning: - self._queue.append((value, False)) - else: - self._spinning = True - self._pool.submit(self._spin, value, False) - - def terminate(self): - with self._lock: - if self._spinning: - self._queue.append((None, True)) - else: - self._spinning = True - self._pool.submit(self._spin, None, True) - - def consume_and_terminate(self, value): - with self._lock: - if self._spinning: - self._queue.append((value, True)) - else: - self._spinning = True - self._pool.submit(self._spin, value, True) - - -class TestServicer(interfaces.Servicer): - """An interfaces.Servicer with instrumented for testing.""" - - def __init__(self, pool): - self._pool = pool - self.condition = threading.Condition() - self._released = False - - def service(self, name, context, output_consumer): - if name == SYNCHRONOUS_ECHO: - return _synchronous_echo(output_consumer) - elif name == ASYNCHRONOUS_ECHO: - return AsynchronousEcho(output_consumer, self._pool) - elif name == IMMEDIATE_FAILURE: - raise ValueError() - elif name == TRIGGERED_FAILURE: - raise NotImplementedError - elif name == WAIT_ON_CONDITION: - with self.condition: - while not self._released: - self.condition.wait() - return _synchronous_echo(output_consumer) - else: - raise NotImplementedError() - - def release(self): - with self.condition: - self._released = True - self.condition.notify_all() - - -class EasyServicedIngestor(interfaces.ServicedIngestor): - """A trivial implementation of interfaces.ServicedIngestor.""" - - def __init__(self, consumer): - self._consumer = consumer - - def consumer(self, operation_context): - """See interfaces.ServicedIngestor.consumer for specification.""" - return self._consumer - - -class FrontAndBackTest(object): - """A test suite usable against any joined Front and Back.""" - - # Pylint doesn't know that this is a unittest.TestCase mix-in. - # pylint: disable=invalid-name - - def testSimplestCall(self): - """Tests the absolute simplest call - a one-ticket fire-and-forget.""" - self.front.operate( - SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT, - util.none_serviced_subscription(), 'test trace ID') - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - - # Assuming nothing really pathological (such as pauses on the order of - # SMALL_TIMEOUT interfering with this test) there are a two different ways - # the back could have experienced execution up to this point: - # (1) The ticket is still either in the front waiting to be transmitted - # or is somewhere on the link between the front and the back. The back has - # no idea that this test is even happening. Calling wait_for_idle on it - # would do no good because in this case the back is idle and the call would - # return with the ticket bound for it still in the front or on the link. - back_operation_stats = self.back.operation_stats() - first_back_possibility = EMPTY_OUTCOME_DICT - # (2) The ticket arrived at the back and the back completed the operation. - second_back_possibility = dict(EMPTY_OUTCOME_DICT) - second_back_possibility[interfaces.Outcome.COMPLETED] = 1 - self.assertIn( - back_operation_stats, (first_back_possibility, second_back_possibility)) - # It's true that if the ticket had arrived at the back and the back had - # begun processing that wait_for_idle could hold test execution until the - # back completed the operation, but that doesn't really collapse the - # possibility space down to one solution. - - def testEntireEcho(self): - """Tests a very simple one-ticket-each-way round-trip.""" - test_payload = 'test payload' - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - self.front.operate( - ASYNCHRONOUS_ECHO, test_payload, True, SMALL_TIMEOUT, subscription, - 'test trace ID') - - util.wait_for_idle(self.front) - util.wait_for_idle(self.back) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertEqual( - 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertListEqual([(test_payload, True)], test_consumer.calls) - - def testBidirectionalStreamingEcho(self): - """Tests sending multiple tickets each way.""" - test_payload_template = 'test_payload: %03d' - test_payloads = [test_payload_template % i for i in range(STREAM_LENGTH)] - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - operation = self.front.operate( - SYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, - 'test trace ID') - - for test_payload in test_payloads: - operation.consumer.consume(test_payload) - operation.consumer.terminate() - - util.wait_for_idle(self.front) - util.wait_for_idle(self.back) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertEqual( - 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertListEqual(test_payloads, test_consumer.values()) - - def testCancellation(self): - """Tests cancelling a long-lived operation.""" - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - operation = self.front.operate( - ASYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, - 'test trace ID') - operation.cancel() - - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.CANCELLED]) - util.wait_for_idle(self.back) - self.assertListEqual([], test_consumer.calls) - - # Assuming nothing really pathological (such as pauses on the order of - # SMALL_TIMEOUT interfering with this test) there are a two different ways - # the back could have experienced execution up to this point: - # (1) Both tickets are still either in the front waiting to be transmitted - # or are somewhere on the link between the front and the back. The back has - # no idea that this test is even happening. Calling wait_for_idle on it - # would do no good because in this case the back is idle and the call would - # return with the tickets bound for it still in the front or on the link. - back_operation_stats = self.back.operation_stats() - first_back_possibility = EMPTY_OUTCOME_DICT - # (2) Both tickets arrived within SMALL_TIMEOUT of one another at the back. - # The back started processing based on the first ticket and then stopped - # upon receiving the cancellation ticket. - second_back_possibility = dict(EMPTY_OUTCOME_DICT) - second_back_possibility[interfaces.Outcome.CANCELLED] = 1 - self.assertIn( - back_operation_stats, (first_back_possibility, second_back_possibility)) - - def testExpiration(self): - """Tests that operations time out.""" - timeout = TICK * 2 - allowance = TICK # How much extra time to - condition = threading.Condition() - test_payload = 'test payload' - subscription = util.termination_only_serviced_subscription() - start_time = time.time() - - outcome_cell = [None] - termination_time_cell = [None] - def termination_action(outcome): - with condition: - outcome_cell[0] = outcome - termination_time_cell[0] = time.time() - condition.notify() - - with condition: - operation = self.front.operate( - SYNCHRONOUS_ECHO, test_payload, False, timeout, subscription, - 'test trace ID') - operation.context.add_termination_callback(termination_action) - while outcome_cell[0] is None: - condition.wait() - - duration = termination_time_cell[0] - start_time - self.assertLessEqual(timeout, duration) - self.assertLess(duration, timeout + allowance) - self.assertEqual(interfaces.Outcome.EXPIRED, outcome_cell[0]) - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.EXPIRED]) - util.wait_for_idle(self.back) - self.assertLessEqual( - 1, self.back.operation_stats()[interfaces.Outcome.EXPIRED]) diff --git a/src/python/grpcio_test/grpc_test/framework/face/_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/_test_case.py deleted file mode 100644 index 486b6e630e..0000000000 --- a/src/python/grpcio_test/grpc_test/framework/face/_test_case.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Common lifecycle code for in-memory-ticket-exchange Face-layer tests.""" - -from grpc.framework.face import implementations -from grpc.framework.foundation import logging_pool -from grpc_test.framework.face.testing import base_util -from grpc_test.framework.face.testing import test_case - -_TIMEOUT = 3 -_MAXIMUM_POOL_SIZE = 10 - - -class FaceTestCase(test_case.FaceTestCase): - """Provides abstract Face-layer tests an in-memory implementation.""" - - def set_up_implementation( - self, name, methods, method_implementations, - multi_method_implementation): - servicer_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - stub_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - - servicer = implementations.servicer( - servicer_pool, method_implementations, multi_method_implementation) - - linked_pair = base_util.linked_pair(servicer, _TIMEOUT) - stub = implementations.generic_stub(linked_pair.front, stub_pool) - return stub, (servicer_pool, stub_pool, linked_pair) - - def tear_down_implementation(self, memo): - servicer_pool, stub_pool, linked_pair = memo - linked_pair.shut_down() - stub_pool.shutdown(wait=True) - servicer_pool.shutdown(wait=True) diff --git a/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py deleted file mode 100644 index dca373ef7c..0000000000 --- a/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc_test.framework.face import _test_case -from grpc_test.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case - - -class EventInvocationSynchronousEventServiceTest( - _test_case.FaceTestCase, - test_case.EventInvocationSynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py deleted file mode 100644 index 99fdf18123..0000000000 --- a/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc_test.framework.face import _test_case -from grpc_test.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case - - -class FutureInvocationAsynchronousEventServiceTest( - _test_case.FaceTestCase, - test_case.FutureInvocationAsynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py b/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py deleted file mode 100644 index 47fc5822de..0000000000 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Utility for serialization in the context of test RPC services.""" - -import collections - - -class Serialization( - collections.namedtuple( - '_Serialization', - ['request_serializers', - 'request_deserializers', - 'response_serializers', - 'response_deserializers'])): - """An aggregation of serialization behaviors for an RPC service. - - Attributes: - request_serializers: A dict from method name to request object serializer - behavior. - request_deserializers: A dict from method name to request object - deserializer behavior. - response_serializers: A dict from method name to response object serializer - behavior. - response_deserializers: A dict from method name to response object - deserializer behavior. - """ - - -def serialization(methods): - """Creates a Serialization from a sequences of interfaces.Method objects.""" - request_serializers = {} - request_deserializers = {} - response_serializers = {} - response_deserializers = {} - for method in methods: - name = method.name() - request_serializers[name] = method.serialize_request - request_deserializers[name] = method.deserialize_request - response_serializers[name] = method.serialize_response - response_deserializers[name] = method.deserialize_response - return Serialization( - request_serializers, request_deserializers, response_serializers, - response_deserializers) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py index e4d2a7a0d7..46a01876d8 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py @@ -236,8 +236,8 @@ class Instruction( collections.namedtuple( 'Instruction', ('kind', 'advance_args', 'advance_kwargs', 'conclude_success', - 'conclude_message', 'conclude_invocation_outcome', - 'conclude_service_outcome',))): + 'conclude_message', 'conclude_invocation_outcome_kind', + 'conclude_service_outcome_kind',))): """""" @enum.unique @@ -532,24 +532,24 @@ class _SequenceController(Controller): self._state.service_side_outcome = outcome if self._todo is not None or self._remaining_elements: self._failed('Premature service-side outcome %s!' % (outcome,)) - elif outcome is not self._sequence.outcome.service: + elif outcome.kind is not self._sequence.outcome_kinds.service: self._failed( - 'Incorrect service-side outcome: %s should have been %s' % ( - outcome, self._sequence.outcome.service)) + 'Incorrect service-side outcome kind: %s should have been %s' % ( + outcome.kind, self._sequence.outcome_kinds.service)) elif self._state.invocation_side_outcome is not None: - self._passed(self._state.invocation_side_outcome, outcome) + self._passed(self._state.invocation_side_outcome.kind, outcome.kind) def invocation_on_termination(self, outcome): with self._condition: self._state.invocation_side_outcome = outcome if self._todo is not None or self._remaining_elements: self._failed('Premature invocation-side outcome %s!' % (outcome,)) - elif outcome is not self._sequence.outcome.invocation: + elif outcome.kind is not self._sequence.outcome_kinds.invocation: self._failed( - 'Incorrect invocation-side outcome: %s should have been %s' % ( - outcome, self._sequence.outcome.invocation)) + 'Incorrect invocation-side outcome kind: %s should have been %s' % ( + outcome.kind, self._sequence.outcome_kinds.invocation)) elif self._state.service_side_outcome is not None: - self._passed(outcome, self._state.service_side_outcome) + self._passed(outcome.kind, self._state.service_side_outcome.kind) class _SequenceControllerCreator(ControllerCreator): diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py index 1d77aaebe6..f547d91681 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py @@ -103,13 +103,14 @@ class Element(collections.namedtuple('Element', ('kind', 'transmission',))): SERVICE_FAILURE = 'service failure' -class Outcome(collections.namedtuple('Outcome', ('invocation', 'service',))): +class OutcomeKinds( + collections.namedtuple('Outcome', ('invocation', 'service',))): """A description of the expected outcome of an operation test. Attributes: - invocation: The base.Outcome value expected on the invocation side of the - operation. - service: The base.Outcome value expected on the service side of the + invocation: The base.Outcome.Kind value expected on the invocation side of + the operation. + service: The base.Outcome.Kind value expected on the service side of the operation. """ @@ -117,7 +118,8 @@ class Outcome(collections.namedtuple('Outcome', ('invocation', 'service',))): class Sequence( collections.namedtuple( 'Sequence', - ('name', 'maximum_duration', 'invocation', 'elements', 'outcome',))): + ('name', 'maximum_duration', 'invocation', 'elements', + 'outcome_kinds',))): """Describes at a high level steps to perform in a test. Attributes: @@ -128,7 +130,8 @@ class Sequence( under test. elements: A sequence of Element values describing at coarse granularity actions to take during the operation under test. - outcome: An Outcome value describing the expected outcome of the test. + outcome_kinds: An OutcomeKinds value describing the expected outcome kinds + of the test. """ _EASY = Sequence( @@ -139,7 +142,7 @@ _EASY = Sequence( Element( Element.Kind.SERVICE_TRANSMISSION, Transmission(True, True, True)), ), - Outcome(base.Outcome.COMPLETED, base.Outcome.COMPLETED)) + OutcomeKinds(base.Outcome.Kind.COMPLETED, base.Outcome.Kind.COMPLETED)) _PEASY = Sequence( 'Peasy', @@ -154,7 +157,7 @@ _PEASY = Sequence( Element( Element.Kind.SERVICE_TRANSMISSION, Transmission(False, True, True)), ), - Outcome(base.Outcome.COMPLETED, base.Outcome.COMPLETED)) + OutcomeKinds(base.Outcome.Kind.COMPLETED, base.Outcome.Kind.COMPLETED)) # TODO(issue 2959): Finish this test suite. This tuple of sequences should diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py index 87332cf612..ddda1018c3 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py @@ -44,7 +44,8 @@ from grpc_test.framework.interfaces.base import test_interfaces _SYNCHRONICITY_VARIATION = (('Sync', False), ('Async', True)) -_EMPTY_OUTCOME_DICT = {outcome: 0 for outcome in base.Outcome} +_EMPTY_OUTCOME_KIND_DICT = { + outcome_kind: 0 for outcome_kind in base.Outcome.Kind} class _Serialization(test_interfaces.Serialization): @@ -118,8 +119,19 @@ class _Operator(base.Operator): 'Deliberately raised exception from Operator.advance (in a test)!') +class _ProtocolReceiver(base.ProtocolReceiver): + + def __init__(self): + self._condition = threading.Condition() + self._contexts = [] + + def context(self, protocol_context): + with self._condition: + self._contexts.append(protocol_context) + + class _Servicer(base.Servicer): - """An base.Servicer with instrumented for testing.""" + """A base.Servicer with instrumented for testing.""" def __init__(self, group, method, controllers, pool): self._condition = threading.Condition() @@ -143,7 +155,7 @@ class _Servicer(base.Servicer): controller.service_on_termination) if outcome is not None: controller.service_on_termination(outcome) - return utilities.full_subscription(operator) + return utilities.full_subscription(operator, _ProtocolReceiver()) class _OperationTest(unittest.TestCase): @@ -168,7 +180,8 @@ class _OperationTest(unittest.TestCase): test_operator = _Operator( self._controller, self._controller.on_invocation_advance, self._pool, None) - subscription = utilities.full_subscription(test_operator) + subscription = utilities.full_subscription( + test_operator, _ProtocolReceiver()) else: # TODO(nathaniel): support and test other subscription kinds. self.fail('Non-full subscriptions not yet supported!') @@ -223,11 +236,12 @@ class _OperationTest(unittest.TestCase): self.assertTrue( instruction.conclude_success, msg=instruction.conclude_message) - expected_invocation_stats = dict(_EMPTY_OUTCOME_DICT) - expected_invocation_stats[instruction.conclude_invocation_outcome] += 1 + expected_invocation_stats = dict(_EMPTY_OUTCOME_KIND_DICT) + expected_invocation_stats[ + instruction.conclude_invocation_outcome_kind] += 1 self.assertDictEqual(expected_invocation_stats, invocation_stats) - expected_service_stats = dict(_EMPTY_OUTCOME_DICT) - expected_service_stats[instruction.conclude_service_outcome] += 1 + expected_service_stats = dict(_EMPTY_OUTCOME_KIND_DICT) + expected_service_stats[instruction.conclude_service_outcome_kind] += 1 self.assertDictEqual(expected_service_stats, service_stats) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py index 272a37f15f..3032736975 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py @@ -72,6 +72,36 @@ class _PauseableIterator(object): return next(self._upstream) +class _Callback(object): + + def __init__(self): + self._condition = threading.Condition() + self._called = False + self._passed_future = None + self._passed_other_stuff = None + + def __call__(self, *args, **kwargs): + with self._condition: + self._called = True + if args: + self._passed_future = args[0] + if 1 < len(args) or kwargs: + self._passed_other_stuff = tuple(args[1:]), dict(kwargs) + self._condition.notify_all() + + def future(self): + with self._condition: + while True: + if self._passed_other_stuff is not None: + raise ValueError( + 'Test callback passed unexpected values: %s', + self._passed_other_stuff) + elif self._called: + return self._passed_future + else: + self._condition.wait() + + class TestCase(test_coverage.Coverage, unittest.TestCase): """A test of the Face layer of RPC Framework. @@ -112,12 +142,15 @@ class TestCase(test_coverage.Coverage, unittest.TestCase): self._digest.unary_unary_messages_sequences.iteritems()): for test_messages in test_messages_sequence: request = test_messages.request() + callback = _Callback() response_future = self._invoker.future(group, method)( request, test_constants.LONG_TIMEOUT) + response_future.add_done_callback(callback) response = response_future.result() test_messages.verify(request, response, self) + self.assertIs(callback.future(), response_future) def testSuccessfulUnaryRequestStreamResponse(self): for (group, method), test_messages_sequence in ( @@ -137,15 +170,19 @@ class TestCase(test_coverage.Coverage, unittest.TestCase): for test_messages in test_messages_sequence: requests = test_messages.requests() request_iterator = _PauseableIterator(iter(requests)) + callback = _Callback() # Use of a paused iterator of requests allows us to test that control is # returned to calling code before the iterator yields any requests. with request_iterator.pause(): response_future = self._invoker.future(group, method)( request_iterator, test_constants.LONG_TIMEOUT) - response = response_future.result() + response_future.add_done_callback(callback) + future_passed_to_callback = callback.future() + response = future_passed_to_callback.result() test_messages.verify(requests, response, self) + self.assertIs(future_passed_to_callback, response_future) def testSuccessfulStreamRequestStreamResponse(self): for (group, method), test_messages_sequence in ( @@ -208,12 +245,15 @@ class TestCase(test_coverage.Coverage, unittest.TestCase): self._digest.unary_unary_messages_sequences.iteritems()): for test_messages in test_messages_sequence: request = test_messages.request() + callback = _Callback() with self._control.pause(): response_future = self._invoker.future(group, method)( request, test_constants.LONG_TIMEOUT) + response_future.add_done_callback(callback) cancel_method_return_value = response_future.cancel() + self.assertIs(callback.future(), response_future) self.assertFalse(cancel_method_return_value) self.assertTrue(response_future.cancelled()) @@ -236,12 +276,15 @@ class TestCase(test_coverage.Coverage, unittest.TestCase): self._digest.stream_unary_messages_sequences.iteritems()): for test_messages in test_messages_sequence: requests = test_messages.requests() + callback = _Callback() with self._control.pause(): response_future = self._invoker.future(group, method)( iter(requests), test_constants.LONG_TIMEOUT) + response_future.add_done_callback(callback) cancel_method_return_value = response_future.cancel() + self.assertIs(callback.future(), response_future) self.assertFalse(cancel_method_return_value) self.assertTrue(response_future.cancelled()) @@ -264,10 +307,13 @@ class TestCase(test_coverage.Coverage, unittest.TestCase): self._digest.unary_unary_messages_sequences.iteritems()): for test_messages in test_messages_sequence: request = test_messages.request() + callback = _Callback() with self._control.pause(): response_future = self._invoker.future( group, method)(request, _3069_test_constant.REALLY_SHORT_TIMEOUT) + response_future.add_done_callback(callback) + self.assertIs(callback.future(), response_future) self.assertIsInstance( response_future.exception(), face.ExpirationError) with self.assertRaises(face.ExpirationError): @@ -290,10 +336,13 @@ class TestCase(test_coverage.Coverage, unittest.TestCase): self._digest.stream_unary_messages_sequences.iteritems()): for test_messages in test_messages_sequence: requests = test_messages.requests() + callback = _Callback() with self._control.pause(): response_future = self._invoker.future(group, method)( iter(requests), _3069_test_constant.REALLY_SHORT_TIMEOUT) + response_future.add_done_callback(callback) + self.assertIs(callback.future(), response_future) self.assertIsInstance( response_future.exception(), face.ExpirationError) with self.assertRaises(face.ExpirationError): @@ -316,11 +365,14 @@ class TestCase(test_coverage.Coverage, unittest.TestCase): self._digest.unary_unary_messages_sequences.iteritems()): for test_messages in test_messages_sequence: request = test_messages.request() + callback = _Callback() with self._control.fail(): response_future = self._invoker.future(group, method)( request, _3069_test_constant.REALLY_SHORT_TIMEOUT) + response_future.add_done_callback(callback) + self.assertIs(callback.future(), response_future) # Because the servicer fails outside of the thread from which the # servicer-side runtime called into it its failure is # indistinguishable from simply not having called its @@ -350,11 +402,14 @@ class TestCase(test_coverage.Coverage, unittest.TestCase): self._digest.stream_unary_messages_sequences.iteritems()): for test_messages in test_messages_sequence: requests = test_messages.requests() + callback = _Callback() with self._control.fail(): response_future = self._invoker.future(group, method)( iter(requests), _3069_test_constant.REALLY_SHORT_TIMEOUT) + response_future.add_done_callback(callback) + self.assertIs(callback.future(), response_future) # Because the servicer fails outside of the thread from which the # servicer-side runtime called into it its failure is # indistinguishable from simply not having called its diff --git a/src/python/grpcio_test/grpc_test/test_common.py b/src/python/grpcio_test/grpc_test/test_common.py index 44284be88b..29431bfb9d 100644 --- a/src/python/grpcio_test/grpc_test/test_common.py +++ b/src/python/grpcio_test/grpc_test/test_common.py @@ -46,19 +46,23 @@ def metadata_transmitted(original_metadata, transmitted_metadata): the same key. Args: - original_metadata: A metadata value used in a test of gRPC. + original_metadata: A metadata value used in a test of gRPC. An iterable over + iterables of length 2. transmitted_metadata: A metadata value corresponding to original_metadata - after having been transmitted via gRPC. + after having been transmitted via gRPC. An iterable over iterables of + length 2. Returns: A boolean indicating whether transmitted_metadata accurately reflects original_metadata after having been transmitted via gRPC. """ original = collections.defaultdict(list) - for key, value in original_metadata: + for key_value_pair in original_metadata: + key, value = tuple(key_value_pair) original[key].append(value) transmitted = collections.defaultdict(list) - for key, value in transmitted_metadata: + for key_value_pair in transmitted_metadata: + key, value = tuple(key_value_pair) transmitted[key].append(value) for key, values in original.iteritems(): diff --git a/src/python/grpcio_test/requirements.txt b/src/python/grpcio_test/requirements.txt index 856198def5..fea80ca07f 100644 --- a/src/python/grpcio_test/requirements.txt +++ b/src/python/grpcio_test/requirements.txt @@ -1,5 +1,6 @@ +grpcio>=0.11.0b0 +oauth2client>=1.4.7 +protobuf>=3.0.0a3 pytest>=2.6 pytest-cov>=2.0 pytest-xdist>=1.11 -oauth2client>=1.4.7 -grpcio>=0.10.0a0 diff --git a/src/python/grpcio_test/setup.cfg b/src/python/grpcio_test/setup.cfg index b32d3f5972..3be93cb918 100644 --- a/src/python/grpcio_test/setup.cfg +++ b/src/python/grpcio_test/setup.cfg @@ -1,3 +1,2 @@ [pytest] -norecursedirs = _cython python_files = *_test.py diff --git a/src/python/grpcio_test/setup.py b/src/python/grpcio_test/setup.py index 802dd1e53a..e9ee45a92a 100644 --- a/src/python/grpcio_test/setup.py +++ b/src/python/grpcio_test/setup.py @@ -40,7 +40,7 @@ os.chdir(os.path.dirname(os.path.abspath(__file__))) # Break import-style to ensure we can actually find our commands module. import commands -_PACKAGES = setuptools.find_packages('.', exclude=['*._cython', '*._cython.*']) +_PACKAGES = setuptools.find_packages('.') _PACKAGE_DIRECTORIES = { '': '.', @@ -71,16 +71,20 @@ _SETUP_REQUIRES = ( _INSTALL_REQUIRES = ( 'oauth2client>=1.4.7', - 'grpcio>=0.10.0a0', + 'grpcio>=0.11.0b0', + # TODO(issue 3321): Unpin protobuf dependency. + 'protobuf==3.0.0a3', ) _COMMAND_CLASS = { - 'test': commands.RunTests + 'test': commands.RunTests, + 'build_proto_modules': commands.BuildProtoModules, + 'build_py': commands.BuildPy, } setuptools.setup( name='grpcio_test', - version='0.10.0a0', + version='0.11.0b0', packages=_PACKAGES, package_dir=_PACKAGE_DIRECTORIES, package_data=_PACKAGE_DATA, diff --git a/src/ruby/.rubocop.yml b/src/ruby/.rubocop.yml index 312bdca384..dd57ab6082 100644 --- a/src/ruby/.rubocop.yml +++ b/src/ruby/.rubocop.yml @@ -9,3 +9,9 @@ AllCops: - 'bin/math_services.rb' - 'pb/grpc/health/v1alpha/*' - 'pb/test/**/*' + +Metrics/CyclomaticComplexity: + Max: 8 + +Metrics/PerceivedComplexity: + Max: 8 diff --git a/src/ruby/README.md b/src/ruby/README.md index 7f75c0e313..76d9f7663a 100644 --- a/src/ruby/README.md +++ b/src/ruby/README.md @@ -1,3 +1,4 @@ +[![Gem](https://img.shields.io/gem/v/grpc.svg)](https://rubygems.org/gems/grpc/) gRPC Ruby ========= @@ -6,7 +7,7 @@ A Ruby implementation of gRPC. Status ------ -Alpha : Ready for early adopters +Beta PREREQUISITES ------------- @@ -19,10 +20,10 @@ INSTALLATION **Linux (Debian):** -Add [Debian testing][] to your `sources.list` file. Example: +Add [Debian jessie-backports][] to your `sources.list` file. Example: ```sh -echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" | \ +echo "deb http://http.debian.net/debian jessie-backports main" | \ sudo tee -a /etc/apt/sources.list ``` @@ -99,4 +100,4 @@ Directory structure is the layout for [ruby extensions][] [ruby extensions]:http://guides.rubygems.org/gems-with-extensions/ [rubydoc]: http://www.rubydoc.info/gems/grpc [grpc.io]: http://www.grpc.io/docs/installation/ruby.html -[Debian testing]:https://www.debian.org/releases/stretch/ +[Debian jessie-backports]:http://backports.debian.org/Instructions/ diff --git a/src/ruby/bin/math_server.rb b/src/ruby/bin/math_server.rb index 562f197317..1ee4c5632d 100755 --- a/src/ruby/bin/math_server.rb +++ b/src/ruby/bin/math_server.rb @@ -195,7 +195,7 @@ def main s.add_http2_port(options['host'], test_server_creds) GRPC.logger.info("... running securely on #{options['host']}") else - s.add_http2_port(options['host']) + s.add_http2_port(options['host'], :this_port_is_insecure) GRPC.logger.info("... running insecurely on #{options['host']}") end diff --git a/src/ruby/bin/noproto_server.rb b/src/ruby/bin/noproto_server.rb index 72a5762040..66554e26f6 100755 --- a/src/ruby/bin/noproto_server.rb +++ b/src/ruby/bin/noproto_server.rb @@ -101,7 +101,7 @@ def main s.add_http2_port(options['host'], test_server_creds) GRPC.logger.info("... running securely on #{options['host']}") else - s.add_http2_port(options['host']) + s.add_http2_port(options['host'], :this_port_is_insecure) GRPC.logger.info("... running insecurely on #{options['host']}") end diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec index 093606bb8b..61cf18cf54 100755 --- a/src/ruby/grpc.gemspec +++ b/src/ruby/grpc.gemspec @@ -17,9 +17,10 @@ Gem::Specification.new do |s| s.requirements << 'libgrpc ~> 0.11.0 needs to be installed' s.files = %w( Rakefile ) - s.files += Dir.glob('lib/**/*') - s.files += Dir.glob('ext/**/*') s.files += Dir.glob('bin/**/*') + s.files += Dir.glob('ext/**/*') + s.files += Dir.glob('lib/**/*') + s.files += Dir.glob('pb/**/*') s.test_files = Dir.glob('spec/**/*') %w(math noproto).each do |b| s.executables += ["#{b}_client.rb", "#{b}_server.rb"] diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb index 24ec1793f6..b8e33ad295 100644 --- a/src/ruby/lib/grpc/generic/client_stub.rb +++ b/src/ruby/lib/grpc/generic/client_stub.rb @@ -170,14 +170,13 @@ module GRPC deadline: nil, timeout: nil, return_op: false, - parent: parent, + parent: nil, **kw) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, parent: parent) - kw_with_jwt_uri = self.class.update_with_jwt_aud_uri(kw, @host, method) - md = @update_metadata.nil? ? kw : @update_metadata.call(kw_with_jwt_uri) + md = update_metadata(kw, method) return c.request_response(req, **md) unless return_op # return the operation view of the active_call; define #execute as a @@ -244,8 +243,7 @@ module GRPC deadline: deadline, timeout: timeout, parent: parent) - kw_with_jwt_uri = self.class.update_with_jwt_aud_uri(kw, @host, method) - md = @update_metadata.nil? ? kw : @update_metadata.call(kw_with_jwt_uri) + md = update_metadata(kw, method) return c.client_streamer(requests, **md) unless return_op # return the operation view of the active_call; define #execute as a @@ -322,8 +320,7 @@ module GRPC deadline: deadline, timeout: timeout, parent: parent) - kw_with_jwt_uri = self.class.update_with_jwt_aud_uri(kw, @host, method) - md = @update_metadata.nil? ? kw : @update_metadata.call(kw_with_jwt_uri) + md = update_metadata(kw, method) return c.server_streamer(req, **md, &blk) unless return_op # return the operation view of the active_call; define #execute @@ -439,8 +436,7 @@ module GRPC deadline: deadline, timeout: timeout, parent: parent) - kw_with_jwt_uri = self.class.update_with_jwt_aud_uri(kw, @host, method) - md = @update_metadata.nil? ? kw : @update_metadata.call(kw_with_jwt_uri) + md = update_metadata(kw, method) return c.bidi_streamer(requests, **md, &blk) unless return_op # return the operation view of the active_call; define #execute @@ -454,6 +450,16 @@ module GRPC private + def update_metadata(kw, method) + return kw if @update_metadata.nil? + just_jwt_uri = self.class.update_with_jwt_aud_uri({}, @host, method) + updated = @update_metadata.call(just_jwt_uri) + + # keys should be lowercase + updated = Hash[updated.each_pair.map { |k, v| [k.downcase, v] }] + kw.merge(updated) + end + # Creates a new active stub # # @param method [string] the method being called. diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb index 3740ac52da..228c500672 100644 --- a/src/ruby/lib/grpc/generic/rpc_server.rb +++ b/src/ruby/lib/grpc/generic/rpc_server.rb @@ -416,6 +416,8 @@ module GRPC until stopped? begin an_rpc = @server.request_call(@cq, loop_tag, INFINITE_FUTURE) + break if (!an_rpc.nil?) && an_rpc.call.nil? + c = new_active_server_call(an_rpc) unless c.nil? mth = an_rpc.method.to_sym diff --git a/src/ruby/lib/grpc/version.rb b/src/ruby/lib/grpc/version.rb index 108c22b25e..9acf7a055a 100644 --- a/src/ruby/lib/grpc/version.rb +++ b/src/ruby/lib/grpc/version.rb @@ -29,5 +29,5 @@ # GRPC contains the General RPC module. module GRPC - VERSION = '0.11.0' + VERSION = '0.11.1' end diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb index 164e304b4d..1388685734 100755 --- a/src/ruby/pb/test/client.rb +++ b/src/ruby/pb/test/client.rb @@ -255,6 +255,12 @@ class NamedTests def per_rpc_creds auth_creds = Google::Auth.get_application_default(@args.oauth_scope) kw = auth_creds.updater_proc.call({}) + + # TODO(jtattermusch): downcase the metadata keys here to make sure + # they are not rejected by C core. This is a hotfix that should + # be addressed by introducing auto-downcasing logic. + kw = Hash[ kw.each_pair.map { |k, v| [k.downcase, v] }] + resp = perform_large_unary(fill_username: true, fill_oauth_scope: true, **kw) @@ -424,12 +430,13 @@ def parse_args test_case_list = test_cases.join(',') opts.on('--test_case CODE', test_cases, {}, 'select a test_case', " (#{test_case_list})") { |v| args['test_case'] = v } - opts.on('-s', '--use_tls', 'require a secure connection?') do |v| - args['secure'] = v + opts.on('--use_tls USE_TLS', ['false', 'true'], + 'require a secure connection?') do |v| + args['secure'] = v == 'true' end - opts.on('-t', '--use_test_ca', + opts.on('--use_test_ca USE_TEST_CA', ['false', 'true'], 'if secure, use the test certificate?') do |v| - args['use_test_ca'] = v + args['use_test_ca'] = v == 'true' end end.parse! _check_args(args) diff --git a/src/ruby/pb/test/server.rb b/src/ruby/pb/test/server.rb index a311bb76e6..25c1b1e9e6 100755 --- a/src/ruby/pb/test/server.rb +++ b/src/ruby/pb/test/server.rb @@ -168,8 +168,9 @@ def parse_options opts.on('--port PORT', 'server port') do |v| options['port'] = v end - opts.on('-s', '--use_tls', 'require a secure connection?') do |v| - options['secure'] = v + opts.on('--use_tls USE_TLS', ['false', 'true'], + 'require a secure connection?') do |v| + options['secure'] = v == 'true' end end.parse! @@ -187,7 +188,7 @@ def main s.add_http2_port(host, test_server_creds) GRPC.logger.info("... running securely on #{host}") else - s.add_http2_port(host) + s.add_http2_port(host, :this_port_is_insecure) GRPC.logger.info("... running insecurely on #{host}") end s.handle(TestTarget) diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb index a05433df75..c5173aee1d 100644 --- a/src/ruby/spec/generic/client_stub_spec.rb +++ b/src/ruby/spec/generic/client_stub_spec.rb @@ -159,6 +159,20 @@ describe 'ClientStub' do th.join end + it 'should downcase the keys provided by the metadata updater' do + server_port = create_test_server + host = "localhost:#{server_port}" + th = run_request_response(@sent_msg, @resp, @pass, + k1: 'downcased-key-v1', k2: 'v2') + update_md = proc do |md| + md[:K1] = 'downcased-key-v1' + md + end + stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) + expect(get_response(stub)).to eq(@resp) + th.join + end + it 'should send a request when configured using an override channel' do server_port = create_test_server alt_host = "localhost:#{server_port}" diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb index e484a9ea50..efe07f734e 100644 --- a/src/ruby/spec/generic/rpc_server_spec.rb +++ b/src/ruby/spec/generic/rpc_server_spec.rb @@ -405,22 +405,6 @@ describe GRPC::RpcServer do t.join end - it 'should not receive metadata if the client times out', server: true do - service = SlowService.new - @srv.handle(service) - t = Thread.new { @srv.run } - @srv.wait_till_running - req = EchoMsg.new - stub = SlowStub.new(@host, **client_opts) - timeout = 0.1 # too short for SlowService to respond - blk = proc { stub.an_rpc(req, timeout: timeout, k1: 'v1', k2: 'v2') } - expect(&blk).to raise_error GRPC::BadStatus - wanted_md = [] - expect(service.received_md).to eq(wanted_md) - @srv.stop - t.join - end - it 'should handle cancellation correctly', server: true do service = SlowService.new @srv.handle(service) |