@@ -320,6 +320,35 @@ struct ToStringWrapper {
320320 }
321321};
322322
323+ // =============================================================================
324+ // C++ Type Operation Builders
325+ // =============================================================================
326+ //
327+ // Extension authors must implement functions with the following signatures
328+ // to enable a new type to be used in VillageSQL, and use the builders
329+ // make_type_encode, make_type_decode, make_type_compare, and make_type_hash to
330+ // and register the names with the type builder.
331+ // TypeEncodeFunc -> VDF: (STRING) -> CUSTOM(type)
332+ // TypeDecodeFunc -> VDF: (CUSTOM(type)) -> STRING
333+ // TypeCompareFunc -> VDF: (CUSTOM(type), CUSTOM(type)) -> INT
334+ // TypeHashFunc -> VDF: (CUSTOM(type)) -> INT
335+
336+ // Encode: string -> binary. Returns false on success, true on error.
337+ // Set *length = SIZE_MAX to produce SQL NULL.
338+ using TypeEncodeFunc = bool (*)(std::string_view from, Span<unsigned char > buf,
339+ size_t *length);
340+
341+ // Decode: binary -> string. Returns false on success, true on error.
342+ using TypeDecodeFunc = bool (*)(Span<const unsigned char > data, Span<char > out,
343+ size_t *out_len);
344+
345+ // Compare: two binary values. Returns <0, 0, or >0.
346+ using TypeCompareFunc = int (*)(Span<const unsigned char > a,
347+ Span<const unsigned char > b);
348+
349+ // Hash: binary value -> hash code.
350+ using TypeHashFunc = size_t (*)(Span<const unsigned char > data);
351+
323352// IntrinsicDefaultWrapper: wraps a vef_intrinsic_default_func_t into a VDF.
324353// VDF signature: (INT) -> STRING (binary), where INT is the resolved
325354// persisted_length (buffer_size). Returns the encoded default value as binary.
@@ -342,6 +371,102 @@ struct IntrinsicDefaultWrapper {
342371 }
343372};
344373
374+ // TypeEncodeVdfWrapper: wraps a TypeEncodeFunc into a VDF.
375+ // VDF signature: (STRING) -> CUSTOM(type).
376+ template <TypeEncodeFunc Func>
377+ struct TypeEncodeVdfWrapper {
378+ static void invoke (vef_context_t *ctx, vef_vdf_args_t *args,
379+ vef_vdf_result_t *result) {
380+ vef_invalue_t arg = get_invalue (ctx, args, 0 );
381+ if (arg.is_null ) {
382+ result->type = VEF_RESULT_NULL;
383+ return ;
384+ }
385+ size_t length;
386+ bool failed = Func ({arg.str_value , arg.str_len },
387+ {result->bin_buf , result->max_bin_len }, &length);
388+ if (failed) {
389+ result->type = VEF_RESULT_ERROR;
390+ constexpr size_t kMaxInputDisplay = 64 ;
391+ size_t display_len = arg.str_len ;
392+ const char *ellipsis = " " ;
393+ if (display_len > kMaxInputDisplay ) {
394+ display_len = kMaxInputDisplay ;
395+ ellipsis = " ..." ;
396+ }
397+ snprintf (result->error_msg , VEF_MAX_ERROR_LEN,
398+ " failed to encode '%.*s%s'" , static_cast <int >(display_len),
399+ arg.str_value , ellipsis);
400+ return ;
401+ }
402+ if (length == SIZE_MAX) {
403+ result->type = VEF_RESULT_NULL;
404+ return ;
405+ }
406+ result->type = VEF_RESULT_VALUE;
407+ result->actual_len = length;
408+ }
409+ };
410+
411+ // TypeDecodeVdfWrapper: wraps a TypeDecodeFunc into a VDF.
412+ // VDF signature: (CUSTOM(type)) -> STRING.
413+ template <TypeDecodeFunc Func>
414+ struct TypeDecodeVdfWrapper {
415+ static void invoke (vef_context_t *ctx, vef_vdf_args_t *args,
416+ vef_vdf_result_t *result) {
417+ vef_invalue_t arg = get_invalue (ctx, args, 0 );
418+ if (arg.is_null ) {
419+ result->type = VEF_RESULT_NULL;
420+ return ;
421+ }
422+ size_t out_len;
423+ bool failed = Func ({arg.bin_value , arg.bin_len },
424+ {result->str_buf , result->max_str_len }, &out_len);
425+ if (failed) {
426+ result->type = VEF_RESULT_ERROR;
427+ snprintf (result->error_msg , VEF_MAX_ERROR_LEN, " failed to decode value" );
428+ return ;
429+ }
430+ result->type = VEF_RESULT_VALUE;
431+ result->actual_len = out_len;
432+ }
433+ };
434+
435+ // TypeCompareVdfWrapper: wraps a TypeCompareFunc into a VDF.
436+ // VDF signature: (CUSTOM(type), CUSTOM(type)) -> INT.
437+ template <TypeCompareFunc Func>
438+ struct TypeCompareVdfWrapper {
439+ static void invoke (vef_context_t *ctx, vef_vdf_args_t *args,
440+ vef_vdf_result_t *result) {
441+ vef_invalue_t a = get_invalue (ctx, args, 0 );
442+ vef_invalue_t b = get_invalue (ctx, args, 1 );
443+ if (a.is_null || b.is_null ) {
444+ result->type = VEF_RESULT_NULL;
445+ return ;
446+ }
447+ result->int_value =
448+ Func ({a.bin_value , a.bin_len }, {b.bin_value , b.bin_len });
449+ result->type = VEF_RESULT_VALUE;
450+ }
451+ };
452+
453+ // TypeHashVdfWrapper: wraps a TypeHashFunc into a VDF.
454+ // VDF signature: (CUSTOM(type)) -> INT.
455+ template <TypeHashFunc Func>
456+ struct TypeHashVdfWrapper {
457+ static void invoke (vef_context_t *ctx, vef_vdf_args_t *args,
458+ vef_vdf_result_t *result) {
459+ vef_invalue_t arg = get_invalue (ctx, args, 0 );
460+ if (arg.is_null ) {
461+ result->type = VEF_RESULT_NULL;
462+ return ;
463+ }
464+ result->int_value =
465+ static_cast <long long >(Func ({arg.bin_value , arg.bin_len }));
466+ result->type = VEF_RESULT_VALUE;
467+ }
468+ };
469+
345470// =============================================================================
346471// Extension Author Function Signatures (for parameterized types)
347472// =============================================================================
@@ -754,6 +879,67 @@ constexpr StaticFuncDesc<1> make_intrinsic_default(const char *name) {
754879 return StaticFuncDesc<1 >(name, meta);
755880}
756881
882+ // Entry point for encode VDFs: (STRING) -> CUSTOM(type_name).
883+ // make_type_encode<&my_func>("func_name", TYPE)
884+ // Register with .func() and reference in type via .encode("func_name").
885+ template <TypeEncodeFunc Func>
886+ constexpr StaticFuncDesc<1 > make_type_encode (const char *name,
887+ const char *type_name) {
888+ FuncWithMetadata meta{};
889+ meta.f = &TypeEncodeVdfWrapper<Func>::invoke;
890+ meta.return_type = to_vef_type (type_name);
891+ meta.param_types [0 ] = to_vef_type (STRING);
892+ meta.num_params = 1 ;
893+ meta.buffer_size = 0 ; // server provides bin_buf sized to persisted_length
894+ return StaticFuncDesc<1 >(name, meta);
895+ }
896+
897+ // Entry point for decode VDFs: (CUSTOM(type_name)) -> STRING.
898+ // make_type_decode<&my_func>("func_name", TYPE)
899+ // Register with .func() and reference in type via .decode("func_name").
900+ template <TypeDecodeFunc Func>
901+ constexpr StaticFuncDesc<1 > make_type_decode (const char *name,
902+ const char *type_name) {
903+ FuncWithMetadata meta{};
904+ meta.f = &TypeDecodeVdfWrapper<Func>::invoke;
905+ meta.return_type = to_vef_type (STRING);
906+ meta.param_types [0 ] = to_vef_type (type_name);
907+ meta.num_params = 1 ;
908+ meta.buffer_size = 0 ;
909+ return StaticFuncDesc<1 >(name, meta);
910+ }
911+
912+ // Entry point for compare VDFs: (CUSTOM(type_name), CUSTOM(type_name)) -> INT.
913+ // make_type_compare<&my_func>("func_name", TYPE)
914+ // Register with .func() and reference in type via .compare("func_name").
915+ template <TypeCompareFunc Func>
916+ constexpr StaticFuncDesc<2 > make_type_compare (const char *name,
917+ const char *type_name) {
918+ FuncWithMetadata meta{};
919+ meta.f = &TypeCompareVdfWrapper<Func>::invoke;
920+ meta.return_type = to_vef_type (INT);
921+ meta.param_types [0 ] = to_vef_type (type_name);
922+ meta.param_types [1 ] = to_vef_type (type_name);
923+ meta.num_params = 2 ;
924+ meta.buffer_size = 0 ;
925+ return StaticFuncDesc<2 >(name, meta);
926+ }
927+
928+ // Entry point for hash VDFs: (CUSTOM(type_name)) -> INT.
929+ // make_type_hash<&my_func>("func_name", TYPE)
930+ // Register with .func() and reference in type via .hash("func_name").
931+ template <TypeHashFunc Func>
932+ constexpr StaticFuncDesc<1 > make_type_hash (const char *name,
933+ const char *type_name) {
934+ FuncWithMetadata meta{};
935+ meta.f = &TypeHashVdfWrapper<Func>::invoke;
936+ meta.return_type = to_vef_type (INT);
937+ meta.param_types [0 ] = to_vef_type (type_name);
938+ meta.num_params = 1 ;
939+ meta.buffer_size = 0 ;
940+ return StaticFuncDesc<1 >(name, meta);
941+ }
942+
757943// =============================================================================
758944// Internal Implementation
759945// =============================================================================
0 commit comments