@@ -92,14 +92,6 @@ Complex load_complex(const unsigned char *buf) {
9292 return Complex{load_double (buf), load_double (buf + 8 )};
9393}
9494
95- // Helper to mark invalid input
96- bool MarkInvalid (size_t *length) {
97- if (length != nullptr ) {
98- *length = SIZE_MAX;
99- }
100- return true ;
101- }
102-
10395// Simple FNV-1a hash
10496size_t fnv1a_hash (const unsigned char *data, size_t len) {
10597 size_t hash = 2166136261u ;
@@ -110,123 +102,145 @@ size_t fnv1a_hash(const unsigned char *data, size_t len) {
110102 return hash;
111103}
112104
113- // COMPLEX encode: "(real,imag)" -> 16 bytes (with canonicalization of -0.0)
114- // Returns false on success, true on error. Set *length = SIZE_MAX for NULL.
115- bool encode_complex (unsigned char *buffer, size_t buffer_size, const char *from,
116- size_t from_len, size_t *length) {
117- // Format in "from" is expected to be "(<double>,<double>)".
118- if (buffer_size < kComplexSize || buffer == nullptr ) {
119- *length = 0 ; // No data written on error
120- return true ;
105+ void ReturnError (std::string_view err_msg, vef_vdf_result_t *result) {
106+ result->type = VEF_RESULT_ERROR;
107+ if (err_msg.size () >= VEF_MAX_ERROR_LEN) {
108+ err_msg = err_msg.substr (0 , VEF_MAX_ERROR_LEN - 1 );
121109 }
110+ err_msg.copy (result->error_msg , err_msg.size ());
111+ result->error_msg [err_msg.size ()] = 0 ;
112+ }
122113
114+ void ReturnComplex (const Complex &cx, vef_vdf_result_t *result) {
115+ result->type = VEF_RESULT_VALUE;
116+ store_complex (result->bin_buf , cx);
117+ result->actual_len = kComplexSize ;
118+ }
119+
120+ // COMPLEX encode: "(real,imag)" -> 16 bytes (with canonicalization of -0.0)
121+ // STRING -> COMPLEX
122+ void complex_from_string (vef_context_t *ctx, vef_invalue_t *in,
123+ vef_vdf_result_t *out) {
124+ if (in->is_null ) {
125+ out->type = VEF_RESULT_NULL;
126+ return ;
127+ }
128+ if (out->max_bin_len < kComplexSize ) {
129+ ReturnError (" response buffer too small" , out);
130+ return ;
131+ }
132+ // Format in "in" is expected to be "(<double>,<double>)".
133+ // in->str_value isn't null-terminated, so create a copy
123134 Complex cx;
124- // from isn't null-terminated, so create a copy
125- std::string from_str (from, from_len);
135+ std::string from_str (in->str_value , in->str_len );
126136 if (sscanf (from_str.c_str (), " ( %lg , %lg )" , &cx.re , &cx.im ) != 2 ) {
127- return MarkInvalid (length);
137+ ReturnError (" failed to parse string '" + from_str + " '" , out);
138+ return ;
128139 }
129-
130140 cx.canonicalize ();
131- store_complex (buffer , cx);
132- *length = kComplexSize ;
133- return false ;
141+ store_complex (out-> bin_buf , cx);
142+ out-> actual_len = kComplexSize ;
143+ out-> type = VEF_RESULT_VALUE ;
134144}
135145
136146// COMPLEX2 encode: "(real,imag)" -> 16 bytes (without canonicalization)
137- bool encode_complex2 (unsigned char *buffer, size_t buffer_size,
138- const char *from, size_t from_len, size_t *length) {
139- // COMPLEX2: encode WITHOUT canonicalization - preserves -0.0 in binary form.
140- // Used to test custom hash function path (hash canonicalizes on the fly).
141- if (buffer_size < kComplexSize || buffer == nullptr ) {
142- *length = 0 ;
143- return true ;
147+ // STRING -> COMPLEX2
148+ // COMPLEX2: encode WITHOUT canonicalization - preserves -0.0 in binary form.
149+ // Used to test custom hash function path (hash canonicalizes on the fly).
150+ void complex2_from_string (vef_context_t *ctx, vef_invalue_t *in,
151+ vef_vdf_result_t *out) {
152+ if (in->is_null ) {
153+ out->type = VEF_RESULT_NULL;
154+ return ;
155+ }
156+ if (out->max_bin_len < kComplexSize ) {
157+ ReturnError (" response buffer too small" , out);
158+ return ;
144159 }
145-
146160 Complex cx;
147- std::string from_str (from, from_len );
161+ std::string from_str (in-> str_value , in-> str_len );
148162 if (sscanf (from_str.c_str (), " ( %lg , %lg )" , &cx.re , &cx.im ) != 2 ) {
149- return MarkInvalid (length);
163+ ReturnError (" failed to parse string '" + from_str + " '" , out);
164+ return ;
150165 }
151-
152166 // No canonicalization - -0.0 is preserved in binary representation.
153167 // The custom hash function will canonicalize on the fly.
154- store_complex (buffer , cx);
155- *length = kComplexSize ;
156- return false ;
168+ store_complex (out-> bin_buf , cx);
169+ out-> actual_len = kComplexSize ;
170+ out-> type = VEF_RESULT_VALUE ;
157171}
158172
159173// Decode: 16 bytes -> "(real,imag)" string
160- // Returns false on success, true on error.
161- bool decode_complex (const unsigned char *buffer, size_t buffer_size, char *to,
162- size_t to_buffer_size, size_t *to_length) {
163- if (buffer == nullptr || to == nullptr || to_length == nullptr ) {
164- return true ;
174+ // COMPLEX -> STRING
175+ void complex_to_string (vef_context_t *ctx, vef_invalue_t *in,
176+ vef_vdf_result_t *out) {
177+ if (in->is_null ) {
178+ out->type = VEF_RESULT_NULL;
179+ return ;
165180 }
166- if (buffer_size < kComplexSize ) {
167- return true ;
181+ if (in->bin_len < kComplexSize ) {
182+ ReturnError (" argument malformed" , out);
183+ return ;
168184 }
169-
170- Complex cx = load_complex (buffer);
171- int written = snprintf (to, to_buffer_size, " (%g,%g)" , cx.re , cx.im );
172- if (written < 0 || static_cast <size_t >(written) >= to_buffer_size) {
173- return true ;
185+ Complex cx = load_complex (in->bin_value );
186+ int written =
187+ snprintf (out->str_buf , out->max_str_len , " (%g,%g)" , cx.re , cx.im );
188+ if (written < 0 || static_cast <size_t >(written) >= out->max_str_len ) {
189+ ReturnError (" output buffer too small" , out);
190+ return ;
174191 }
175- *to_length = written;
176- return false ;
192+ out-> actual_len = written;
193+ out-> type = VEF_RESULT_VALUE ;
177194}
178195
179- // Comparison function for ORDER BY, indexes
180- int cmp_complex (const unsigned char *data1, size_t len1,
181- const unsigned char *data2, size_t len2) {
182- if (len1 < kComplexSize || len2 < kComplexSize ) {
183- return 0 ; // Invalid lengths, treat as equal
196+ // Compare VDF for ORDER BY, indexes: COMPLEX x COMPLEX -> INT
197+ void complex_compare (vef_context_t *ctx, vef_invalue_t *in_l,
198+ vef_invalue_t *in_r, vef_vdf_result_t *out) {
199+ if (in_l->bin_len < kComplexSize || in_r->bin_len < kComplexSize ) {
200+ out->int_value = 0 ; // Invalid lengths, treat as equal
201+ out->type = VEF_RESULT_VALUE;
202+ return ;
184203 }
185204
186- Complex c1 = load_complex (data1 );
187- Complex c2 = load_complex (data2 );
205+ Complex c1 = load_complex (in_l-> bin_value );
206+ Complex c2 = load_complex (in_r-> bin_value );
188207
189208 // Compare real parts first
190- if (c1.re < c2.re ) return -1 ;
191- if (c1.re > c2.re ) return 1 ;
192-
209+ if (c1.re < c2.re )
210+ out->int_value = -1 ;
211+ else if (c1.re > c2.re )
212+ out->int_value = 1 ;
193213 // Real parts equal, compare imaginary parts
194- if (c1.im < c2.im ) return -1 ;
195- if (c1.im > c2.im ) return 1 ;
214+ else if (c1.im < c2.im )
215+ out->int_value = -1 ;
216+ else if (c1.im > c2.im )
217+ out->int_value = 1 ;
218+ else
219+ out->int_value = 0 ; // Both parts equal
196220
197- return 0 ; // Both parts equal
221+ out-> type = VEF_RESULT_VALUE;
198222}
199223
200- // COMPLEX2 hash: canonicalizes -0 to +0 before hashing so that -0.0 and +0.0
201- // hash to the same bucket. This allows COMPLEX2 to preserve -0 in storage
202- // while still working correctly with hash joins and EXCEPT operations.
203- size_t hash_complex2 (const unsigned char *data, size_t len) {
204- if (len < kComplexSize ) {
205- return fnv1a_hash (data, len);
224+ // Hash VDF: COMPLEX2 -> INT
225+ // Canonicalizes -0 to +0 before hashing so that -0.0 and +0.0 hash to the
226+ // same bucket. This allows COMPLEX2 to preserve -0 in storage while still
227+ // working correctly with hash joins and EXCEPT operations.
228+ void complex2_hash (vef_context_t *ctx, vef_invalue_t *in,
229+ vef_vdf_result_t *out) {
230+ if (in->bin_len < kComplexSize ) {
231+ out->int_value =
232+ static_cast <long long >(fnv1a_hash (in->bin_value , in->bin_len ));
233+ out->type = VEF_RESULT_VALUE;
234+ return ;
206235 }
207236
208- Complex cx = load_complex (data );
237+ Complex cx = load_complex (in-> bin_value );
209238 cx.canonicalize ();
210239
211240 // Hash the canonicalized values
212241 unsigned char canonical[kComplexSize ];
213242 store_complex (canonical, cx);
214- return fnv1a_hash (canonical, kComplexSize );
215- }
216-
217- // Compare VDF: complex_compare(a, b) -> INT
218- void complex_compare_impl (vef_context_t *ctx, vef_invalue_t *in_l,
219- vef_invalue_t *in_r, vef_vdf_result_t *out) {
220- out->int_value = cmp_complex (in_l->bin_value , in_l->bin_len , in_r->bin_value ,
221- in_r->bin_len );
222- out->type = VEF_RESULT_VALUE;
223- }
224-
225- // Hash VDF: complex2_hash(c) -> INT
226- void complex2_hash_impl (vef_context_t *ctx, vef_invalue_t *in,
227- vef_vdf_result_t *out) {
228- out->int_value =
229- static_cast <long long >(hash_complex2 (in->bin_value , in->bin_len ));
243+ out->int_value = static_cast <long long >(fnv1a_hash (canonical, kComplexSize ));
230244 out->type = VEF_RESULT_VALUE;
231245}
232246
@@ -237,21 +251,6 @@ std::optional<Complex> TryLoadFromInValue(const vef_invalue_t *v) {
237251 return load_complex (v->bin_value );
238252}
239253
240- void ReturnError (std::string_view err_msg, vef_vdf_result_t *result) {
241- result->type = VEF_RESULT_ERROR;
242- if (err_msg.size () >= VEF_MAX_ERROR_LEN) {
243- err_msg = err_msg.substr (0 , VEF_MAX_ERROR_LEN - 1 );
244- }
245- err_msg.copy (result->error_msg , err_msg.size ());
246- result->error_msg [err_msg.size ()] = 0 ;
247- }
248-
249- void ReturnComplex (const Complex &cx, vef_vdf_result_t *result) {
250- result->type = VEF_RESULT_VALUE;
251- store_complex (result->bin_buf , cx);
252- result->actual_len = kComplexSize ;
253- }
254-
255254// Arithmetic: complex_add(a, b) -> COMPLEX
256255void complex_add_impl (vef_context_t *ctx, vef_invalue_t *in_l,
257256 vef_invalue_t *in_r, vef_vdf_result_t *out) {
@@ -477,28 +476,40 @@ VEF_GENERATE_ENTRY_POINTS(
477476 .hash(" complex2_hash" )
478477 .build())
479478 // Type conversion functions (also serve as encode/decode VDFs)
480- .func(make_func(" complex_from_string" )
481- .from_string<&encode_complex>(COMPLEX))
482- .func(
483- make_func (" complex_to_string" ).to_string<&decode_complex>(COMPLEX))
484- .func(make_func(" complex2_from_string" )
485- .from_string<&encode_complex2>(COMPLEX2))
486- .func(make_func(" complex2_to_string" )
487- .to_string<&decode_complex>(COMPLEX2))
479+ .func(make_func<&complex_from_string>(" complex_from_string" )
480+ .returns(COMPLEX)
481+ .param(STRING)
482+ .deterministic()
483+ .build())
484+ .func(make_func<&complex_to_string>(" complex_to_string" )
485+ .returns(STRING)
486+ .param(COMPLEX)
487+ .deterministic()
488+ .build())
489+ .func(make_func<&complex2_from_string>(" complex2_from_string" )
490+ .returns(COMPLEX2)
491+ .param(STRING)
492+ .deterministic()
493+ .build())
494+ .func(make_func<&complex_to_string>(" complex2_to_string" )
495+ .returns(STRING)
496+ .param(COMPLEX2)
497+ .deterministic()
498+ .build())
488499 // Compare and hash VDFs
489- .func(make_func<&complex_compare_impl >(" complex_compare" )
500+ .func(make_func<&complex_compare >(" complex_compare" )
490501 .returns(INT)
491502 .param(COMPLEX)
492503 .param(COMPLEX)
493504 .deterministic()
494505 .build())
495- .func(make_func<&complex_compare_impl >(" complex2_compare" )
506+ .func(make_func<&complex_compare >(" complex2_compare" )
496507 .returns(INT)
497508 .param(COMPLEX2)
498509 .param(COMPLEX2)
499510 .deterministic()
500511 .build())
501- .func(make_func<&complex2_hash_impl >(" complex2_hash" )
512+ .func(make_func<&complex2_hash >(" complex2_hash" )
502513 .returns(INT)
503514 .param(COMPLEX2)
504515 .deterministic()
0 commit comments