From f08719390b5efa683f7ec767f94dc6e891f5ac69 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 15:11:43 +0200 Subject: [PATCH 01/11] Add initial Makefile and GitHub workflow; update .gitignore; include additional headers in sqlite-vector.c --- .github/workflows/main.yml | 171 +++++++++++++++++++++++++++++++++++++ .gitignore | 9 ++ Makefile | 142 ++++++++++++++++++++++++++++++ src/sqlite-vector.c | 1 + 4 files changed, 323 insertions(+) create mode 100644 .github/workflows/main.yml create mode 100644 Makefile diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..3bef3d7 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,171 @@ +name: build, test and release sqlite-vector +on: + push: + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + runs-on: ${{ matrix.os }} + name: ${{ matrix.name }}${{ matrix.arch && format('-{0}', matrix.arch) || '' }} build${{ matrix.arch != 'arm64-v8a' && matrix.name != 'isim' && matrix.name != 'ios' && ' + test' || ''}} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + arch: x86_64 + name: linux + - os: LinuxARM64 + arch: arm64 + name: linux + - os: macos-latest + name: macos + - os: windows-latest + arch: x86_64 + name: windows + - os: ubuntu-latest + arch: arm64-v8a + name: android + make: PLATFORM=android ARCH=arm64-v8a + - os: ubuntu-latest + arch: x86_64 + name: android + make: PLATFORM=android ARCH=x86_64 + sqlite-amalgamation-zip: https://sqlite.org/2025/sqlite-amalgamation-3490100.zip + - os: macos-latest + name: ios + make: PLATFORM=ios + - os: macos-latest + name: isim + make: PLATFORM=isim + + defaults: + run: + shell: bash + + steps: + + - uses: actions/checkout@v4.2.2 + + - name: build sqlite-vector + run: make ${{ matrix.make && matrix.make || ''}} + + - name: windows install sqlite3 + if: matrix.os == 'windows-latest' + run: choco install sqlite -y + + - name: macos install sqlite3 without SQLITE_OMIT_LOAD_EXTENSION + if: matrix.name == 'macos' + run: brew link sqlite --force + + - name: android setup test environment + if: matrix.name == 'android' && matrix.arch != 'arm64-v8a' + run: | + + echo "::group::enable kvm group perms" + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + echo "::endgroup::" + + echo "::group::download and build sqlite3 without SQLITE_OMIT_LOAD_EXTENSION" + curl -O ${{ matrix.sqlite-amalgamation-zip }} + unzip sqlite-amalgamation-*.zip + export ${{ matrix.make }} + $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.arch }}-linux-android26-clang sqlite-amalgamation-*/shell.c sqlite-amalgamation-*/sqlite3.c -o sqlite3 -ldl + # remove unused folders to save up space + rm -rf sqlite-amalgamation-*.zip sqlite-amalgamation-* openssl + echo "::endgroup::" + + echo "::group::prepare the test script" + make test PLATFORM=$PLATFORM ARCH=$ARCH || echo "It should fail. Running remaining commands in the emulator" + cat > commands.sh << EOF + mv -f /data/local/tmp/sqlite3 /system/xbin + cd /data/local/tmp + $(make test PLATFORM=$PLATFORM ARCH=$ARCH -n) + EOF + echo "::endgroup::" + + - name: android test sqlite-vector + if: matrix.name == 'android' && matrix.arch != 'arm64-v8a' + uses: reactivecircus/android-emulator-runner@v2.34.0 + with: + api-level: 26 + arch: ${{ matrix.arch }} + script: | + adb root + adb remount + adb push ${{ github.workspace }}/. /data/local/tmp/ + adb shell "sh /data/local/tmp/commands.sh" + + - name: test sqlite-vector + if: matrix.name == 'linux' || matrix.name == 'windows' || matrix.name == 'macos' + run: make test + + - uses: actions/upload-artifact@v4.6.2 + if: always() + with: + name: vector-${{ matrix.name }}${{ matrix.arch && format('-{0}', matrix.arch) || '' }} + path: dist/vector.* + if-no-files-found: error + + release: + runs-on: ubuntu-latest + name: release + needs: build + if: github.ref == 'refs/heads/main' + + env: + GH_TOKEN: ${{ github.token }} + + steps: + + - uses: actions/checkout@v4.2.2 + + - uses: actions/download-artifact@v4.2.1 + with: + path: artifacts + + - name: release tag version from sqlite-vector.h + id: tag + run: | + FILE="src/sqlite-vector.h" + VERSION=$(grep -oP '#define SQLITE_VECTOR_VERSION\s+"\K[^"]+' "$FILE") + if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + LATEST=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.name') + if [[ "$VERSION" != "$LATEST" || "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then + echo "version=$VERSION" >> $GITHUB_OUTPUT + else + echo "::warning file=src/sqlite-vector.h::To release a new version, please update the SQLITE_VECTOR_VERSION in src/sqlite-vector.h to be different than the latest $LATEST" + fi + exit 0 + fi + echo "❌ SQLITE_VECTOR_VERSION not found in sqlite-vector.h" + exit 1 + + - name: zip artifacts + run: | + for folder in "artifacts"/*; do + if [ -d "$folder" ]; then + name=$(basename "$folder") + if [[ "$name" != "github-pages" ]]; then + zip -jq "${name}-${{ steps.tag.outputs.version }}.zip" "$folder"/* + tar -cJf "${name}-${{ steps.tag.outputs.version }}.tar.xz" -C "$folder" . + tar -czf "${name}-${{ steps.tag.outputs.version }}.tar.gz" -C "$folder" . + fi + fi + done + + - uses: softprops/action-gh-release@v2.2.1 + if: steps.tag.outputs.version != '' + with: + generate_release_notes: true + tag_name: ${{ steps.tag.outputs.version }} + files: | + vector-*-${{ steps.tag.outputs.version }}.zip + vector-*-${{ steps.tag.outputs.version }}.tar.xz + vector-*-${{ steps.tag.outputs.version }}.tar.gz + make_latest: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index e43b0f9..06667c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ .DS_Store +*.xcworkspacedata +*.xcuserstate +*.xcbkptlist +*.plist +/build +/dist +*.sqlite +*.a +.vscode \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7c6d611 --- /dev/null +++ b/Makefile @@ -0,0 +1,142 @@ +# Makefile for SQLite Vector Extension +# Supports compilation for Linux, macOS, Windows, Android and iOS + +# customize sqlite3 executable with +# make test SQLITE3=/opt/homebrew/Cellar/sqlite/3.49.1/bin/sqlite3 +SQLITE3 ?= sqlite3 + +# Set default platform if not specified +ifeq ($(OS),Windows_NT) + PLATFORM := windows + HOST := windows + CPUS := $(shell powershell -Command "[Environment]::ProcessorCount") +else + HOST = $(shell uname -s | tr '[:upper:]' '[:lower:]') + ifeq ($(HOST),darwin) + PLATFORM := macos + CPUS := $(shell sysctl -n hw.ncpu) + else + PLATFORM := $(HOST) + CPUS := $(shell nproc) + endif +endif + +# Speed up builds by using all available CPU cores +MAKEFLAGS += -j$(CPUS) + +# Compiler and flags +CC = gcc +CFLAGS = -Wall -Wextra -Wno-unused-parameter -I$(SRC_DIR) -I$(LIB_DIR) -DSQLITE_CORE + +# Directories +SRC_DIR = src +DIST_DIR = dist +LIB_DIR = libs +VPATH = $(SRC_DIR):$(LIB_DIR) +BUILD_DIR = build + +# Files +SRC_FILES = $(wildcard $(SRC_DIR)/*.c) +OBJ_FILES = $(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SRC_FILES))) + +# Platform-specific settings +ifeq ($(PLATFORM),windows) + TARGET := $(DIST_DIR)/vector.dll + LDFLAGS += -shared + # Create .def file for Windows + DEF_FILE := $(BUILD_DIR)/vector.def +else ifeq ($(PLATFORM),macos) + TARGET := $(DIST_DIR)/vector.dylib + LDFLAGS += -arch x86_64 -arch arm64 -dynamiclib -undefined dynamic_lookup + CFLAGS += -arch x86_64 -arch arm64 +else ifeq ($(PLATFORM),android) + # Set ARCH to find Android NDK's Clang compiler, the user should set the ARCH + ifeq ($(filter %,$(ARCH)),) + $(error "Android ARCH must be set to ARCH=x86_64 or ARCH=arm64-v8a") + endif + # Set ANDROID_NDK path to find android build tools + # e.g. on MacOS: export ANDROID_NDK=/Users/username/Library/Android/sdk/ndk/25.2.9519653 + ifeq ($(filter %,$(ANDROID_NDK)),) + $(error "Android NDK must be set") + endif + + BIN = $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(HOST)-x86_64/bin + PATH := $(BIN):$(PATH) + + ifneq (,$(filter $(ARCH),arm64 arm64-v8a)) + override ARCH := aarch64 + endif + + CC = $(BIN)/$(ARCH)-linux-android26-clang + TARGET := $(DIST_DIR)/vector.so + LDFLAGS += -shared +else ifeq ($(PLATFORM),ios) + TARGET := $(DIST_DIR)/vector.dylib + SDK := -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=11.0 + LDFLAGS += -dynamiclib $(SDK) + CFLAGS += -arch arm64 $(SDK) +else ifeq ($(PLATFORM),isim) + TARGET := $(DIST_DIR)/vector.dylib + SDK := -isysroot $(shell xcrun --sdk iphonesimulator --show-sdk-path) -miphonesimulator-version-min=11.0 + LDFLAGS += -arch x86_64 -arch arm64 -dynamiclib $(SDK) + CFLAGS += -arch x86_64 -arch arm64 $(SDK) +else # linux + TARGET := $(DIST_DIR)/vector.so + LDFLAGS += -shared +endif + +# Windows .def file generation +$(DEF_FILE): +ifeq ($(PLATFORM),windows) + @echo "LIBRARY vector.dll" > $@ + @echo "EXPORTS" >> $@ + @echo " sqlite3_vector_init" >> $@ +endif + +# Make sure the build and dist directories exist +$(shell mkdir -p $(BUILD_DIR) $(DIST_DIR)) + +# Default target +extension: $(TARGET) +all: $(TARGET) + +# Loadable library +$(TARGET): $(OBJ_FILES) $(DEF_FILE) + $(CC) $(OBJ_FILES) $(DEF_FILE) -o $@ $(LDFLAGS) +ifeq ($(PLATFORM),windows) + # Generate import library for Windows + dlltool -D $@ -d $(DEF_FILE) -l $(DIST_DIR)/vector.lib +endif + +# Object files +$(BUILD_DIR)/%.o: %.c + $(CC) $(CFLAGS) -O3 -fPIC -c $< -o $@ + +test: $(TARGET) + $(SQLITE3) ":memory:" -cmd ".bail on" ".load ./$<" "SELECT vector_version();" + +# Clean up generated files +clean: + rm -rf $(BUILD_DIR)/* $(DIST_DIR)/* *.gcda *.gcno *.gcov *.sqlite + +# Help message +help: + @echo "SQLite Vector Extension Makefile" + @echo "Usage:" + @echo " make [PLATFORM=platform] [ARCH=arch] [ANDROID_NDK=\$$ANDROID_HOME/ndk/26.1.10909125] [target]" + @echo "" + @echo "Platforms:" + @echo " linux (default on Linux)" + @echo " macos (default on macOS)" + @echo " windows (default on Windows)" + @echo " android (needs ARCH to be set to x86_64 or arm64-v8a and ANDROID_NDK to be set)" + @echo " ios (only on macOS)" + @echo " isim (only on macOS)" + @echo "" + @echo "Targets:" + @echo " all - Build the extension (default)" + @echo " clean - Remove built files" + @echo " test - Test the extension" + @echo " help - Display this help message" + +.PHONY: all clean test extension help diff --git a/src/sqlite-vector.c b/src/sqlite-vector.c index accb06a..a0b9302 100644 --- a/src/sqlite-vector.c +++ b/src/sqlite-vector.c @@ -18,6 +18,7 @@ #include #include #include +#include #define DEBUG_VECTOR_ALWAYS(...) do {printf(__VA_ARGS__ );printf("\n");} while (0) From 186f264f42e48c73d9d60ef261b68261c66f6a25 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 15:37:40 +0200 Subject: [PATCH 02/11] Test fix type for difference calculations in uint8_distance_l2_impl_neon to use uint16x8_t --- src/distance-neon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/distance-neon.c b/src/distance-neon.c index 1510a3e..56e4338 100644 --- a/src/distance-neon.c +++ b/src/distance-neon.c @@ -155,8 +155,8 @@ static inline float uint8_distance_l2_impl_neon(const void *v1, const void *v2, uint8x16_t vb = vld1q_u8(b + i); // compute 8-bit differences widened to signed 16-bit - int16x8_t diff_lo = vsubl_u8(vget_low_u8(va), vget_low_u8(vb)); - int16x8_t diff_hi = vsubl_u8(vget_high_u8(va), vget_high_u8(vb)); + uint16x8_t diff_lo = vsubl_u8(vget_low_u8(va), vget_low_u8(vb)); + uint16x8_t diff_hi = vsubl_u8(vget_high_u8(va), vget_high_u8(vb)); // widen to signed 32-bit and square int32x4_t diff_lo_0 = vmovl_s16(vget_low_s16(diff_lo)); From d205bb472f7019714faaa9b28ec97f16a126ae2a Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 15:39:35 +0200 Subject: [PATCH 03/11] Test fix type for differences in uint8_distance_l2_impl_neon to cast signed int16x8_t --- src/distance-neon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/distance-neon.c b/src/distance-neon.c index 56e4338..c1af56d 100644 --- a/src/distance-neon.c +++ b/src/distance-neon.c @@ -155,8 +155,8 @@ static inline float uint8_distance_l2_impl_neon(const void *v1, const void *v2, uint8x16_t vb = vld1q_u8(b + i); // compute 8-bit differences widened to signed 16-bit - uint16x8_t diff_lo = vsubl_u8(vget_low_u8(va), vget_low_u8(vb)); - uint16x8_t diff_hi = vsubl_u8(vget_high_u8(va), vget_high_u8(vb)); + int16x8_t diff_lo = (int16x8_t)vsubl_u8(vget_low_u8(va), vget_low_u8(vb)); + int16x8_t diff_hi = (int16x8_t)vsubl_u8(vget_high_u8(va), vget_high_u8(vb)); // widen to signed 32-bit and square int32x4_t diff_lo_0 = vmovl_s16(vget_low_s16(diff_lo)); From 640830b3c1cc6287116ff3ee0516093a27df8696 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 15:44:45 +0200 Subject: [PATCH 04/11] Update build step in GitHub Actions to 'extension' --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3bef3d7..92278f6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -51,7 +51,7 @@ jobs: - uses: actions/checkout@v4.2.2 - name: build sqlite-vector - run: make ${{ matrix.make && matrix.make || ''}} + run: make extension ${{ matrix.make && matrix.make || ''}} - name: windows install sqlite3 if: matrix.os == 'windows-latest' From ff5611fc95c35950dbf835f9c1430e44bced84aa Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 15:49:13 +0200 Subject: [PATCH 05/11] Add command to load vector in Android test setup --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 92278f6..d97cc64 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -85,6 +85,7 @@ jobs: cat > commands.sh << EOF mv -f /data/local/tmp/sqlite3 /system/xbin cd /data/local/tmp + sqlite3 ":memory:" -cmd ".bail on" ".load ./dist/vector" "SELECT vector_version();" $(make test PLATFORM=$PLATFORM ARCH=$ARCH -n) EOF echo "::endgroup::" From 427d4f2a517212560a24c684435f5814e20b1967 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 15:53:00 +0200 Subject: [PATCH 06/11] Fix float min/max initialization for cross-platform compatibility --- src/sqlite-vector.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sqlite-vector.c b/src/sqlite-vector.c index a0b9302..66cc391 100644 --- a/src/sqlite-vector.c +++ b/src/sqlite-vector.c @@ -20,6 +20,10 @@ #include #include +#if defined(_WIN32) || defined(__linux__) +#include +#endif + #define DEBUG_VECTOR_ALWAYS(...) do {printf(__VA_ARGS__ );printf("\n");} while (0) #if ENABLE_VECTOR_DEBUG @@ -853,8 +857,13 @@ static int vector_rebuild_quantization (sqlite3_context *context, const char *ta // STEP 1 // find global min/max across ALL vectors + #if defined(_WIN32) || defined(__linux__) + float min_val = FLT_MAX; + float max_val = -FLT_MAX; + #else float min_val = MAXFLOAT; float max_val = -MAXFLOAT; + #endif while (1) { rc = sqlite3_step(vm); From 591e717e41887807f531399838e7266bc18e9bfc Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 16:04:10 +0200 Subject: [PATCH 07/11] Remove SQLITE_CORE build flag and Add SQLITE_EXTENSION_INIT --- Makefile | 2 +- src/sqlite-vector.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7c6d611..5de5ec9 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ MAKEFLAGS += -j$(CPUS) # Compiler and flags CC = gcc -CFLAGS = -Wall -Wextra -Wno-unused-parameter -I$(SRC_DIR) -I$(LIB_DIR) -DSQLITE_CORE +CFLAGS = -Wall -Wextra -Wno-unused-parameter -I$(SRC_DIR) -I$(LIB_DIR) # Directories SRC_DIR = src diff --git a/src/sqlite-vector.c b/src/sqlite-vector.c index 66cc391..ecc2357 100644 --- a/src/sqlite-vector.c +++ b/src/sqlite-vector.c @@ -24,6 +24,10 @@ #include #endif +#ifndef SQLITE_CORE +SQLITE_EXTENSION_INIT1 +#endif + #define DEBUG_VECTOR_ALWAYS(...) do {printf(__VA_ARGS__ );printf("\n");} while (0) #if ENABLE_VECTOR_DEBUG @@ -1758,6 +1762,9 @@ static void vector_backend (sqlite3_context *context, int argc, sqlite3_value ** // MARK: - SQLITE_VECTOR_API int sqlite3_vector_init (sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) { + #ifndef SQLITE_CORE + SQLITE_EXTENSION_INIT2(pApi); + #endif int rc = SQLITE_OK; init_distance_functions(false); From 2eee07137dea915039abd17f900cb43eb80dcce4 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 16:06:31 +0200 Subject: [PATCH 08/11] Remove sqlite3 command from android test setup script --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d97cc64..92278f6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -85,7 +85,6 @@ jobs: cat > commands.sh << EOF mv -f /data/local/tmp/sqlite3 /system/xbin cd /data/local/tmp - sqlite3 ":memory:" -cmd ".bail on" ".load ./dist/vector" "SELECT vector_version();" $(make test PLATFORM=$PLATFORM ARCH=$ARCH -n) EOF echo "::endgroup::" From 7f00b534a15c2cdc58039a57dc16c7a53f73e3fa Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 16:14:00 +0200 Subject: [PATCH 09/11] Add _GNU_SOURCE definition for Windows compatibility --- src/sqlite-vector.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sqlite-vector.c b/src/sqlite-vector.c index ecc2357..4a7ae92 100644 --- a/src/sqlite-vector.c +++ b/src/sqlite-vector.c @@ -9,6 +9,10 @@ #include "sqlite-vector.h" #include "distance-cpu.h" +#ifdef _WIN32 +#define _GNU_SOURCE +#endif + #include #include #include From 9d775a6f944619f69f9abe9045f7e64e815d13a9 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 16:16:14 +0200 Subject: [PATCH 10/11] Implement strcasestr function for Windows compatibility --- src/sqlite-vector.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/sqlite-vector.c b/src/sqlite-vector.c index 4a7ae92..5f84ebf 100644 --- a/src/sqlite-vector.c +++ b/src/sqlite-vector.c @@ -9,10 +9,6 @@ #include "sqlite-vector.h" #include "distance-cpu.h" -#ifdef _WIN32 -#define _GNU_SOURCE -#endif - #include #include #include @@ -24,6 +20,33 @@ #include #include +#ifdef _WIN32 +char* strcasestr(const char* haystack, const char* needle) { + if (!haystack || !needle) { + return NULL; + } + + if (*needle == '\0') { + return (char*)haystack; + } + + size_t needle_len = strlen(needle); + size_t haystack_len = strlen(haystack); + + if (needle_len > haystack_len) { + return NULL; + } + + for (size_t i = 0; i <= haystack_len - needle_len; i++) { + if (strnicmp(haystack + i, needle, needle_len) == 0) { + return (char*)(haystack + i); + } + } + + return NULL; +} +#endif + #if defined(_WIN32) || defined(__linux__) #include #endif From 734302a721f744f552cce1dadb3e056a07ea5733 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 23 Jun 2025 16:20:24 +0200 Subject: [PATCH 11/11] Refactor strcasestr implementation for improved readability and efficiency --- src/sqlite-vector.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/sqlite-vector.c b/src/sqlite-vector.c index 5f84ebf..ea1bf7a 100644 --- a/src/sqlite-vector.c +++ b/src/sqlite-vector.c @@ -21,28 +21,18 @@ #include #ifdef _WIN32 -char* strcasestr(const char* haystack, const char* needle) { - if (!haystack || !needle) { - return NULL; - } - - if (*needle == '\0') { - return (char*)haystack; - } - - size_t needle_len = strlen(needle); - size_t haystack_len = strlen(haystack); - - if (needle_len > haystack_len) { - return NULL; - } - - for (size_t i = 0; i <= haystack_len - needle_len; i++) { - if (strnicmp(haystack + i, needle, needle_len) == 0) { - return (char*)(haystack + i); +char *strcasestr(const char *haystack, const char *needle) { + if (!haystack || !needle) return NULL; + if (!*needle) return (char *)haystack; + for (; *haystack; ++haystack) { + const char *h = haystack; + const char *n = needle; + while (*h && *n && tolower((unsigned char)*h) == tolower((unsigned char)*n)) { + ++h; + ++n; } + if (!*n) return (char *)haystack; } - return NULL; } #endif