pidl:NDR/Client: avoid useless memcpy()
[samba.git] / pidl / lib / Parse / Pidl / Samba4 / NDR / Client.pm
index 4805934346dee9cd798c474706bbfd22a7ee08b4..b4954ca62410438a04e8b5e7d8c6e958cedd7995 100644 (file)
@@ -12,6 +12,7 @@ use Exporter;
 
 use Parse::Pidl qw(fatal warning error);
 use Parse::Pidl::Util qw(has_property ParseExpr);
+use Parse::Pidl::NDR qw(ContainsPipe);
 use Parse::Pidl::Typelist qw(mapTypeName);
 use Parse::Pidl::Samba4 qw(choose_header is_intree DeclLong);
 use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv);
@@ -44,6 +45,17 @@ sub new($)
        bless($self, $class);
 }
 
+sub ParseFunctionHasPipes($$)
+{
+       my ($self, $fn) = @_;
+
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               return 1 if ContainsPipe($e, $e->{LEVELS}[0]);
+       }
+
+       return 0;
+}
+
 sub ParseFunction_r_State($$$$)
 {
        my ($self, $if, $fn, $name) = @_;
@@ -89,10 +101,11 @@ sub ParseFunction_r_Send($$$$)
        $self->pidl("");
 
        my $out_params = 0;
-       foreach (@{$fn->{ELEMENTS}}) {
-               if (grep(/out/, @{$_->{DIRECTION}})) {
-                       $out_params++;
-               }
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/out/, @{$e->{DIRECTION}});
+               next if ContainsPipe($e, $e->{LEVELS}[0]);
+               $out_params++;
+
        }
 
        my $submem;
@@ -103,12 +116,12 @@ sub ParseFunction_r_Send($$$$)
                $self->pidl("return tevent_req_post(req, ev);");
                $self->deindent;
                $self->pidl("}");
-               $self->pidl("");
                $submem = "state->out_mem_ctx";
        } else {
                $self->pidl("state->out_mem_ctx = NULL;");
                $submem = "state";
        }
+       $self->pidl("");
 
        $self->pidl("subreq = dcerpc_binding_handle_call_send(state, ev, h,");
        $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
@@ -145,9 +158,9 @@ sub ParseFunction_r_Done($$$$)
        $self->pidl("");
 
        $self->pidl("status = dcerpc_binding_handle_call_recv(subreq);");
-       $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
+       $self->pidl("TALLOC_FREE(subreq);");
+       $self->pidl("if (tevent_req_nterror(req, status)) {");
        $self->indent;
-       $self->pidl("tevent_req_nterror(req, status);");
        $self->pidl("return;");
        $self->deindent;
        $self->pidl("}");
@@ -161,7 +174,6 @@ sub ParseFunction_r_Done($$$$)
 
 sub ParseFunction_r_Recv($$$$)
 {
-       my ($if, $fn, $name) = @_;
        my ($self, $if, $fn, $name) = @_;
        my $uname = uc $name;
 
@@ -198,10 +210,20 @@ sub ParseFunction_r_Recv($$$$)
 
 sub ParseFunction_r_Sync($$$$)
 {
-       my ($if, $fn, $name) = @_;
        my ($self, $if, $fn, $name) = @_;
        my $uname = uc $name;
 
+       if ($self->ParseFunctionHasPipes($fn)) {
+               $self->pidl_both("/*");
+               $self->pidl_both(" * The following function is skipped because");
+               $self->pidl_both(" * it uses pipes:");
+               $self->pidl_both(" *");
+               $self->pidl_both(" * dcerpc_$name\_r()");
+               $self->pidl_both(" */");
+               $self->pidl_both("");
+               return;
+       }
+
        my $proto = "NTSTATUS dcerpc_$name\_r(struct dcerpc_binding_handle *h, TALLOC_CTX *mem_ctx, struct $name *r)";
 
        $self->fn_declare($proto);
@@ -251,15 +273,15 @@ sub HeaderProperties($$)
        }
 }
 
-sub ParseCopyInArgument($$$$$$)
+sub ParseCopyArgument($$$$$)
 {
-       my ($self, $fn, $e, $r, $i, $invalid_response_type) = @_;
+       my ($self, $fn, $e, $r, $i) = @_;
        my $l = $e->{LEVELS}[0];
 
        if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED} == 1) {
-               $self->pidl("memcpy(${r}in.$e->{NAME}, ${i}$e->{NAME}, sizeof(${r}in.$e->{NAME}));");
+               $self->pidl("memcpy(${r}$e->{NAME}, ${i}$e->{NAME}, sizeof(${r}$e->{NAME}));");
        } else {
-               $self->pidl("${r}in.$e->{NAME} = ${i}$e->{NAME};");
+               $self->pidl("${r}$e->{NAME} = ${i}$e->{NAME};");
        }
 }
 
