123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891 |
- #if defined(GB_SYSTEM_UNIX)
- // Required for intrinsics on GCC
- #include <xmmintrin.h>
- #endif
- #if defined(GB_COMPILER_MSVC)
- #include <intrin.h>
- #endif
- #if defined(GB_SYSTEM_WINDOWS)
- #define NOMINMAX 1
- #include <windows.h>
- #undef NOMINMAX
- #endif
- #define GB_WINDOWS_H_INCLUDED
- #define GB_IMPLEMENTATION
- #include "gb/gb.h"
- #include <wchar.h>
- #include <stdio.h>
- #if defined(GB_COMPILER_MSVC)
- #include <psapi.h>
- #endif
- #include <math.h>
- #include <string.h>
- #include <atomic> // Because I wanted the C++11 memory order semantics, of which gb.h does not offer (because it was a C89 library)
- gbAllocator heap_allocator(void);
- #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
- i32 next_pow2(i32 n);
- i64 next_pow2(i64 n);
- isize next_pow2_isize(isize n);
- void debugf(char const *fmt, ...);
- #include "threading.cpp"
- #include "unicode.cpp"
- #include "array.cpp"
- #include "queue.cpp"
- #include "common_memory.cpp"
- #include "string.cpp"
- #include "range_cache.cpp"
- int isize_cmp(isize x, isize y) {
- if (x < y) {
- return -1;
- } else if (x > y) {
- return +1;
- }
- return 0;
- }
- int u64_cmp(u64 x, u64 y) {
- if (x < y) {
- return -1;
- } else if (x > y) {
- return +1;
- }
- return 0;
- }
- int i64_cmp(i64 x, i64 y) {
- if (x < y) {
- return -1;
- } else if (x > y) {
- return +1;
- }
- return 0;
- }
- int i32_cmp(i32 x, i32 y) {
- if (x < y) {
- return -1;
- } else if (x > y) {
- return +1;
- }
- return 0;
- }
- u32 fnv32a(void const *data, isize len) {
- u8 const *bytes = cast(u8 const *)data;
- u32 h = 0x811c9dc5;
-
- for (; len >= 8; len -= 8, bytes += 8) {
- h = (h ^ bytes[0]) * 0x01000193;
- h = (h ^ bytes[1]) * 0x01000193;
- h = (h ^ bytes[2]) * 0x01000193;
- h = (h ^ bytes[3]) * 0x01000193;
- h = (h ^ bytes[4]) * 0x01000193;
- h = (h ^ bytes[5]) * 0x01000193;
- h = (h ^ bytes[6]) * 0x01000193;
- h = (h ^ bytes[7]) * 0x01000193;
- }
- while (len--) {
- h = (h ^ *bytes++) * 0x01000193;
- }
- return h;
- }
- u64 fnv64a(void const *data, isize len) {
- u8 const *bytes = cast(u8 const *)data;
- u64 h = 0xcbf29ce484222325ull;
-
- for (; len >= 8; len -= 8, bytes += 8) {
- h = (h ^ bytes[0]) * 0x100000001b3ull;
- h = (h ^ bytes[1]) * 0x100000001b3ull;
- h = (h ^ bytes[2]) * 0x100000001b3ull;
- h = (h ^ bytes[3]) * 0x100000001b3ull;
- h = (h ^ bytes[4]) * 0x100000001b3ull;
- h = (h ^ bytes[5]) * 0x100000001b3ull;
- h = (h ^ bytes[6]) * 0x100000001b3ull;
- h = (h ^ bytes[7]) * 0x100000001b3ull;
- }
- while (len--) {
- h = (h ^ *bytes++) * 0x100000001b3ull;
- }
- return h;
- }
- u64 u64_digit_value(Rune r) {
- switch (r) {
- case '0': return 0;
- case '1': return 1;
- case '2': return 2;
- case '3': return 3;
- case '4': return 4;
- case '5': return 5;
- case '6': return 6;
- case '7': return 7;
- case '8': return 8;
- case '9': return 9;
- case 'a': return 10;
- case 'b': return 11;
- case 'c': return 12;
- case 'd': return 13;
- case 'e': return 14;
- case 'f': return 15;
- case 'A': return 10;
- case 'B': return 11;
- case 'C': return 12;
- case 'D': return 13;
- case 'E': return 14;
- case 'F': return 15;
- }
- return 16; // NOTE(bill): Larger than highest possible
- }
- u64 u64_from_string(String string) {
- u64 base = 10;
- bool has_prefix = false;
- if (string.len > 2 && string[0] == '0') {
- switch (string[1]) {
- case 'b': base = 2; has_prefix = true; break;
- case 'o': base = 8; has_prefix = true; break;
- case 'd': base = 10; has_prefix = true; break;
- case 'z': base = 12; has_prefix = true; break;
- case 'x': base = 16; has_prefix = true; break;
- case 'h': base = 16; has_prefix = true; break;
- }
- }
- u8 *text = string.text;
- isize len = string.len;
- if (has_prefix) {
- text += 2;
- len -= 2;
- }
- u64 result = 0ull;
- for (isize i = 0; i < len; i++) {
- Rune r = cast(Rune)text[i];
- if (r == '_') {
- continue;
- }
- u64 v = u64_digit_value(r);
- if (v >= base) {
- break;
- }
- result *= base;
- result += v;
- }
- return result;
- }
- gb_global char const global_num_to_char_table[] =
- "0123456789"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "@$";
- String u64_to_string(u64 v, char *out_buf, isize out_buf_len) {
- char buf[32] = {0};
- isize i = gb_size_of(buf);
- u64 b = 10;
- while (v >= b) {
- buf[--i] = global_num_to_char_table[v%b];
- v /= b;
- }
- buf[--i] = global_num_to_char_table[v%b];
- isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
- gb_memmove(out_buf, &buf[i], len);
- return make_string(cast(u8 *)out_buf, len);
- }
- String i64_to_string(i64 a, char *out_buf, isize out_buf_len) {
- char buf[32] = {0};
- isize i = gb_size_of(buf);
- bool negative = false;
- if (a < 0) {
- negative = true;
- a = -a;
- }
- u64 v = cast(u64)a;
- u64 b = 10;
- while (v >= b) {
- buf[--i] = global_num_to_char_table[v%b];
- v /= b;
- }
- buf[--i] = global_num_to_char_table[v%b];
- if (negative) {
- buf[--i] = '-';
- }
- isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
- gb_memmove(out_buf, &buf[i], len);
- return make_string(cast(u8 *)out_buf, len);
- }
- gb_global i64 const signed_integer_mins[] = {
- 0,
- -128ll,
- -32768ll,
- 0,
- -2147483648ll,
- 0,
- 0,
- 0,
- (-9223372036854775807ll - 1ll),
- };
- gb_global i64 const signed_integer_maxs[] = {
- 0,
- 127ll,
- 32767ll,
- 0,
- 2147483647ll,
- 0,
- 0,
- 0,
- 9223372036854775807ll,
- };
- gb_global u64 const unsigned_integer_maxs[] = {
- 0,
- 255ull,
- 65535ull,
- 0,
- 4294967295ull,
- 0,
- 0,
- 0,
- 18446744073709551615ull,
- };
- bool add_overflow_u64(u64 x, u64 y, u64 *result) {
- *result = x + y;
- return *result < x || *result < y;
- }
- bool sub_overflow_u64(u64 x, u64 y, u64 *result) {
- *result = x - y;
- return *result > x;
- }
- void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) {
- #if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT)
- *lo = _umul128(x, y, hi);
- #else
- // URL(bill): https://stackoverflow.com/questions/25095741/how-can-i-multiply-64-bit-operands-and-get-128-bit-result-portably#25096197
- u64 u1, v1, w1, t, w3, k;
- u1 = (x & 0xffffffff);
- v1 = (y & 0xffffffff);
- t = (u1 * v1);
- w3 = (t & 0xffffffff);
- k = (t >> 32);
- x >>= 32;
- t = (x * v1) + k;
- k = (t & 0xffffffff);
- w1 = (t >> 32);
- y >>= 32;
- t = (u1 * y) + k;
- k = (t >> 32);
- *hi = (x * y) + w1 + k;
- *lo = (t << 32) + w3;
- #endif
- }
- gb_global String global_module_path = {0};
- gb_global bool global_module_path_set = false;
- #include "ptr_map.cpp"
- #include "ptr_set.cpp"
- #include "string_map.cpp"
- #include "string_set.cpp"
- #include "priority_queue.cpp"
- #include "thread_pool.cpp"
- struct StringIntern {
- StringIntern *next;
- isize len;
- char str[1];
- };
- PtrMap<uintptr, StringIntern *> string_intern_map = {}; // Key: u64
- gb_global Arena string_intern_arena = {};
- char const *string_intern(char const *text, isize len) {
- u64 hash = gb_fnv64a(text, len);
- uintptr key = cast(uintptr)(hash ? hash : 1);
- StringIntern **found = map_get(&string_intern_map, key);
- if (found) {
- for (StringIntern *it = *found; it != nullptr; it = it->next) {
- if (it->len == len && gb_strncmp(it->str, (char *)text, len) == 0) {
- return it->str;
- }
- }
- }
- StringIntern *new_intern = cast(StringIntern *)arena_alloc(&string_intern_arena, gb_offset_of(StringIntern, str) + len + 1, gb_align_of(StringIntern));
- new_intern->len = len;
- new_intern->next = found ? *found : nullptr;
- gb_memmove(new_intern->str, text, len);
- new_intern->str[len] = 0;
- map_set(&string_intern_map, key, new_intern);
- return new_intern->str;
- }
- char const *string_intern(String const &string) {
- return string_intern(cast(char const *)string.text, string.len);
- }
- void init_string_interner(void) {
- map_init(&string_intern_map, heap_allocator());
- }
- i32 next_pow2(i32 n) {
- if (n <= 0) {
- return 0;
- }
- n--;
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- n++;
- return n;
- }
- i64 next_pow2(i64 n) {
- if (n <= 0) {
- return 0;
- }
- n--;
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- n |= n >> 32;
- n++;
- return n;
- }
- isize next_pow2_isize(isize n) {
- if (n <= 0) {
- return 0;
- }
- n--;
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- #if defined(GB_ARCH_64_BIT)
- n |= n >> 32;
- #endif
- n++;
- return n;
- }
- u32 next_pow2_u32(u32 n) {
- if (n == 0) {
- return 0;
- }
- n--;
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- n++;
- return n;
- }
- i32 bit_set_count(u32 x) {
- x -= ((x >> 1) & 0x55555555);
- x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
- x = (((x >> 4) + x) & 0x0f0f0f0f);
- x += (x >> 8);
- x += (x >> 16);
- return cast(i32)(x & 0x0000003f);
- }
- i64 bit_set_count(u64 x) {
- u32 a = *(cast(u32 *)&x);
- u32 b = *(cast(u32 *)&x + 1);
- return bit_set_count(a) + bit_set_count(b);
- }
- u32 floor_log2(u32 x) {
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- return cast(u32)(bit_set_count(x) - 1);
- }
- u64 floor_log2(u64 x) {
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- x |= x >> 32;
- return cast(u64)(bit_set_count(x) - 1);
- }
- u32 ceil_log2(u32 x) {
- i32 y = cast(i32)(x & (x-1));
- y |= -y;
- y >>= 32-1;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- return cast(u32)(bit_set_count(x) - 1 - y);
- }
- u64 ceil_log2(u64 x) {
- i64 y = cast(i64)(x & (x-1));
- y |= -y;
- y >>= 64-1;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- x |= x >> 32;
- return cast(u64)(bit_set_count(x) - 1 - y);
- }
- u32 prev_pow2(u32 n) {
- if (n == 0) {
- return 0;
- }
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- return n - (n >> 1);
- }
- i32 prev_pow2(i32 n) {
- if (n <= 0) {
- return 0;
- }
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- return n - (n >> 1);
- }
- i64 prev_pow2(i64 n) {
- if (n <= 0) {
- return 0;
- }
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- n |= n >> 32;
- return n - (n >> 1);
- }
- u16 f32_to_f16(f32 value) {
- union { u32 i; f32 f; } v;
- i32 i, s, e, m;
- v.f = value;
- i = (i32)v.i;
- s = (i >> 16) & 0x00008000;
- e = ((i >> 23) & 0x000000ff) - (127 - 15);
- m = i & 0x007fffff;
- if (e <= 0) {
- if (e < -10) return cast(u16)s;
- m = (m | 0x00800000) >> (1 - e);
- if (m & 0x00001000)
- m += 0x00002000;
- return cast(u16)(s | (m >> 13));
- } else if (e == 0xff - (127 - 15)) {
- if (m == 0) {
- return cast(u16)(s | 0x7c00); /* NOTE(bill): infinity */
- } else {
- /* NOTE(bill): NAN */
- m >>= 13;
- return cast(u16)(s | 0x7c00 | m | (m == 0));
- }
- } else {
- if (m & 0x00001000) {
- m += 0x00002000;
- if (m & 0x00800000) {
- m = 0;
- e += 1;
- }
- }
- if (e > 30) {
- float volatile f = 1e12f;
- int j;
- for (j = 0; j < 10; j++) {
- f *= f; /* NOTE(bill): Cause overflow */
- }
- return cast(u16)(s | 0x7c00);
- }
- return cast(u16)(s | (e << 10) | (m >> 13));
- }
- }
- f32 f16_to_f32(u16 value) {
- typedef union { u32 u; f32 f; } fp32;
- fp32 v;
- fp32 magic = {(254u - 15u) << 23};
- fp32 inf_or_nan = {(127u + 16u) << 23};
- v.u = (value & 0x7fffu) << 13;
- v.f *= magic.f;
- if (v.f >= inf_or_nan.f) {
- v.u |= 255u << 23;
- }
- v.u |= (value & 0x8000u) << 16;
- return v.f;
- }
- f64 gb_sqrt(f64 x) {
- return sqrt(x);
- }
- // Doubly Linked Lists
- #define DLIST_SET(curr_element, next_element) do { \
- (curr_element)->next = (next_element); \
- (curr_element)->next->prev = (curr_element); \
- (curr_element) = (curr_element)->next; \
- } while (0)
- #define DLIST_APPEND(root_element, curr_element, next_element) do { \
- if ((root_element) == nullptr) { \
- (root_element) = (curr_element) = (next_element); \
- } else { \
- DLIST_SET(curr_element, next_element); \
- } \
- } while (0)
- #if defined(GB_SYSTEM_WINDOWS)
- wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
- u32 i, j;
- u32 len = cast(u32)string16_len(cmd_line);
- i = ((len+2)/2)*gb_size_of(void *) + gb_size_of(void *);
- wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t));
- wchar_t *_argv = cast(wchar_t *)((cast(u8 *)argv)+i);
- u32 argc = 0;
- argv[argc] = _argv;
- bool in_quote = false;
- bool in_text = false;
- bool in_space = true;
- i = 0;
- j = 0;
- for (;;) {
- wchar_t a = cmd_line[i];
- if (a == 0) {
- break;
- }
- if (in_quote) {
- if (a == '\"') {
- in_quote = false;
- } else {
- _argv[j++] = a;
- }
- } else {
- switch (a) {
- case '\"':
- in_quote = true;
- in_text = true;
- if (in_space) argv[argc++] = _argv+j;
- in_space = false;
- break;
- case ' ':
- case '\t':
- case '\n':
- case '\r':
- if (in_text) _argv[j++] = '\0';
- in_text = false;
- in_space = true;
- break;
- default:
- in_text = true;
- if (in_space) argv[argc++] = _argv+j;
- _argv[j++] = a;
- in_space = false;
- break;
- }
- }
- i++;
- }
- _argv[j] = '\0';
- argv[argc] = nullptr;
- if (_argc) *_argc = argc;
- return argv;
- }
- #endif
- #include "path.cpp"
- struct LoadedFile {
- void *handle;
-
- void const *data;
- i32 size;
- };
- enum LoadedFileError {
- LoadedFile_None,
-
- LoadedFile_Empty,
- LoadedFile_FileTooLarge,
- LoadedFile_Invalid,
- LoadedFile_NotExists,
- LoadedFile_Permission,
-
- LoadedFile_COUNT,
- };
- LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_file, bool copy_file_contents) {
- LoadedFileError err = LoadedFile_None;
-
- if (!copy_file_contents) {
- #if defined(GB_SYSTEM_WINDOWS)
- isize w_len = 0;
- wchar_t *w_str = gb__alloc_utf8_to_ucs2(temporary_allocator(), fullpath, &w_len);
- if (w_str == nullptr) {
- return LoadedFile_Invalid;
- }
- i64 file_size = 0;
- LARGE_INTEGER li_file_size = {};
- HANDLE handle = nullptr;
- HANDLE file_mapping = nullptr;
- void *file_data = nullptr;
-
- handle = CreateFileW(w_str, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
- if (handle == INVALID_HANDLE_VALUE) {
- handle = nullptr;
- goto window_handle_file_error;
- }
-
- li_file_size = {};
- if (!GetFileSizeEx(handle, &li_file_size)) {
- goto window_handle_file_error;
- }
- file_size = cast(i64)li_file_size.QuadPart;
- if (file_size > I32_MAX) {
- CloseHandle(handle);
- return LoadedFile_FileTooLarge;
- }
-
- if (file_size == 0) {
- CloseHandle(handle);
- err = LoadedFile_Empty;
- memory_mapped_file->handle = nullptr;
- memory_mapped_file->data = nullptr;
- memory_mapped_file->size = 0;
- return err;
- }
-
- file_mapping = CreateFileMappingW(handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
- CloseHandle(handle);
-
- file_data = MapViewOfFileEx(file_mapping, FILE_MAP_READ, 0, 0, 0/*file_size*/, nullptr/*base address*/);
- memory_mapped_file->handle = cast(void *)file_mapping;
- memory_mapped_file->data = file_data;
- memory_mapped_file->size = cast(i32)file_size;
- return err;
-
- window_handle_file_error:;
- {
- DWORD handle_err = GetLastError();
- CloseHandle(handle);
- err = LoadedFile_Invalid;
- switch (handle_err) {
- case ERROR_FILE_NOT_FOUND:
- case ERROR_PATH_NOT_FOUND:
- case ERROR_INVALID_DRIVE:
- err = LoadedFile_NotExists;
- break;
- case ERROR_ACCESS_DENIED:
- case ERROR_INVALID_ACCESS:
- err = LoadedFile_Permission;
- break;
- }
- return err;
- }
- #endif
- }
-
- gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath);
- if (fc.size > I32_MAX) {
- err = LoadedFile_FileTooLarge;
- gb_file_free_contents(&fc);
- } else if (fc.data != nullptr) {
- memory_mapped_file->handle = nullptr;
- memory_mapped_file->data = fc.data;
- memory_mapped_file->size = cast(i32)fc.size;
- } else {
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, fullpath);
- defer (gb_file_close(&f));
- switch (file_err) {
- case gbFileError_Invalid: err = LoadedFile_Invalid; break;
- case gbFileError_NotExists: err = LoadedFile_NotExists; break;
- case gbFileError_Permission: err = LoadedFile_Permission; break;
- }
- if (err == LoadedFile_None && gb_file_size(&f) == 0) {
- err = LoadedFile_Empty;
- }
- }
- return err;
- }
- #define USE_DAMERAU_LEVENSHTEIN 1
- isize levenstein_distance_case_insensitive(String const &a, String const &b) {
- isize w = b.len+1;
- isize h = a.len+1;
- isize *matrix = gb_alloc_array(temporary_allocator(), isize, w*h);
- for (isize i = 0; i <= a.len; i++) {
- matrix[i*w + 0] = i;
- }
- for (isize i = 0; i <= b.len; i++) {
- matrix[0*w + i] = i;
- }
- for (isize i = 1; i <= a.len; i++) {
- char a_c = gb_char_to_lower(cast(char)a.text[i-1]);
- for (isize j = 1; j <= b.len; j++) {
- char b_c = gb_char_to_lower(cast(char)b.text[j-1]);
- if (a_c == b_c) {
- matrix[i*w + j] = matrix[(i-1)*w + j-1];
- } else {
- isize remove = matrix[(i-1)*w + j] + 1;
- isize insert = matrix[i*w + j-1] + 1;
- isize substitute = matrix[(i-1)*w + j-1] + 1;
- isize minimum = remove;
- if (insert < minimum) {
- minimum = insert;
- }
- if (substitute < minimum) {
- minimum = substitute;
- }
- // Damerau-Levenshtein (transposition extension)
- #if USE_DAMERAU_LEVENSHTEIN
- if (i > 1 && j > 1) {
- isize transpose = matrix[(i-2)*w + j-2] + 1;
- if (transpose < minimum) {
- minimum = transpose;
- }
- }
- #endif
- matrix[i*w + j] = minimum;
- }
- }
- }
- return matrix[a.len*w + b.len];
- }
- struct DistanceAndTarget {
- isize distance;
- String target;
- };
- struct DidYouMeanAnswers {
- Array<DistanceAndTarget> distances;
- String key;
- };
- enum {MAX_SMALLEST_DID_YOU_MEAN_DISTANCE = 3-USE_DAMERAU_LEVENSHTEIN};
- DidYouMeanAnswers did_you_mean_make(gbAllocator allocator, isize cap, String const &key) {
- DidYouMeanAnswers d = {};
- array_init(&d.distances, allocator, 0, cap);
- d.key = key;
- return d;
- }
- void did_you_mean_destroy(DidYouMeanAnswers *d) {
- array_free(&d->distances);
- }
- void did_you_mean_append(DidYouMeanAnswers *d, String const &target) {
- if (target.len == 0 || target == "_") {
- return;
- }
- DistanceAndTarget dat = {};
- dat.target = target;
- dat.distance = levenstein_distance_case_insensitive(d->key, target);
- array_add(&d->distances, dat);
- }
- Slice<DistanceAndTarget> did_you_mean_results(DidYouMeanAnswers *d) {
- gb_sort_array(d->distances.data, d->distances.count, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance)));
- isize count = 0;
- for (isize i = 0; i < d->distances.count; i++) {
- isize distance = d->distances[i].distance;
- if (distance > MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) {
- break;
- }
- count += 1;
- }
- return slice_array(d->distances, 0, count);
- }
|