This is an automated email from the ASF dual-hosted git repository.

nickva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb-jiffy.git

commit c26d934186e12b890a38505756b9b7c72e41207e
Author: Nick Vatamaniuc <[email protected]>
AuthorDate: Sun Jun 14 01:01:56 2026 -0400

    Encode maps directly without an intermediate EJSON proplist
    
    Encode a map from a flat `[k1, v1, ...]` list built from a map itertor 
instead
    `{k,v}` tuples. To drive yielding properly a new `$map_ref$` stack marker 
but
    otherwise it's a regular old list.
    
    Saw a 5% median speedup on tests with lots of json objects like `gsoc` and
    neutral on others.
---
 c_src/encoder.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 c_src/jiffy.c   |  1 +
 c_src/jiffy.h   |  1 +
 3 files changed, 72 insertions(+), 7 deletions(-)

diff --git a/c_src/encoder.c b/c_src/encoder.c
index fa4bf98..c58ccbc 100644
--- a/c_src/encoder.c
+++ b/c_src/encoder.c
@@ -728,14 +728,18 @@ enc_comma(Encoder* e)
     return 1;
 }
 
+// Build an interleaved [k1, v1, k2, v2, ...] list from a map itertor. Then
+// encoder can stream the object out so we don't have to build a tuple and then
+// have to do an enif_get_tuple in the encode pass thus saving some allocations
+// and CPU resources.
+//
 static int
-enc_map_to_ejson(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM* out)
+enc_map_to_memberlist(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM* out)
 {
     ErlNifMapIterator iter;
     size_t size;
 
     ERL_NIF_TERM list;
-    ERL_NIF_TERM tuple;
     ERL_NIF_TERM key;
     ERL_NIF_TERM val;
 
@@ -746,7 +750,7 @@ enc_map_to_ejson(ErlNifEnv* env, ERL_NIF_TERM map, 
ERL_NIF_TERM* out)
     list = enif_make_list(env, 0);
 
     if(size == 0) {
-        *out = enif_make_tuple1(env, list);
+        *out = list;
         return 1;
     }
 
@@ -759,13 +763,13 @@ enc_map_to_ejson(ErlNifEnv* env, ERL_NIF_TERM map, 
ERL_NIF_TERM* out)
             enif_map_iterator_destroy(env, &iter);
             return 0;
         }
-        tuple = enif_make_tuple2(env, key, val);
-        list = enif_make_list_cell(env, tuple, list);
+        list = enif_make_list_cell(env, val, list);
+        list = enif_make_list_cell(env, key, list);
     } while(enif_map_iterator_next(env, &iter));
 
     enif_map_iterator_destroy(env, &iter);
 
-    *out = enif_make_tuple1(env, list);
+    *out = list;
     return 1;
 }
 
@@ -833,6 +837,8 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM 
argv[])
 
     ERL_NIF_TERM curr;
     ERL_NIF_TERM item;
+    ERL_NIF_TERM mkey;
+    ERL_NIF_TERM mval;
     const ERL_NIF_TERM* tuple;
     ERL_NIF_TERM tmp_argv[3];
     int arity;
@@ -944,6 +950,37 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM 
argv[])
                 termstack_push(&stack, curr);
                 termstack_push(&stack, e->atoms->ref_array);
                 termstack_push(&stack, item);
+            } else if(enif_is_identical(curr, e->atoms->ref_map)) {
+                // Maps are encoded as [k1, v1, ...]
+                curr = termstack_pop(&stack);
+
+                if(!enif_get_list_cell(env, curr, &mkey, &curr)) {
+                    if(!enc_end_object(e)) {
+                        ret = enc_error(e, "internal_error");
+                        goto done;
+                    }
+                    continue;
+                }
+                if(!enif_get_list_cell(env, curr, &mval, &curr)) {
+                    ret = enc_error(e, "internal_error");
+                    goto done;
+                }
+                if(!enc_comma(e)) {
+                    ret = enc_error(e, "internal_error");
+                    goto done;
+                }
+                if(!enc_object_key(env, e, mkey)) {
+                    ret = enc_obj_error(e, "invalid_object_member_key", mkey);
+                    goto done;
+                }
+                if(!enc_colon(e)) {
+                    ret = enc_error(e, "internal_error");
+                    goto done;
+                }
+
+                termstack_push(&stack, curr);
+                termstack_push(&stack, e->atoms->ref_map);
+                termstack_push(&stack, mval);
             } else if(enif_is_identical(curr, e->atoms->atom_null)) {
                 if(!enc_literal(e, "null", 4)) {
                     ret = enc_error(e, "null");
@@ -1028,12 +1065,38 @@ encode_iter(ErlNifEnv* env, int argc, const 
ERL_NIF_TERM argv[])
             termstack_push(&stack, e->atoms->ref_object);
             termstack_push(&stack, tuple[1]);
         } else if(enif_is_map(env, curr)) {
-            if(!enc_map_to_ejson(env, curr, &curr)) {
+            // A map is encoded as a flat [k1, v1, ...] list
+            if(!enc_map_to_memberlist(env, curr, &curr)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(!enc_start_object(e)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(!enif_get_list_cell(env, curr, &mkey, &curr)) {
+                if(!enc_end_object(e)) {
+                    ret = enc_error(e, "internal_error");
+                    goto done;
+                }
+                continue;
+            }
+            if(!enif_get_list_cell(env, curr, &mval, &curr)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(!enc_object_key(env, e, mkey)) {
+                ret = enc_obj_error(e, "invalid_object_member_key", mkey);
+                goto done;
+            }
+            if(!enc_colon(e)) {
                 ret = enc_error(e, "internal_error");
                 goto done;
             }
 
             termstack_push(&stack, curr);
+            termstack_push(&stack, e->atoms->ref_map);
+            termstack_push(&stack, mval);
         } else if(enif_is_list(env, curr)) {
             if(!enc_start_array(e)) {
                 ret = enc_error(e, "internal_error");
diff --git a/c_src/jiffy.c b/c_src/jiffy.c
index debdab9..534f6d6 100644
--- a/c_src/jiffy.c
+++ b/c_src/jiffy.c
@@ -49,6 +49,7 @@ load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
     // Markers used in encoding
     st->ref_object = make_atom(env, "$object_ref$");
     st->ref_array = make_atom(env, "$array_ref$");
+    st->ref_map = make_atom(env, "$map_ref$");
 
     st->res_dec = enif_open_resource_type(
             env,
diff --git a/c_src/jiffy.h b/c_src/jiffy.h
index 9547016..3cc9c2c 100644
--- a/c_src/jiffy.h
+++ b/c_src/jiffy.h
@@ -58,6 +58,7 @@ typedef struct {
 
     ERL_NIF_TERM    ref_object;
     ERL_NIF_TERM    ref_array;
+    ERL_NIF_TERM    ref_map;
 
     ErlNifResourceType* res_dec;
     ErlNifResourceType* res_enc;

Reply via email to