@@ -303,35 +325,94 @@ sub ParseOutputArgument($$$$$$)
                my $in_env = GenerateFunctionInEnv($fn, $r);
                my $out_env = GenerateFunctionOutEnv($fn, $r);
                my $l = $e->{LEVELS}[$level];
-               unless (defined($l->{SIZE_IS})) {
-                       fatal($e->{ORIGINAL}, "no size known for [out] array `$e->{NAME}'");
+
+               my $in_var = undef;
+               if (grep(/in/, @{$e->{DIRECTION}})) {
+                       $in_var = ParseExpr($e->{NAME}, $in_env, $e->{ORIGINAL});
+               }
+               my $out_var = ParseExpr($e->{NAME}, $out_env, $e->{ORIGINAL});
+
+               my $in_size_is = undef;
+               my $out_size_is = undef;
+               my $out_length_is = undef;
+
+               my $avail_len = undef;
+               my $needed_len = undef;
+
+               $self->pidl("{");
+               $self->indent;
+               my $copy_len_var = "_copy_len_$e->{NAME}";
+               $self->pidl("size_t $copy_len_var;");
+
+               if (not defined($l->{SIZE_IS})) {
+                       if (not $l->{IS_ZERO_TERMINATED}) {
+                               fatal($e->{ORIGINAL}, "no size known for [out] array `$e->{NAME}'");
+                       }
+                       if (has_property($e, "charset")) {
+                               $avail_len = "ndr_charset_length($in_var, CH_UNIX)";
+                               $needed_len = "ndr_charset_length($out_var, CH_UNIX)";
+                       } else {
+                               $avail_len = "ndr_string_length($in_var, sizeof(*$in_var))";
+                               $needed_len = "ndr_string_length($out_var, sizeof(*$out_var))";
+                       }
+                       $in_size_is = "";
+                       $out_size_is = "";
+                       $out_length_is = "";
                } else {
-                       my $in_size_is = ParseExpr($l->{SIZE_IS}, $in_env, $e->{ORIGINAL});
-                       my $out_size_is = ParseExpr($l->{SIZE_IS}, $out_env, $e->{ORIGINAL});
-                       my $out_length_is = $out_size_is;
+                       $in_size_is = ParseExpr($l->{SIZE_IS}, $in_env, $e->{ORIGINAL});
+                       $out_size_is = ParseExpr($l->{SIZE_IS}, $out_env, $e->{ORIGINAL});
+                       $out_length_is = $out_size_is;
                        if (defined($l->{LENGTH_IS})) {
                                $out_length_is = ParseExpr($l->{LENGTH_IS}, $out_env, $e->{ORIGINAL});
                        }
-                       if ($out_size_is ne $in_size_is) {
-                               $self->pidl("if (($out_size_is) > ($in_size_is)) {");
-                               $self->indent;
-                               $self->ParseInvalidResponse($invalid_response_type);
-                               $self->deindent;
-                               $self->pidl("}");
-                       }
-                       if ($out_length_is ne $out_size_is) {
-                               $self->pidl("if (($out_length_is) > ($out_size_is)) {");
-                               $self->indent;
-                               $self->ParseInvalidResponse($invalid_response_type);
-                               $self->deindent;
-                               $self->pidl("}");
-                       }
                        if (has_property($e, "charset")) {
-                               $self->pidl("memcpy(discard_const_p(uint8_t *, $o$e->{NAME}), ${r}out.$e->{NAME}, ($out_length_is) * sizeof(*$o$e->{NAME}));");
-                       } else {
-                               $self->pidl("memcpy($o$e->{NAME}, ${r}out.$e->{NAME}, ($out_length_is) * sizeof(*$o$e->{NAME}));");
+                               if (defined($in_var)) {
+                                       $avail_len = "ndr_charset_length($in_var, CH_UNIX)";
+                               } else {
+                                       $avail_len = $out_length_is;
+                               }
+                               $needed_len = "ndr_charset_length($out_var, CH_UNIX)";
                        }
                }
+
+               if ($out_size_is ne $in_size_is) {
+                       $self->pidl("if (($out_size_is) > ($in_size_is)) {");
+                       $self->indent;
+                       $self->ParseInvalidResponse($invalid_response_type);
+                       $self->deindent;
+                       $self->pidl("}");
+               }
+               if ($out_length_is ne $out_size_is) {
+                       $self->pidl("if (($out_length_is) > ($out_size_is)) {");
+                       $self->indent;
+                       $self->ParseInvalidResponse($invalid_response_type);
+                       $self->deindent;
+                       $self->pidl("}");
+               }
+               if (defined($needed_len)) {
+                       $self->pidl("$copy_len_var = $needed_len;");
+                       $self->pidl("if ($copy_len_var > $avail_len) {");
+                       $self->indent;
+                       $self->ParseInvalidResponse($invalid_response_type);
+                       $self->deindent;
+                       $self->pidl("}");
+               } else {
+                       $self->pidl("$copy_len_var = $out_length_is;");
+               }
+
+               my $dest_ptr = "$o$e->{NAME}";
+               my $elem_size = "sizeof(*$dest_ptr)";
+               $self->pidl("if ($dest_ptr != $out_var) {");
+               $self->indent;
+               if (has_property($e, "charset")) {
+                       $dest_ptr = "discard_const_p(uint8_t *, $dest_ptr)";
+               }
+               $self->pidl("memcpy($dest_ptr, $out_var, $copy_len_var * $elem_size);");
+               $self->deindent;
+               $self->pidl("}");
+
+               $self->deindent;
+               $self->pidl("}");
        } else {
                $self->pidl("*$o$e->{NAME} = *${r}out.$e->{NAME};");
        }
