Skip to content

Commit ece1b80

Browse files
Convert Python bytes and strings to Elixir binaries without copying (#19)
1 parent 7fbd743 commit ece1b80

File tree

1 file changed

+16
-16
lines changed

1 file changed

+16
-16
lines changed

c_src/pythonx/pythonx.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -266,16 +266,16 @@ void raise_if_failed(ErlNifEnv *env, Py_ssize_t size) {
266266
}
267267
}
268268

269-
ERL_NIF_TERM py_object_to_binary_term(ErlNifEnv *env, PyObjectPtr py_object) {
269+
ERL_NIF_TERM py_str_to_binary_term(ErlNifEnv *env, PyObjectPtr py_object) {
270270
Py_ssize_t size;
271271
auto buffer = PyUnicode_AsUTF8AndSize(py_object, &size);
272272
raise_if_failed(env, buffer);
273273

274-
ERL_NIF_TERM binary_term;
275-
auto data = enif_make_new_binary(env, size, &binary_term);
276-
memcpy(data, buffer, size);
277-
278-
return binary_term;
274+
// The buffer is immutable and lives as long as the Python object,
275+
// so we create the term as a resource binary to make it zero-copy.
276+
Py_IncRef(py_object);
277+
auto ex_object_resource = fine::make_resource<ExObjectResource>(py_object);
278+
return fine::make_resource_binary(env, ex_object_resource, buffer, size);
279279
}
280280

281281
fine::Ok<> init(ErlNifEnv *env, std::string python_dl_path,
@@ -583,7 +583,7 @@ fine::Term unicode_to_string(ErlNifEnv *env, ExObject ex_object) {
583583
ensure_initialized();
584584
auto gil_guard = PyGILGuard();
585585

586-
return py_object_to_binary_term(env, ex_object.resource->py_object);
586+
return py_str_to_binary_term(env, ex_object.resource->py_object);
587587
}
588588

589589
FINE_NIF(unicode_to_string, ERL_NIF_DIRTY_JOB_CPU_BOUND);
@@ -745,7 +745,7 @@ fine::Term format_exception(ErlNifEnv *env, ExError error) {
745745
auto py_line = PyList_GetItem(py_lines, i);
746746
raise_if_failed(env, py_line);
747747

748-
terms.push_back(py_object_to_binary_term(env, py_line));
748+
terms.push_back(py_str_to_binary_term(env, py_line));
749749
}
750750

751751
return enif_make_list_from_array(env, terms.data(),
@@ -803,7 +803,7 @@ fine::Term decode_once(ErlNifEnv *env, ExObject ex_object) {
803803
raise_if_failed(env, py_str);
804804
auto py_str_guard = PyDecRefGuard(py_str);
805805

806-
auto binary_term = py_object_to_binary_term(env, py_str);
806+
auto binary_term = py_str_to_binary_term(env, py_str);
807807

808808
return fine::encode(
809809
env, std::make_tuple(atoms::integer, fine::Term(binary_term)));
@@ -904,7 +904,7 @@ fine::Term decode_once(ErlNifEnv *env, ExObject ex_object) {
904904
auto is_unicode = PyObject_IsInstance(py_object, py_str_type);
905905
raise_if_failed(env, is_unicode);
906906
if (is_unicode) {
907-
return py_object_to_binary_term(env, py_object);
907+
return py_str_to_binary_term(env, py_object);
908908
}
909909

910910
auto py_bytes_type = PyDict_GetItemString(py_builtins, "bytes");
@@ -917,11 +917,11 @@ fine::Term decode_once(ErlNifEnv *env, ExObject ex_object) {
917917
auto result = PyBytes_AsStringAndSize(py_object, &buffer, &size);
918918
raise_if_failed(env, result);
919919

920-
ERL_NIF_TERM binary_term;
921-
auto data = enif_make_new_binary(env, size, &binary_term);
922-
memcpy(data, buffer, size);
923-
924-
return binary_term;
920+
// The buffer is immutable and lives as long as the Python object,
921+
// so we create the term as a resource binary to make it zero-copy.
922+
Py_IncRef(py_object);
923+
auto ex_object_resource = fine::make_resource<ExObjectResource>(py_object);
924+
return fine::make_resource_binary(env, ex_object_resource, buffer, size);
925925
}
926926

927927
auto py_set_type = PyDict_GetItemString(py_builtins, "set");
@@ -1341,7 +1341,7 @@ eval(ErlNifEnv *env, ErlNifBinary code, std::string code_md5,
13411341
continue;
13421342
}
13431343

1344-
auto key_term = py_object_to_binary_term(env, py_key);
1344+
auto key_term = py_str_to_binary_term(env, py_key);
13451345
key_terms.push_back(key_term);
13461346

13471347
// Incref before making the resource

0 commit comments

Comments
 (0)