Skip to content

Commit cbf7b14

Browse files
committed
avoid copy by keeping reference to user's byte array
1 parent b56ddbd commit cbf7b14

1 file changed

Lines changed: 27 additions & 8 deletions

File tree

src/native/http_request_response.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,15 @@ JNIEXPORT jint JNICALL Java_software_amazon_awssdk_crt_http_HttpStream_httpStrea
611611

612612
struct http_stream_write_data_callback_data {
613613
struct http_stream_binding *stream_cb_data;
614-
struct aws_byte_buf data_buf;
614+
/*
615+
* Global ref to the Java byte[] passed in by the caller, and the aws_byte_cursor acquired
616+
* from it via aws_jni_byte_cursor_from_jbyteArray_acquire. Held on callback_data so the
617+
* JNI-side bytes stay valid for the full async write, and released from
618+
* s_cleanup_write_data_callback_data once the write completes (or fails synchronously).
619+
* Avoids copying the payload into a native aws_byte_buf before sending.
620+
*/
621+
jbyteArray data_array;
622+
struct aws_byte_cursor data_cur;
615623
struct aws_input_stream *data_stream;
616624
jobject completion_callback;
617625
};
@@ -622,7 +630,11 @@ static void s_cleanup_write_data_callback_data(
622630
if (callback_data->data_stream) {
623631
aws_input_stream_destroy(callback_data->data_stream);
624632
}
625-
aws_byte_buf_clean_up(&callback_data->data_buf);
633+
if (callback_data->data_array) {
634+
/* Release the JNI pin/copy (needs the jbyteArray + original cursor), then drop our global ref. */
635+
aws_jni_byte_cursor_from_jbyteArray_release(env, callback_data->data_array, callback_data->data_cur);
636+
(*env)->DeleteGlobalRef(env, callback_data->data_array);
637+
}
626638
(*env)->DeleteGlobalRef(env, callback_data->completion_callback);
627639
aws_mem_release(aws_jni_get_allocator(), callback_data);
628640
}
@@ -676,12 +688,19 @@ JNIEXPORT jint JNICALL Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpS
676688
};
677689

678690
if (data != NULL) {
679-
struct aws_byte_cursor data_cur = aws_jni_byte_cursor_from_jbyteArray_acquire(env, data);
680-
aws_byte_buf_init_copy_from_cursor(&callback_data->data_buf, aws_jni_get_allocator(), data_cur);
681-
aws_jni_byte_cursor_from_jbyteArray_release(env, data, data_cur);
682-
683-
data_cur = aws_byte_cursor_from_buf(&callback_data->data_buf);
684-
callback_data->data_stream = aws_input_stream_new_from_cursor(aws_jni_get_allocator(), &data_cur);
691+
/*
692+
* Promote to a global ref so it survives beyond this JNI frame — the async write
693+
* completion fires on the event-loop thread after this function returns.
694+
* Acquire the cursor once, store both on callback_data, and let
695+
* s_cleanup_write_data_callback_data release them when the write finishes.
696+
* aws_input_stream_new_from_cursor copies the {ptr,len} cursor struct into its own
697+
* state, but does NOT copy the underlying bytes, so this path avoids the payload copy.
698+
*/
699+
callback_data->data_array = (*env)->NewGlobalRef(env, data);
700+
callback_data->data_cur = aws_jni_byte_cursor_from_jbyteArray_acquire(env, data);
701+
702+
callback_data->data_stream =
703+
aws_input_stream_new_from_cursor(aws_jni_get_allocator(), &callback_data->data_cur);
685704
options.data = callback_data->data_stream;
686705
}
687706

0 commit comments

Comments
 (0)