Skip to content

Commit 1e91494

Browse files
authored
FFI: replace vx_string/vx_binary with vx_view (#8668)
vx_string/vx_binary is not a zero-copy abstraction. For every utf/binary view you allocate memory. This PR replaces both with a vx_view non-owning view akin to C++'s string_view. Signed-off-by: Mikhail Kot <mikhail@spiraldb.com>
1 parent 4abe3d0 commit 1e91494

27 files changed

Lines changed: 547 additions & 722 deletions

docs/api/c/index.rst

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -83,31 +83,5 @@ responsible for freeing them.
8383
.. c:autofunction:: vx_error_free
8484
:file: vortex.h
8585

86-
.. c:autofunction:: vx_error_get_message
86+
.. c:autofunction:: vx_error_message
8787
:file: vortex.h
88-
89-
Strings
90-
-------
91-
92-
Vortex strings wrap a Rust `Arc<str>`, and therefore are reference-counted, UTF-8 encoded, and not null-terminated.
93-
94-
.. c:autotype:: vx_string
95-
:file: vortex.h
96-
97-
.. c:autofunction:: vx_string_clone
98-
:file: vortex.h
99-
100-
.. c:autofunction:: vx_string_free
101-
:file: vortex.h
102-
103-
.. c:autofunction:: vx_string_new
104-
:file: vortex.h
105-
106-
.. c:autofunction:: vx_string_new_from_cstr
107-
:file: vortex.h
108-
109-
.. c:autofunction:: vx_string_len
110-
:file: vortex.h
111-
112-
.. c:autofunction:: vx_string_ptr
113-
:file: vortex.h

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@
171171
("c:identifier", "int16_t"),
172172
("c:identifier", "uint8_t"),
173173
("c:identifier", "int8_t"),
174+
("c:identifier", "vx_view"),
174175
]
175176

176177
hawkmoth_transform_default = "c_to_rust"

