Skip to content

Commit ff89c29

Browse files
Code Review feedback
1 parent dc017d6 commit ff89c29

5 files changed

Lines changed: 242 additions & 222 deletions

File tree

villagesql/examples/vsql-complex/src/complex.cc

Lines changed: 124 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -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
10496
size_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
256255
void 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

Comments
 (0)