From f1eecf9786a0c18fd659eb8b392f172f12e3b586 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Thu, 22 Feb 2024 09:13:59 -0500 Subject: [PATCH] feat: expose the allocator and array header files for external scanners --- cli/build.rs | 13 +++ cli/dynamic-symbols-darwin.txt | 4 + cli/dynamic-symbols.txt | 6 ++ cli/loader/src/lib.rs | 13 ++- cli/src/generate/mod.rs | 2 + cli/src/generate/templates/alloc.h | 60 ++++++++++++ cli/src/tests/helpers/fixtures.rs | 19 ++++ docs/section-3-creating-parsers.md | 110 ++++++++++++++++++++- lib/binding_rust/lib.rs | 2 + lib/src/alloc.c | 8 +- lib/src/alloc.h | 20 ++-- lib/src/array.h | 151 +++++++++++++++++------------ 12 files changed, 326 insertions(+), 82 deletions(-) create mode 100644 cli/dynamic-symbols-darwin.txt create mode 100644 cli/dynamic-symbols.txt create mode 100644 cli/src/generate/templates/alloc.h diff --git a/cli/build.rs b/cli/build.rs index bbd9e84c..67f9c21d 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -20,6 +20,19 @@ fn main() { .unwrap() .as_secs_f64(); println!("cargo:rustc-env=BUILD_TIME={build_time}"); + + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly", + ))] + println!("cargo:rustc-link-arg=-Wl,--dynamic-list=cli/dynamic-symbols.txt"); + + #[cfg(any(target_os = "macos", target_os = "ios"))] + println!("cargo:rustc-link-arg=-Wl,-exported_symbols_list,cli/dynamic-symbols-darwin.txt"); } fn web_playground_files_present() -> bool { diff --git a/cli/dynamic-symbols-darwin.txt b/cli/dynamic-symbols-darwin.txt new file mode 100644 index 00000000..1b4f3c68 --- /dev/null +++ b/cli/dynamic-symbols-darwin.txt @@ -0,0 +1,4 @@ +_ts_current_malloc +_ts_current_calloc +_ts_current_realloc +_ts_current_free diff --git a/cli/dynamic-symbols.txt b/cli/dynamic-symbols.txt new file mode 100644 index 00000000..3c1ecc36 --- /dev/null +++ b/cli/dynamic-symbols.txt @@ -0,0 +1,6 @@ +{ + ts_current_malloc; + ts_current_calloc; + ts_current_realloc; + ts_current_free; +}; diff --git a/cli/loader/src/lib.rs b/cli/loader/src/lib.rs index 7d22b874..f859f861 100644 --- a/cli/loader/src/lib.rs +++ b/cli/loader/src/lib.rs @@ -529,6 +529,7 @@ impl Loader { .cpp(true) .opt_level(2) .cargo_metadata(false) + .cargo_warnings(false) .target(BUILD_TARGET) .host(BUILD_TARGET) .flag_if_supported("-Werror=implicit-function-declaration"); @@ -584,10 +585,6 @@ impl Loader { command.arg("-O2"); } - // For conditional compilation of external scanner code when - // used internally by `tree-siteer parse` and other sub commands. - command.arg("-DTREE_SITTER_INTERNAL_BUILD"); - if let Some(scanner_path) = scanner_path.as_ref() { if scanner_path.extension() == Some("c".as_ref()) { command.arg("-xc").arg("-std=c99").arg(scanner_path); @@ -599,6 +596,14 @@ impl Loader { command.arg("-xc").arg(parser_path); } + // For conditional compilation of external scanner code when + // used internally by `tree-sitter parse` and other sub commands. + command.arg("-DTREE_SITTER_INTERNAL_BUILD"); + + // Always use the same allocator in the CLI as any scanner, useful for debugging and + // tracking memory leaks in tests. + command.arg("-DTS_REUSE_ALLOCATOR"); + let output = command.output().with_context(|| { format!("Failed to execute the C compiler with the following command:\n{command:?}") })?; diff --git a/cli/src/generate/mod.rs b/cli/src/generate/mod.rs index 8fb39a39..21c9555e 100644 --- a/cli/src/generate/mod.rs +++ b/cli/src/generate/mod.rs @@ -122,6 +122,8 @@ pub fn generate_parser_in_directory( write_file(&src_path.join("parser.c"), c_code)?; write_file(&src_path.join("node-types.json"), node_types_json)?; + write_file(&header_path.join("alloc.h"), tree_sitter::ALLOC_HEADER)?; + write_file(&header_path.join("array.h"), tree_sitter::ARRAY_HEADER)?; write_file(&header_path.join("parser.h"), tree_sitter::PARSER_HEADER)?; if !path_in_ignore(&repo_path) { diff --git a/cli/src/generate/templates/alloc.h b/cli/src/generate/templates/alloc.h new file mode 100644 index 00000000..1f7610a3 --- /dev/null +++ b/cli/src/generate/templates/alloc.h @@ -0,0 +1,60 @@ +#ifndef TREE_SITTER_ALLOC_H_ +#define TREE_SITTER_ALLOC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifdef _WIN32 +#define TS_PUBLIC __declspec(dllexport) +#else +#define TS_PUBLIC __attribute__((visibility("default"))) +#endif + +TS_PUBLIC extern void *(*ts_current_malloc)(size_t); +TS_PUBLIC extern void *(*ts_current_calloc)(size_t, size_t); +TS_PUBLIC extern void *(*ts_current_realloc)(void *, size_t); +TS_PUBLIC extern void (*ts_current_free)(void *); + +// Allow clients to override allocation functions +#ifdef TS_REUSE_ALLOCATOR + +#ifndef ts_malloc +#define ts_malloc ts_current_malloc +#endif +#ifndef ts_calloc +#define ts_calloc ts_current_calloc +#endif +#ifndef ts_realloc +#define ts_realloc ts_current_realloc +#endif +#ifndef ts_free +#define ts_free ts_current_free +#endif + +#else + +#ifndef ts_malloc +#define ts_malloc malloc +#endif +#ifndef ts_calloc +#define ts_calloc calloc +#endif +#ifndef ts_realloc +#define ts_realloc realloc +#endif +#ifndef ts_free +#define ts_free free +#endif + +#endif + +#ifdef __cplusplus +} +#endif + +#endif // TREE_SITTER_ALLOC_H_ diff --git a/cli/src/tests/helpers/fixtures.rs b/cli/src/tests/helpers/fixtures.rs index 98daf80f..bad016c4 100644 --- a/cli/src/tests/helpers/fixtures.rs +++ b/cli/src/tests/helpers/fixtures.rs @@ -107,6 +107,25 @@ pub fn get_test_language(name: &str, parser_code: &str, path: Option<&Path>) -> let header_path = src_dir.join("tree_sitter"); fs::create_dir_all(&header_path).unwrap(); + + fs::write(header_path.join("alloc.h"), tree_sitter::PARSER_HEADER) + .with_context(|| { + format!( + "Failed to write {:?}", + header_path.join("alloc.h").file_name().unwrap() + ) + }) + .unwrap(); + + fs::write(header_path.join("array.h"), tree_sitter::PARSER_HEADER) + .with_context(|| { + format!( + "Failed to write {:?}", + header_path.join("array.h").file_name().unwrap() + ) + }) + .unwrap(); + fs::write(header_path.join("parser.h"), tree_sitter::PARSER_HEADER) .with_context(|| { format!( diff --git a/docs/section-3-creating-parsers.md b/docs/section-3-creating-parsers.md index 714864e4..2b42c6e7 100644 --- a/docs/section-3-creating-parsers.md +++ b/docs/section-3-creating-parsers.md @@ -112,6 +112,8 @@ The first time you run `tree-sitter generate`, it will also generate a few other * `bindings/c/tree-sitter-language.h` - This file provides the C interface of your language. * `bindings/c/tree-sitter-language.pc` - This file provides pkg-config metadata about your language's C library. * `src/tree_sitter/parser.h` - This file provides some basic C definitions that are used in your generated `parser.c` file. +* `src/tree_sitter/alloc.h` - This file provides some memory allocation macros that are to be used in your external scanner, if you have one. +* `src/tree_sitter/array.h` - This file provides some array macros that are to be used in your external scanner, if you have one. #### Go @@ -144,7 +146,6 @@ The first time you run `tree-sitter generate`, it will also generate a few other * `Package.swift` - This file tells Swift how to compile your language. * `bindings/swift/TreeSitterLanguage/language.h` - This file wraps your language in a Swift module when used in Swift. - If there is an ambiguity or *local ambiguity* in your grammar, Tree-sitter will detect it during parser generation, and it will exit with a `Unresolved conflict` error message. See below for more information on these errors. ### Command: `test` @@ -690,7 +691,9 @@ Then, add another C or C++ source file to your project. Currently, its path must In this new source file, define an [`enum`][enum] type containing the names of all of your external tokens. The ordering of this enum must match the order in your grammar's `externals` array; the actual names do not matter. ```c -#include +#include "tree_sitter/parser.h" +#include "tree_sitter/alloc.h" +#include "tree_sitter/array.h" enum TokenType { INDENT, @@ -790,6 +793,109 @@ if (valid_symbols[INDENT] || valid_symbols[DEDENT]) { } ``` +#### External Scanner Helpers + +##### Allocator + +Instead of using libc's `malloc`, `calloc`, `realloc`, and `free`, you should use the versions prefixed with `ts_` from `tree_sitter/alloc.h`. +These macros can allow a potential consumer to override the default allocator with their own implementation, but by default will use the libc functions. + +As a consumer of the tree-sitter core library as well as any parser libraries that might use allocations, you can enable overriding the default allocator and have it use the same one as the library allocator, of which you can set with `ts_set_allocator`. +To enable this overriding in scanners, you must compile them with the `TS_REUSE_ALLOCATOR` macro defined, and tree-sitter the library must be linked into your final app dynamically, since it needs to resolve the internal functions at runtime. If you are compiling +an executable binary that uses the core library, but want to load parsers dynamically at runtime, then you will have to use a special linker flag on Unix. For non-Darwin systems, that would be `--dynamic-list` and for Darwin systems, that would be `-exported_symbols_list`. +The CLI does exactly this, so you can use it as a reference (check out `cli/build.rs`). + +For example, assuming you wanted to allocate 100 bytes for your scanner, you'd do so like the following example: + +```c +#include "tree_sitter/parser.h" +#include "tree_sitter/alloc.h" +#include "tree_sitter/array.h" + +// ... + +void* tree_sitter_my_language_external_scanner_create() { + return ts_calloc(100, 1); // or ts_malloc(100) +} + +// ... + +``` + +##### Arrays + +If you need to use array-like types in your scanner, such as tracking a stack of indentations or tags, you should use the array macros from `tree_sitter/array.h`. + +There are quite a few of them provided for you, but here's how you could get started tracking some . Check out the header itself for more detailed documentation. + +**NOTE**: Do not use any of the array functions or macros that are prefixed with an underscore and have comments saying that it is not what you are looking for. +These are internal functions used as helpers by other macros that are public. They are not meant to be used directly, nor are they what you want. + +```c +#include "tree_sitter/parser.h" +#include "tree_sitter/alloc.h" +#include "tree_sitter/array.h" + +enum TokenType { + INDENT, + DEDENT, + NEWLINE, + STRING, +} + +// Create the array in your create function + +void* tree_sitter_my_language_external_scanner_create() { + return ts_calloc(1, sizeof(Array(int))); + + // or if you want to zero out the memory yourself + + Array(int) *stack = ts_malloc(sizeof(Array(int))); + array_init(&stack); + return stack; +} + +bool tree_sitter_my_language_external_scanner_scan( + void *payload, + TSLexer *lexer, + const bool *valid_symbols +) { + Array(int) *stack = payload; + if (valid_symbols[INDENT]) { + array_push(stack, lexer->get_column(lexer)); + lexer->result_symbol = INDENT; + return true; + } + if (valid_symbols[DEDENT]) { + array_pop(stack); // this returns the popped element by value, but we don't need it + lexer->result_symbol = DEDENT; + return true; + } + + // we can also use an array on the stack to keep track of a string + + Array(char) next_string = array_new(); + + if (valid_symbols[STRING] && lexer->lookahead == '"') { + lexer->advance(lexer, false); + while (lexer->lookahead != '"' && lexer->lookahead != '\n' && !lexer->eof(lexer)) { + array_push(&next_string, lexer->lookahead); + lexer->advance(lexer, false); + } + + // assume we have some arbitrary constraint of not having more than 100 characters in a string + if (lexer->lookahead == '"' && next_string.size <= 100) { + lexer->advance(lexer, false); + lexer->result_symbol = STRING; + return true; + } + } + + return false; +} + +``` + #### Other External Scanner Details If a token in the `externals` array is valid at a given position in the parse, the external scanner will be called first before anything else is done. This means the external scanner functions as a powerful override of Tree-sitter's lexing behavior, and can be used to solve problems that can't be cracked with ordinary lexical, parse, or dynamic precedence. diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index 56ab8027..65bed75b 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -44,6 +44,8 @@ pub const LANGUAGE_VERSION: usize = ffi::TREE_SITTER_LANGUAGE_VERSION as usize; pub const MIN_COMPATIBLE_LANGUAGE_VERSION: usize = ffi::TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION as usize; +pub const ALLOC_HEADER: &str = include_str!("../../cli/src/generate/templates/alloc.h"); +pub const ARRAY_HEADER: &str = include_str!("../src/array.h"); pub const PARSER_HEADER: &str = include_str!("../src/parser.h"); /// An opaque object that defines how to parse a particular language. The code for each diff --git a/lib/src/alloc.c b/lib/src/alloc.c index a6adaf5e..a8c1bbac 100644 --- a/lib/src/alloc.c +++ b/lib/src/alloc.c @@ -29,10 +29,10 @@ static void *ts_realloc_default(void *buffer, size_t size) { } // Allow clients to override allocation functions dynamically -void *(*ts_current_malloc)(size_t) = ts_malloc_default; -void *(*ts_current_calloc)(size_t, size_t) = ts_calloc_default; -void *(*ts_current_realloc)(void *, size_t) = ts_realloc_default; -void (*ts_current_free)(void *) = free; +TS_PUBLIC void *(*ts_current_malloc)(size_t) = ts_malloc_default; +TS_PUBLIC void *(*ts_current_calloc)(size_t, size_t) = ts_calloc_default; +TS_PUBLIC void *(*ts_current_realloc)(void *, size_t) = ts_realloc_default; +TS_PUBLIC void (*ts_current_free)(void *) = free; void ts_set_allocator( void *(*new_malloc)(size_t size), diff --git a/lib/src/alloc.h b/lib/src/alloc.h index c51f84ab..037903a1 100644 --- a/lib/src/alloc.h +++ b/lib/src/alloc.h @@ -1,20 +1,24 @@ #ifndef TREE_SITTER_ALLOC_H_ #define TREE_SITTER_ALLOC_H_ -#include "tree_sitter/api.h" - #ifdef __cplusplus extern "C" { #endif -#include #include #include +#include -extern void *(*ts_current_malloc)(size_t); -extern void *(*ts_current_calloc)(size_t, size_t); -extern void *(*ts_current_realloc)(void *, size_t); -extern void (*ts_current_free)(void *); +#ifdef _WIN32 +#define TS_PUBLIC __declspec(dllexport) +#else +#define TS_PUBLIC __attribute__((visibility("default"))) +#endif + +TS_PUBLIC extern void *(*ts_current_malloc)(size_t); +TS_PUBLIC extern void *(*ts_current_calloc)(size_t, size_t); +TS_PUBLIC extern void *(*ts_current_realloc)(void *, size_t); +TS_PUBLIC extern void (*ts_current_free)(void *); // Allow clients to override allocation functions #ifndef ts_malloc @@ -34,4 +38,4 @@ extern void (*ts_current_free)(void *); } #endif -#endif // TREE_SITTER_ALLOC_H_ +#endif // TREE_SITTER_ALLOC_H_ diff --git a/lib/src/array.h b/lib/src/array.h index e026f6b2..43358fcc 100644 --- a/lib/src/array.h +++ b/lib/src/array.h @@ -5,12 +5,13 @@ extern "C" { #endif -#include -#include -#include +#include "./alloc.h" + #include #include -#include "./alloc.h" +#include +#include +#include #define Array(T) \ struct { \ @@ -19,94 +20,108 @@ extern "C" { uint32_t capacity; \ } +/// Initialize an array. #define array_init(self) \ ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) +/// Create an empty array. #define array_new() \ { NULL, 0, 0 } +/// Get a pointer to the element at a given `index` in the array. #define array_get(self, _index) \ (assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index]) +/// Get a pointer to the first element in the array. #define array_front(self) array_get(self, 0) +/// Get a pointer to the last element in the array. #define array_back(self) array_get(self, (self)->size - 1) +/// Clear the array, setting its size to zero. Note that this does not free any +/// memory allocated for the array's contents. #define array_clear(self) ((self)->size = 0) +/// Reserve `new_capacity` elements of space in the array. If `new_capacity` is +/// less than the array's current capacity, this function has no effect. #define array_reserve(self, new_capacity) \ - array__reserve((VoidArray *)(self), array__elem_size(self), new_capacity) + _array__reserve((Array *)(self), array_elem_size(self), new_capacity) -// Free any memory allocated for this array. -#define array_delete(self) array__delete((VoidArray *)(self)) +/// Free any memory allocated for this array. Note that this does not free any +/// memory allocated for the array's contents. +#define array_delete(self) _array__delete((Array *)(self)) +/// Push a new `element` onto the end of the array. #define array_push(self, element) \ - (array__grow((VoidArray *)(self), 1, array__elem_size(self)), \ + (_array__grow((Array *)(self), 1, array_elem_size(self)), \ (self)->contents[(self)->size++] = (element)) -// Increase the array's size by a given number of elements, reallocating -// if necessary. New elements are zero-initialized. +/// Increase the array's size by `count` elements. +/// New elements are zero-initialized. #define array_grow_by(self, count) \ - (array__grow((VoidArray *)(self), count, array__elem_size(self)), \ - memset((self)->contents + (self)->size, 0, (count) * array__elem_size(self)), \ + (_array__grow((Array *)(self), count, array_elem_size(self)), \ + memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)), \ (self)->size += (count)) +/// Append all elements from one array to the end of another. #define array_push_all(self, other) \ array_extend((self), (other)->size, (other)->contents) -// Append `count` elements to the end of the array, reading their values from the -// `contents` pointer. +/// Append `count` elements to the end of the array, reading their values from the +/// `contents` pointer. #define array_extend(self, count, contents) \ - array__splice( \ - (VoidArray *)(self), array__elem_size(self), (self)->size, \ + _array__splice( \ + (Array *)(self), array_elem_size(self), (self)->size, \ 0, count, contents \ ) -// Remove `old_count` elements from the array starting at the given `index`. At -// the same index, insert `new_count` new elements, reading their values from the -// `new_contents` pointer. +/// Remove `old_count` elements from the array starting at the given `index`. At +/// the same index, insert `new_count` new elements, reading their values from the +/// `new_contents` pointer. #define array_splice(self, _index, old_count, new_count, new_contents) \ - array__splice( \ - (VoidArray *)(self), array__elem_size(self), _index, \ + _array__splice( \ + (Array *)(self), array_elem_size(self), _index, \ old_count, new_count, new_contents \ ) -// Insert one `element` into the array at the given `index`. +/// Insert one `element` into the array at the given `index`. #define array_insert(self, _index, element) \ - array__splice((VoidArray *)(self), array__elem_size(self), _index, 0, 1, &(element)) + _array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element)) -// Remove one `element` from the array at the given `index`. +/// Remove one element from the array at the given `index`. #define array_erase(self, _index) \ - array__erase((VoidArray *)(self), array__elem_size(self), _index) + _array__erase((Array *)(self), array_elem_size(self), _index) +/// Pop the last element off the array, returning the element by value. #define array_pop(self) ((self)->contents[--(self)->size]) +/// Assign the contents of one array to another, reallocating if necessary. #define array_assign(self, other) \ - array__assign((VoidArray *)(self), (const VoidArray *)(other), array__elem_size(self)) + _array__assign((Array *)(self), (const Array *)(other), array_elem_size(self)) #define array_swap(self, other) \ - array__swap((VoidArray *)(self), (VoidArray *)(other)) + _array__swap((Array *)(self), (Array *)(other)) -// Search a sorted array for a given `needle` value, using the given `compare` -// callback to determine the order. -// -// If an existing element is found to be equal to `needle`, then the `index` -// out-parameter is set to the existing value's index, and the `exists` -// out-parameter is set to true. Otherwise, `index` is set to an index where -// `needle` should be inserted in order to preserve the sorting, and `exists` -// is set to false. +/// Search a sorted array for a given `needle` value, using the given `compare` +/// callback to determine the order. +/// +/// If an existing element is found to be equal to `needle`, then the `index` +/// out-parameter is set to the existing value's index, and the `exists` +/// out-parameter is set to true. Otherwise, `index` is set to an index where +/// `needle` should be inserted in order to preserve the sorting, and `exists` +/// is set to false. #define array_search_sorted_with(self, compare, needle, _index, _exists) \ - array__search_sorted(self, 0, compare, , needle, _index, _exists) + _array__search_sorted(self, 0, compare, , needle, _index, _exists) -// Search a sorted array for a given `needle` value, using integer comparisons -// of a given struct field (specified with a leading dot) to determine the order. -// -// See also `array_search_sorted_with`. +/// Search a sorted array for a given `needle` value, using integer comparisons +/// of a given struct field (specified with a leading dot) to determine the order. +/// +/// See also `array_search_sorted_with`. #define array_search_sorted_by(self, field, needle, _index, _exists) \ - array__search_sorted(self, 0, compare_int, field, needle, _index, _exists) + _array__search_sorted(self, 0, compare_int, field, needle, _index, _exists) -// Insert a given `value` into a sorted array, using the given `compare` -// callback to determine the order. +/// Insert a given `value` into a sorted array, using the given `compare` +/// callback to determine the order. #define array_insert_sorted_with(self, compare, value) \ do { \ unsigned _index, _exists; \ @@ -114,10 +129,10 @@ extern "C" { if (!_exists) array_insert(self, _index, value); \ } while (0) -// Insert a given `value` into a sorted array, using integer comparisons of -// a given struct field (specified with a leading dot) to determine the order. -// -// See also `array_search_sorted_by`. +/// Insert a given `value` into a sorted array, using integer comparisons of +/// a given struct field (specified with a leading dot) to determine the order. +/// +/// See also `array_search_sorted_by`. #define array_insert_sorted_by(self, field, value) \ do { \ unsigned _index, _exists; \ @@ -127,11 +142,12 @@ extern "C" { // Private -typedef Array(void) VoidArray; +typedef Array(void) Array; -#define array__elem_size(self) sizeof(*(self)->contents) +#define array_elem_size(self) sizeof(*(self)->contents) -static inline void array__delete(VoidArray *self) { +/// This is not what you're looking for, see `array_delete`. +static inline void _array__delete(Array *self) { if (self->contents) { ts_free(self->contents); self->contents = NULL; @@ -140,7 +156,8 @@ static inline void array__delete(VoidArray *self) { } } -static inline void array__erase(VoidArray *self, size_t element_size, +/// This is not what you're looking for, see `array_erase`. +static inline void _array__erase(Array *self, size_t element_size, uint32_t index) { assert(index < self->size); char *contents = (char *)self->contents; @@ -149,7 +166,8 @@ static inline void array__erase(VoidArray *self, size_t element_size, self->size--; } -static inline void array__reserve(VoidArray *self, size_t element_size, uint32_t new_capacity) { +/// This is not what you're looking for, see `array_reserve`. +static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) { if (new_capacity > self->capacity) { if (self->contents) { self->contents = ts_realloc(self->contents, new_capacity * element_size); @@ -160,29 +178,33 @@ static inline void array__reserve(VoidArray *self, size_t element_size, uint32_t } } -static inline void array__assign(VoidArray *self, const VoidArray *other, size_t element_size) { - array__reserve(self, element_size, other->size); +/// This is not what you're looking for, see `array_assign`. +static inline void _array__assign(Array *self, const Array *other, size_t element_size) { + _array__reserve(self, element_size, other->size); self->size = other->size; memcpy(self->contents, other->contents, self->size * element_size); } -static inline void array__swap(VoidArray *self, VoidArray *other) { - VoidArray swap = *other; +/// This is not what you're looking for, see `array_swap`. +static inline void _array__swap(Array *self, Array *other) { + Array swap = *other; *other = *self; *self = swap; } -static inline void array__grow(VoidArray *self, uint32_t count, size_t element_size) { +/// This is not what you're looking for, see `array_push` or `array_grow_by`. +static inline void _array__grow(Array *self, uint32_t count, size_t element_size) { uint32_t new_size = self->size + count; if (new_size > self->capacity) { uint32_t new_capacity = self->capacity * 2; if (new_capacity < 8) new_capacity = 8; if (new_capacity < new_size) new_capacity = new_size; - array__reserve(self, element_size, new_capacity); + _array__reserve(self, element_size, new_capacity); } } -static inline void array__splice(VoidArray *self, size_t element_size, +/// This is not what you're looking for, see `array_splice`. +static inline void _array__splice(Array *self, size_t element_size, uint32_t index, uint32_t old_count, uint32_t new_count, const void *elements) { uint32_t new_size = self->size + new_count - old_count; @@ -190,7 +212,7 @@ static inline void array__splice(VoidArray *self, size_t element_size, uint32_t new_end = index + new_count; assert(old_end <= self->size); - array__reserve(self, element_size, new_size); + _array__reserve(self, element_size, new_size); char *contents = (char *)self->contents; if (self->size > old_end) { @@ -218,8 +240,9 @@ static inline void array__splice(VoidArray *self, size_t element_size, self->size += new_count - old_count; } -// A binary search routine, based on Rust's `std::slice::binary_search_by`. -#define array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \ +/// A binary search routine, based on Rust's `std::slice::binary_search_by`. +/// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`. +#define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \ do { \ *(_index) = start; \ *(_exists) = false; \ @@ -238,8 +261,8 @@ static inline void array__splice(VoidArray *self, size_t element_size, else if (comparison < 0) *(_index) += 1; \ } while (0) -// Helper macro for the `_sorted_by` routines below. This takes the left (existing) -// parameter by reference in order to work with the generic sorting function above. +/// Helper macro for the `_sorted_by` routines below. This takes the left (existing) +/// parameter by reference in order to work with the generic sorting function above. #define compare_int(a, b) ((int)*(a) - (int)(b)) #ifdef __cplusplus