vortex-ffi/cbindgen.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ typedef struct ArrowArrayStream FFI_ArrowArrayStream;
6565
#endif
6666
"""
6767

68+
trailer = """
69+
#include <string.h>
70+
71+
/**
72+
* Create a view over a null-terminated C string.
73+
* View is valid as long as "str" is valid
74+
*/
75+
static inline vx_view vx_view_from_cstr(const char* str) {
76+
vx_view s;
77+
s.ptr = str;
78+
s.len = strlen(str);
79+
return s;
80+
}
81+
"""
82+
6883
[export.rename]
6984
"f16" = "uint16_t"
7085

vortex-ffi/cinclude/vortex.h

Lines changed: 80 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -490,11 +490,6 @@ typedef struct vx_array_iterator vx_array_iterator;
490490
*/
491491
typedef struct vx_array_sink vx_array_sink;
492492

493-
/**
494-
* Strings for use within Vortex.
495-
*/
496-
typedef struct vx_binary vx_binary;
497-
498493
/**
499494
* A reference to one or more (possibly remote) paths.
500495
* Creating vx_data_source opens the first matched path to read the schema.
@@ -562,11 +557,6 @@ typedef struct vx_scan vx_scan;
562557
*/
563558
typedef struct vx_session vx_session;
564559

565-
/**
566-
* Strings for use within Vortex.
567-
*/
568-
typedef struct vx_string vx_string;
569-
570560
typedef struct vx_struct_column_builder vx_struct_column_builder;
571561

572562
/**
@@ -595,17 +585,34 @@ typedef struct {
595585
const vx_array *array;
596586
} vx_validity;
597587

588+
/**
589+
* A non owning view over a byte range.
590+
*/
591+
typedef struct {
592+
/**
593+
* NULL "ptr" requires len == 0
594+
*/
595+
const char *ptr;
596+
/**
597+
* Length in bytes.
598+
*/
599+
size_t len;
600+
} vx_view;
601+
598602
/**
599603
* Options for creating a data source.
600604
*/
601605
typedef struct {
602606
/**
603-
* Required: paths to files, tables, or layout trees.
604-
* May be a glob pattern like "*.vortex".
605-
* If you want to include multiple paths, concat them with a comma:
606-
* "file1.vortex,../file2.vortex".
607+
* Required: paths to files, tables, or layout trees. Each entry may be a
608+
* glob pattern like "*.vortex". Must point to an array of size
609+
* "paths_len". paths bytes are copied.
610+
*/
611+
const vx_view *paths;
612+
/**
613+
* Number of entries in `paths`.
607614
*/
608-
const char *paths;
615+
size_t paths_len;
609616
} vx_data_source_options;
610617

611618
/**
@@ -838,16 +845,24 @@ double vx_array_get_f64(const vx_array *array, size_t index);
838845
double vx_array_get_storage_f64(const vx_array *array, size_t index);
839846

840847
/**
841-
* Return the utf-8 string at `index` in the array. The pointer will be null if the value at `index` is null.
842-
* The caller must free the returned pointer.
848+
* Return UTF-8 string at "index" in a canonical Utf8 array.
849+
*
850+
* For invalid elements the returned value is unspecified, check validity via
851+
* vx_array_get_validity.
852+
* Returned view is valid as long as "array" is valid.
853+
* Errors if index is out of bounds or array is not a canonical Utf8 array.
843854
*/
844-
const vx_string *vx_array_get_utf8(const vx_array *array, uint32_t index);
855+
vx_view vx_array_utf8_at(const vx_array *array, size_t index, vx_error **error_out);
845856

846857
/**
847-
* Return the binary at `index` in the array. The pointer will be null if the value at `index` is null.
848-
* The caller must free the returned pointer.
858+
* Return a binary string at "index" in a canonical Binary array.
859+
*
860+
* For invalid elements the returned value is unspecified, check validity via
861+
* vx_array_get_validity.
862+
* Returned view is valid as long as "array" is valid.
863+
* Errors if index is out of bounds or array is not a canonical Binary array.
849864
*/
850-
const vx_binary *vx_array_get_binary(const vx_array *array, uint32_t index);
865+
vx_view vx_array_binary_at(const vx_array *array, size_t index, vx_error **error_out);
851866

852867
/**
853868
* For a canonical Bool array, return bool at "index".
@@ -908,31 +923,6 @@ void vx_array_iterator_free(const vx_array_iterator *ptr);
908923
*/
909924
const vx_array *vx_array_iterator_next(vx_array_iterator *iter, vx_error **error_out);
910925

911-
/**
912-
* Clone a vx_binary. Returned handle must be release with vx_binary_free
913-
*/
914-
const vx_binary *vx_binary_clone(const vx_binary *ptr);
915-
916-
/**
917-
* Free a vx_binary
918-
*/
919-
void vx_binary_free(const vx_binary *ptr);
920-
921-
/**
922-
* Create a new Vortex UTF-8 string by copying from a pointer and length.
923-
*/
924-
const vx_binary *vx_binary_new(const char *ptr, size_t len);
925-
926-
/**
927-
* Return the length of the string in bytes.
928-
*/
929-
size_t vx_binary_len(const vx_binary *ptr);
930-
931-
/**
932-
* Return the pointer to the string data.
933-
*/
934-
const char *vx_binary_ptr(const vx_binary *ptr);
935-
936926
/**
937927
* Clone a vx_data_source. Returned handle must be release with vx_data_source_free
938928
*/
@@ -1106,9 +1096,12 @@ bool vx_dtype_is_timestamp(const DType *dtype);
11061096
uint8_t vx_dtype_time_unit(const DType *dtype);
11071097

11081098
/**
1109-
* Returns the time zone, assuming the type is time. Caller is responsible for freeing the returned pointer.
1099+
* Return time zone assuming "dtype" is time.
1100+
* Returns {NULL, 0} when timestamp has no time zone.
1101+
*
1102+
* Returned view is valid as long as "dtype" is valid.
11101103
*/
1111-
const vx_string *vx_dtype_time_zone(const DType *dtype);
1104+
vx_view vx_dtype_time_zone(const DType *dtype);
11121105

11131106
/**
11141107
* Convert a dtype to ArrowSchema.
@@ -1135,9 +1128,10 @@ const vx_dtype *vx_dtype_from_arrow_schema(FFI_ArrowSchema *schema, vx_error **e
11351128
void vx_error_free(const vx_error *ptr);
11361129

11371130
/**
1138-
* Return an error message for this error
1131+
* Return error message for this error.
1132+
* Returned view is valid while "error" is valid.
11391133
*/
1140-
const vx_string *vx_error_get_message(const vx_error *error);
1134+
vx_view vx_error_message(const vx_error *error);
11411135

11421136
/**
11431137
* Return category code for "error".
@@ -1215,7 +1209,7 @@ vx_expression *vx_expression_literal(const vx_scalar *scalar, vx_error **err);
12151209
* vx_expression_free(select);
12161210
* vx_expression_free(root);
12171211
*/
1218-
vx_expression *vx_expression_select(const char *const *names, size_t len, const vx_expression *child);
1212+
vx_expression *vx_expression_select(const vx_view *names, size_t len, const vx_expression *child);
12191213

12201214
/**
12211215
* Create an AND expression for multiple child expressions.
@@ -1277,8 +1271,11 @@ vx_expression *vx_expression_is_null(const vx_expression *child);
12771271
*
12781272
* Example: if child is Struct { name=u8, age=u16 } and we do
12791273
* vx_expression_get_item("name", child), output type will be DTYPE_U8
1274+
*
1275+
* "item" is copied. Returns NULL if "child" is NULL or "item" is not valid
1276+
* UTF-8.
12801277
*/
1281-
vx_expression *vx_expression_get_item(const char *item, const vx_expression *child);
1278+
vx_expression *vx_expression_get_item(vx_view item, const vx_expression *child);
12821279

12831280
/**
12841281
* Create an expression that checks if a value is contained in a list.
@@ -1298,7 +1295,7 @@ const vx_file *vx_file_clone(const vx_file *ptr);
12981295
void vx_file_free(const vx_file *ptr);
12991296

13001297
void vx_file_write_array(const vx_session *session,
1301-
const char *path,
1298+
vx_view path,
13021299
const vx_array *array,
13031300
vx_error **error_out);
13041301

@@ -1399,11 +1396,10 @@ vx_scalar *vx_scalar_new_f16_bits(uint16_t bits, bool is_nullable);
13991396
/**
14001397
* Create a UTF-8 scalar.
14011398
*
1402-
* The byte range is copied into the scalar. A NULL data pointer is allowed only
1403-
* for an empty byte range. Invalid UTF-8 returns NULL and writes the error
1404-
* output.
1399+
* The string bytes are copied into the scalar. Invalid UTF-8 returns NULL and
1400+
* writes the error output.
14051401
*/
1406-
vx_scalar *vx_scalar_new_utf8(const char *ptr, size_t len, bool is_nullable, vx_error **err);
1402+
vx_scalar *vx_scalar_new_utf8(vx_view value, bool is_nullable, vx_error **err);
14071403

14081404
/**
14091405
* Create a binary scalar.
@@ -1636,11 +1632,10 @@ vx_session *vx_session_clone(const vx_session *session);
16361632
/**
16371633
* Opens a writable array stream, where sink is used to push values into the stream.
16381634
* To close the stream close the sink with `vx_array_sink_close`.
1635+
* "path" is copied.
16391636
*/
1640-
vx_array_sink *vx_array_sink_open_file(const vx_session *session,
1641-
const char *path,
1642-
const vx_dtype *dtype,
1643-
vx_error **error_out);
1637+
vx_array_sink *
1638+
vx_array_sink_open_file(const vx_session *session, vx_view path, const vx_dtype *dtype, vx_error **error_out);
16441639

16451640
/**
16461641
* Push an array into a file sink.
@@ -1660,36 +1655,6 @@ void vx_array_sink_close(vx_array_sink *sink, vx_error **error_out);
16601655
*/
16611656
void vx_array_sink_abort(vx_array_sink *sink);
16621657

1663-
/**
1664-
* Clone a vx_string. Returned handle must be release with vx_string_free
1665-
*/
1666-
const vx_string *vx_string_clone(const vx_string *ptr);
1667-
1668-
/**
1669-
* Free a vx_string
1670-
*/
1671-
void vx_string_free(const vx_string *ptr);
1672-
1673-
/**
1674-
* Create a new Vortex UTF-8 string by copying from a pointer and length.
1675-
*/
1676-
const vx_string *vx_string_new(const char *ptr, size_t len);
1677-
1678-
/**
1679-
* Create a new Vortex UTF-8 string by copying from a null-terminated C-style string.
1680-
*/
1681-
const vx_string *vx_string_new_from_cstr(const char *ptr);
1682-
1683-
/**
1684-
* Return the length of the string in bytes.
1685-
*/
1686-
size_t vx_string_len(const vx_string *ptr);
1687-
1688-
/**
1689-
* Return the pointer to the string data.
1690-
*/
1691-
const char *vx_string_ptr(const vx_string *ptr);
1692-
16931658
/**
16941659
* Free an owned [`vx_struct_column_builder`] object.
16951660
*/
@@ -1712,7 +1677,7 @@ vx_struct_column_builder *vx_struct_column_builder_new(const vx_validity *validi
17121677
* deallocate it using vx_struct_column_builder_free.
17131678
*/
17141679
void vx_struct_column_builder_add_field(vx_struct_column_builder *builder,
1715-
const char *name,
1680+
vx_view name,
17161681
const vx_array *field,
17171682
vx_error **error);
17181683

@@ -1752,10 +1717,12 @@ void vx_struct_fields_free(const vx_struct_fields *ptr);
17521717
uint64_t vx_struct_fields_nfields(const vx_struct_fields *dtype);
17531718

17541719
/**
1755-
* Return an owned name of the field at a given index.
1756-
* If index is out of bounds, returns NULL.
1720+
* Return field name at a given index.
1721+
* If index is out of bounds, returns {NULL, 0}.
1722+
*
1723+
* Returned view is valid as long as "dtype" is valid.
17571724
*/
1758-
const vx_string *vx_struct_fields_field_name(const vx_struct_fields *dtype, size_t idx);
1725+
vx_view vx_struct_fields_field_name(const vx_struct_fields *dtype, size_t idx);
17591726

17601727
/**
17611728
* Return an owned dtype of the field at a given index.
@@ -1776,12 +1743,13 @@ vx_struct_fields_builder *vx_struct_fields_builder_new(void);
17761743
/**
17771744
* Add a field to the struct dtype builder.
17781745
*
1779-
* Takes ownership of both the `name` and `dtype` pointers.
1780-
* Must either free or finalize the builder.
1746+
* "name" is copied. Takes ownership of "dtype".
1747+
* Caller must free or finalize the builder.
17811748
*/
17821749
void vx_struct_fields_builder_add_field(vx_struct_fields_builder *builder,
1783-
const vx_string *name,
1784-
const vx_dtype *dtype);
1750+
vx_view name,
1751+
const vx_dtype *dtype,
1752+
vx_error **error_out);
17851753

17861754
/**
17871755
* Finalize the struct dtype builder, returning a new `vx_struct_fields`.
@@ -1793,3 +1761,16 @@ vx_struct_fields *vx_struct_fields_builder_finalize(vx_struct_fields_builder *bu
17931761
#ifdef __cplusplus
17941762
} // extern "C"
17951763
#endif // __cplusplus
1764+
1765+
#include <string.h>
1766+
1767+
/**
1768+
* Create a view over a null-terminated C string.
1769+
* View is valid as long as "str" is valid
1770+
*/
1771+
static inline vx_view vx_view_from_cstr(const char *str) {
1772+
vx_view s;
1773+
s.ptr = str;
1774+
s.len = strlen(str);
1775+
return s;
1776+
}

0 commit comments

Comments
 (0)