1- use rustler:: sys:: { enif_make_list_from_array, enif_make_map_from_arrays, ERL_NIF_TERM } ;
1+ use rustler:: sys:: {
2+ enif_make_list_from_array, enif_make_map_from_arrays, enif_make_map_put, enif_make_new_map,
3+ ERL_NIF_TERM ,
4+ } ;
25use rustler:: { Env , NewBinary , Term } ;
36use sonic_rs:: { JsonContainerTrait , JsonType , JsonValueTrait } ;
47
@@ -83,14 +86,18 @@ pub fn value_to_term<'a>(env: Env<'a>, value: &sonic_rs::Value) -> Term<'a> {
8386 }
8487 let mut map: ERL_NIF_TERM = 0 ;
8588 unsafe {
86- enif_make_map_from_arrays (
89+ if enif_make_map_from_arrays (
8790 env. as_c_arg ( ) ,
8891 keys. as_ptr ( ) ,
8992 vals. as_ptr ( ) ,
9093 count,
9194 & mut map,
92- ) ;
93- Term :: new ( env, map)
95+ ) != 0
96+ {
97+ Term :: new ( env, map)
98+ } else {
99+ build_map_dedup ( env, obj)
100+ }
94101 }
95102 } else {
96103 let mut keys: Vec < ERL_NIF_TERM > = Vec :: with_capacity ( count) ;
@@ -101,16 +108,38 @@ pub fn value_to_term<'a>(env: Env<'a>, value: &sonic_rs::Value) -> Term<'a> {
101108 }
102109 let mut map: ERL_NIF_TERM = 0 ;
103110 unsafe {
104- enif_make_map_from_arrays (
111+ if enif_make_map_from_arrays (
105112 env. as_c_arg ( ) ,
106113 keys. as_ptr ( ) ,
107114 vals. as_ptr ( ) ,
108115 count,
109116 & mut map,
110- ) ;
111- Term :: new ( env, map)
117+ ) != 0
118+ {
119+ Term :: new ( env, map)
120+ } else {
121+ build_map_dedup ( env, obj)
122+ }
112123 }
113124 }
114125 }
115126 }
116127}
128+
129+ /// Fallback for objects with duplicate keys. Iterates all pairs so that the
130+ /// last value for each duplicate key wins, matching common JSON parser behaviour.
131+ /// Marked `#[cold]` so the optimiser keeps the duplicate-free fast path hot.
132+ #[ cold]
133+ fn build_map_dedup < ' a > ( env : Env < ' a > , obj : & sonic_rs:: Object ) -> Term < ' a > {
134+ unsafe {
135+ let mut map = enif_make_new_map ( env. as_c_arg ( ) ) ;
136+ for ( k, v) in obj. iter ( ) {
137+ let key = make_binary_term ( env, k) . as_c_arg ( ) ;
138+ let val = value_to_term ( env, v) . as_c_arg ( ) ;
139+ let mut new_map: ERL_NIF_TERM = 0 ;
140+ enif_make_map_put ( env. as_c_arg ( ) , map, key, val, & mut new_map) ;
141+ map = new_map;
142+ }
143+ Term :: new ( env, map)
144+ }
145+ }
0 commit comments