@@ -405,17 +486,20 @@ sub ParseFunction_Send($$$$)
        foreach my $e (@{$fn->{ELEMENTS}}) {
                next unless (grep(/in/, @{$e->{DIRECTION}}));
 
-               $self->ParseCopyInArgument($fn, $e, "state->orig.", "_", "async");
+               $self->ParseCopyArgument($fn, $e, "state->orig.in.", "_");
        }
        $self->pidl("");
 
        my $out_params = 0;
        $self->pidl("/* Out parameters */");
-       foreach (@{$fn->{ELEMENTS}}) {
-               if (grep(/out/, @{$_->{DIRECTION}})) {
-                       $self->pidl("state->orig.out.$_->{NAME} = _$_->{NAME};");
-                       $out_params++;
-               }
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/out/, @{$e->{DIRECTION}});
+
+               $self->ParseCopyArgument($fn, $e, "state->orig.out.", "_");
+
+               next if ContainsPipe($e, $e->{LEVELS}[0]);
+
+               $out_params++;
        }
        $self->pidl("");
 
@@ -484,9 +568,8 @@ sub ParseFunction_Done($$$$)
 
        $self->pidl("status = dcerpc_$name\_r_recv(subreq, mem_ctx);");
        $self->pidl("TALLOC_FREE(subreq);");
-       $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
+       $self->pidl("if (tevent_req_nterror(req, status)) {");
        $self->indent;
-       $self->pidl("tevent_req_nterror(req, status);");
        $self->pidl("return;");
        $self->deindent;
        $self->pidl("}");
@@ -494,6 +577,7 @@ sub ParseFunction_Done($$$$)
 
        $self->pidl("/* Copy out parameters */");
        foreach my $e (@{$fn->{ELEMENTS}}) {
+               next if ContainsPipe($e, $e->{LEVELS}[0]);
                next unless (grep(/out/, @{$e->{DIRECTION}}));
 
                $self->ParseOutputArgument($fn, $e,
@@ -549,7 +633,7 @@ sub ParseFunction_Recv($$$$)
        $self->pidl("}");
        $self->pidl("");
 
-       $self->pidl("/* Steal possbile out parameters to the callers context */");
+       $self->pidl("/* Steal possible out parameters to the callers context */");
        $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);");
        $self->pidl("");
 
@@ -570,6 +654,18 @@ sub ParseFunction_Sync($$$$)
 {
        my ($self, $if, $fn, $name) = @_;
 
+       if ($self->ParseFunctionHasPipes($fn)) {
+               $self->pidl_both("/*");
+               $self->pidl_both(" * The following function is skipped because");
+               $self->pidl_both(" * it uses pipes:");
+               $self->pidl_both(" *");
+               $self->pidl_both(" * dcerpc_$name()");
+               $self->pidl_both(" */");
+               $self->pidl_both("");
+               return;
+       }
+
+       my $uname = uc $name;
        my $fn_args = "";
        my $fn_str = "NTSTATUS dcerpc_$name";
        my $pad = genpad($fn_str);
@@ -592,15 +688,29 @@ sub ParseFunction_Sync($$$$)
        $self->pidl("struct $name r;");
        $self->pidl("NTSTATUS status;");
        $self->pidl("");
-       $self->pidl("/* In parameters */");
 
+       $self->pidl("/* In parameters */");
        foreach my $e (@{$fn->{ELEMENTS}}) {
                next unless (grep(/in/, @{$e->{DIRECTION}}));
 
-               $self->ParseCopyInArgument($fn, $e, "r.", "_", "sync");
+               $self->ParseCopyArgument($fn, $e, "r.in.", "_");
        }
+       $self->pidl("");
+
+       $self->pidl("/* Out parameters */");
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/out/, @{$e->{DIRECTION}});
 
+               $self->ParseCopyArgument($fn, $e, "r.out.", "_");
+       }
        $self->pidl("");
+
+       if (defined($fn->{RETURN_TYPE})) {
+               $self->pidl("/* Result */");
+               $self->pidl("ZERO_STRUCT(r.out.result);");
+               $self->pidl("");
+       }
+
        $self->pidl("status = dcerpc_$name\_r(h, mem_ctx, &r);");
        $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
        $self->indent;
@@ -611,6 +721,7 @@ sub ParseFunction_Sync($$$$)
 
        $self->pidl("/* Return variables */");
        foreach my $e (@{$fn->{ELEMENTS}}) {
+               next if ContainsPipe($e, $e->{LEVELS}[0]);
                next unless (grep(/out/, @{$e->{DIRECTION}}));
 
                $self->ParseOutputArgument($fn, $e, "r.", "_", "sync");
@@ -636,6 +747,24 @@ sub ParseFunction($$$)
 {
        my ($self, $if, $fn) = @_;
 
+       if ($self->ParseFunctionHasPipes($fn)) {
+               $self->pidl_both("/*");
+               $self->pidl_both(" * The following function is skipped because");
+               $self->pidl_both(" * it uses pipes:");
+               $self->pidl_both(" *");
+               $self->pidl_both(" * dcerpc_$fn->{NAME}_r_send()");
+               $self->pidl_both(" * dcerpc_$fn->{NAME}_r_recv()");
+               $self->pidl_both(" * dcerpc_$fn->{NAME}_r()");
+               $self->pidl_both(" *");
+               $self->pidl_both(" * dcerpc_$fn->{NAME}_send()");
+               $self->pidl_both(" * dcerpc_$fn->{NAME}_recv()");
+               $self->pidl_both(" * dcerpc_$fn->{NAME}()");
+               $self->pidl_both(" */");
+               $self->pidl_both("");
+               warning($fn->{ORIGINAL}, "$fn->{NAME}: dcerpc client does not support pipe yet");
+               return;
+       }
+
        $self->ParseFunction_r_State($if, $fn, $fn->{NAME});
        $self->ParseFunction_r_Send($if, $fn, $fn->{NAME});
        $self->ParseFunction_r_Done($if, $fn, $fn->{NAME});
@@ -652,6 +781,9 @@ sub ParseFunction($$$)
                        if ($e->{LEVELS}[1]->{TYPE} eq "DATA" and
                            $e->{LEVELS}[1]->{DATA_TYPE} eq "string") {
                                $reason = "is a pointer to type 'string'";
+                       } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and
+                                $e->{LEVELS}[1]->{IS_ZERO_TERMINATED}) {
+                               next;
                        } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and
                                 not defined($e->{LEVELS}[1]->{SIZE_IS})) {
                                $reason = "is a pointer to an unsized array";
@@ -661,7 +793,7 @@ sub ParseFunction($$$)
                }
                if ($e->{LEVELS}[0]->{TYPE} eq "ARRAY") {
                        if (not defined($e->{LEVELS}[0]->{SIZE_IS})) {
-                               $reason = "is a pointer to an unsized array";
+                               $reason = "is an unsized array";
                        } else {
                                next;
                        }
@@ -697,6 +829,7 @@ my %done;
 sub ParseInterface($$)
 {
        my ($self, $if) = @_;
+       my $ifu = uc($if->{NAME});
 
        $self->pidl_hdr("#ifndef _HEADER_RPC_$if->{NAME}");
        $self->pidl_hdr("#define _HEADER_RPC_$if->{NAME}");