mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
wasm: support more vendor libraries
Adds support for: - box2d - cgltf - stb image - stb rect pack
This commit is contained in:
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
# vendor:libc
|
||||
|
||||
A (very small) subset of a libc implementation over Odin libraries.
|
||||
This is mainly intended for use in Odin WASM builds to allow using libraries like box2d, cgltf etc. without emscripten hacks.
|
||||
|
||||
You can use this with clang by doing `clang -c --target=wasm32 --sysroot=$(odin root)/vendor/libc` (+ all other flags and inputs).
|
||||
This will (if all the libc usage of the library is implemented) spit out a `.o` file you can use with the foreign import system.
|
||||
If you then also make sure this package is included in the Odin side of the project (`@(require) import "vendor:libc"`) you will be able
|
||||
compile to WASM like Odin expects.
|
||||
|
||||
This is currently used by `vendor:box2d`, `vendor:stb/image`, `vendor:stb/truetype`, `vendor:stb/rect_pack`, and `vendor:cgltf`.
|
||||
You can see how building works by looking at those.
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
package odin_libc
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@(require, linkage="strong", link_name="__odin_libc_assert_fail")
|
||||
__odin_libc_assert_fail :: proc "c" (func: cstring, file: cstring, line: i32, expr: cstring) -> ! {
|
||||
context = g_ctx
|
||||
loc := runtime.Source_Code_Location{
|
||||
file_path = string(file),
|
||||
line = line,
|
||||
column = 0,
|
||||
procedure = string(func),
|
||||
}
|
||||
context.assertion_failure_proc("runtime assertion", string(expr), loc)
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
#ifdef NDEBUG
|
||||
#define assert(e) ((void)0)
|
||||
#else
|
||||
|
||||
#ifdef __FILE_NAME__
|
||||
#define __ASSERT_FILE_NAME __FILE_NAME__
|
||||
#else /* __FILE_NAME__ */
|
||||
#define __ASSERT_FILE_NAME __FILE__
|
||||
#endif /* __FILE_NAME__ */
|
||||
|
||||
void __odin_libc_assert_fail(const char *, const char *, int, const char *);
|
||||
|
||||
#define assert(e) \
|
||||
(__builtin_expect(!(e), 0) ? __odin_libc_assert_fail(__func__, __ASSERT_FILE_NAME, __LINE__, #e) : (void)0)
|
||||
|
||||
#endif /* NDEBUG */
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
float sqrtf(float);
|
||||
float cosf(float);
|
||||
float sinf(float);
|
||||
float atan2f(float, float);
|
||||
bool isnan(float);
|
||||
bool isinf(float);
|
||||
double floor(double x);
|
||||
double ceil(double x);
|
||||
double sqrt(double x);
|
||||
double pow(double x, double y);
|
||||
double fmod(double x, double y);
|
||||
double cos(double x);
|
||||
double acos(double x);
|
||||
double fabs(double x);
|
||||
int abs(int);
|
||||
double ldexp(double, int);
|
||||
double exp(double);
|
||||
float log(float);
|
||||
float sin(float);
|
||||
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct {} FILE;
|
||||
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
|
||||
#define stdout ((FILE *)2)
|
||||
#define stderr ((FILE *)3)
|
||||
|
||||
FILE *fopen(const char *, char *);
|
||||
int fclose(FILE *);
|
||||
int fseek(FILE *, long, int);
|
||||
long ftell(FILE *);
|
||||
size_t fread(void *, size_t, size_t, FILE *);
|
||||
size_t fwrite(const void *, size_t, size_t, FILE *);
|
||||
|
||||
int vfprintf(FILE *, const char *, va_list);
|
||||
int vsnprintf(char *, size_t, const char *, va_list);
|
||||
|
||||
static inline int snprintf(char *buf, size_t size, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int result = vsnprintf(buf, size, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int fprintf(FILE *f, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int result = vfprintf(f, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int printf(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int result = vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
#include <stddef.h>
|
||||
|
||||
void *malloc(size_t size);
|
||||
|
||||
void *aligned_alloc(size_t alignment, size_t size);
|
||||
|
||||
void free(void *);
|
||||
|
||||
void *realloc(void *, size_t);
|
||||
|
||||
void qsort(void* base, size_t num, size_t size, int (*compare)(const void*, const void*));
|
||||
|
||||
int atoi(const char *);
|
||||
long atol(const char *);
|
||||
long long atoll(const char *);
|
||||
|
||||
double atof(const char *);
|
||||
|
||||
long strtol(const char *, char **, int);
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
#include <stddef.h>
|
||||
|
||||
void *memcpy(void *, const void *, size_t);
|
||||
void *memset(void *, int, size_t);
|
||||
void *memmove(void *, void *, size_t);
|
||||
int memcmp(const void *, const void *, size_t);
|
||||
|
||||
unsigned long strlen(const char *str);
|
||||
|
||||
char *strchr(const char *, int);
|
||||
char *strrchr(const char *, int);
|
||||
|
||||
char *strncpy(char *, const char *, size_t);
|
||||
char *strcpy(char *, const char *);
|
||||
|
||||
size_t strcspn(const char *, const char *);
|
||||
|
||||
int strcmp(const char *, const char *);
|
||||
int strncmp(const char *, const char *, size_t);
|
||||
|
||||
char *strstr(const char *, const char *);
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
package odin_libc
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:mem"
|
||||
|
||||
@(private)
|
||||
g_ctx: runtime.Context
|
||||
@(private)
|
||||
g_allocator: mem.Compat_Allocator
|
||||
|
||||
@(init)
|
||||
init_context :: proc() {
|
||||
g_ctx = context
|
||||
|
||||
// Wrapping the allocator with the mem.Compat_Allocator so we can
|
||||
// mimic the realloc semantics.
|
||||
mem.compat_allocator_init(&g_allocator, g_ctx.allocator)
|
||||
g_ctx.allocator = mem.compat_allocator(&g_allocator)
|
||||
}
|
||||
|
||||
// NOTE: the allocator must respect an `old_size` of `-1` on resizes!
|
||||
set_context :: proc(ctx := context) {
|
||||
g_ctx = ctx
|
||||
}
|
||||
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
package odin_libc
|
||||
|
||||
import "base:builtin"
|
||||
|
||||
import "core:math"
|
||||
|
||||
@(require, linkage="strong", link_name="sqrtf")
|
||||
sqrtf :: proc "c" (v: f32) -> f32 {
|
||||
return math.sqrt(v)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="cosf")
|
||||
cosf :: proc "c" (v: f32) -> f32 {
|
||||
return math.cos(v)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="sinf")
|
||||
sinf :: proc "c" (v: f32) -> f32 {
|
||||
return math.sin(v)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="atan2f")
|
||||
atan2f :: proc "c" (v: f32, v2: f32) -> f32 {
|
||||
return math.atan2(v, v2)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="isnan")
|
||||
isnan :: proc "c" (v: f32) -> bool {
|
||||
return math.is_nan(v)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="isinf")
|
||||
isinf :: proc "c" (v: f32) -> bool {
|
||||
return math.is_inf(v)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="sqrt")
|
||||
sqrt :: proc "c" (x: f64) -> f64 {
|
||||
return math.sqrt(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="floor")
|
||||
floor :: proc "c" (x: f64) -> f64 {
|
||||
return math.floor(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="ceil")
|
||||
ceil :: proc "c" (x: f64) -> f64 {
|
||||
return math.ceil(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="pow")
|
||||
pow :: proc "c" (x, y: f64) -> f64 {
|
||||
return math.pow(x, y)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="fmod")
|
||||
fmod :: proc "c" (x, y: f64) -> f64 {
|
||||
return math.mod(x, y)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="cos")
|
||||
cos :: proc "c" (x: f64) -> f64 {
|
||||
return math.cos(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="acos")
|
||||
acos :: proc "c" (x: f64) -> f64 {
|
||||
return math.acos(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="fabs")
|
||||
fabs :: proc "c" (x: f64) -> f64 {
|
||||
return math.abs(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="abs")
|
||||
abs :: proc "c" (x: i32) -> i32 {
|
||||
return builtin.abs(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="ldexp")
|
||||
ldexp :: proc "c" (x: f64, y: i32) -> f64{
|
||||
return math.ldexp(x, int(y))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="exp")
|
||||
exp :: proc "c" (x: f64) -> f64 {
|
||||
return math.exp(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="log")
|
||||
log :: proc "c" (x: f32) -> f32 {
|
||||
return math.ln(x)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="sin")
|
||||
sin :: proc "c" (x: f32) -> f32 {
|
||||
return math.sin(x)
|
||||
}
|
||||
Vendored
+106
@@ -0,0 +1,106 @@
|
||||
package odin_libc
|
||||
|
||||
import "core:c"
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
import stb "vendor:stb/sprintf"
|
||||
|
||||
FILE :: uintptr
|
||||
|
||||
@(require, linkage="strong", link_name="fopen")
|
||||
fopen :: proc "c" (path: cstring, mode: cstring) -> FILE {
|
||||
context = g_ctx
|
||||
unimplemented("odin_libc.fopen")
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="fseek")
|
||||
fseek :: proc "c" (file: FILE, offset: c.long, whence: i32) -> i32 {
|
||||
context = g_ctx
|
||||
handle := os.Handle(file-1)
|
||||
_, err := os.seek(handle, i64(offset), int(whence))
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="ftell")
|
||||
ftell :: proc "c" (file: FILE) -> c.long {
|
||||
context = g_ctx
|
||||
handle := os.Handle(file-1)
|
||||
off, err := os.seek(handle, 0, os.SEEK_CUR)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return c.long(off)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="fclose")
|
||||
fclose :: proc "c" (file: FILE) -> i32 {
|
||||
context = g_ctx
|
||||
handle := os.Handle(file-1)
|
||||
if os.close(handle) != nil {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="fread")
|
||||
fread :: proc "c" (buffer: [^]byte, size: uint, count: uint, file: FILE) -> uint {
|
||||
context = g_ctx
|
||||
handle := os.Handle(file-1)
|
||||
n, _ := os.read(handle, buffer[:min(size, count)])
|
||||
return uint(max(0, n))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="fwrite")
|
||||
fwrite :: proc "c" (buffer: [^]byte, size: uint, count: uint, file: FILE) -> uint {
|
||||
context = g_ctx
|
||||
handle := os.Handle(file-1)
|
||||
n, _ := os.write(handle, buffer[:min(size, count)])
|
||||
return uint(max(0, n))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="vsnprintf")
|
||||
vsnprintf :: proc "c" (buf: [^]byte, count: uint, fmt: cstring, args: ^c.va_list) -> i32 {
|
||||
i32_count := i32(count)
|
||||
assert_contextless(i32_count >= 0)
|
||||
return stb.vsnprintf(buf, i32_count, fmt, args)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="vfprintf")
|
||||
vfprintf :: proc "c" (file: FILE, fmt: cstring, args: ^c.va_list) -> i32 {
|
||||
context = g_ctx
|
||||
|
||||
handle := os.Handle(file-1)
|
||||
|
||||
MAX_STACK :: 4096
|
||||
|
||||
buf: []byte
|
||||
stack_buf: [MAX_STACK]byte = ---
|
||||
{
|
||||
n := stb.vsnprintf(&stack_buf[0], MAX_STACK, fmt, args)
|
||||
if n <= 0 {
|
||||
return n
|
||||
}
|
||||
|
||||
if n >= MAX_STACK {
|
||||
buf = make([]byte, n)
|
||||
n2 := stb.vsnprintf(raw_data(buf), i32(len(buf)), fmt, args)
|
||||
assert(n == n2)
|
||||
} else {
|
||||
buf = stack_buf[:n]
|
||||
}
|
||||
}
|
||||
defer if len(buf) > MAX_STACK {
|
||||
delete(buf)
|
||||
}
|
||||
|
||||
_, err := io.write_full(os.stream_from_handle(handle), buf)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return i32(len(buf))
|
||||
}
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
package odin_libc
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:c"
|
||||
import "core:slice"
|
||||
import "core:sort"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
|
||||
@(require, linkage="strong", link_name="malloc")
|
||||
malloc :: proc "c" (size: uint) -> rawptr {
|
||||
context = g_ctx
|
||||
ptr, err := runtime.mem_alloc_non_zeroed(int(size))
|
||||
assert(err == nil, "allocation failure")
|
||||
return raw_data(ptr)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="aligned_alloc")
|
||||
aligned_alloc :: proc "c" (alignment: uint, size: uint) -> rawptr {
|
||||
context = g_ctx
|
||||
ptr, err := runtime.mem_alloc_non_zeroed(int(size), int(alignment))
|
||||
assert(err == nil, "allocation failure")
|
||||
return raw_data(ptr)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="free")
|
||||
free :: proc "c" (ptr: rawptr) {
|
||||
context = g_ctx
|
||||
runtime.mem_free(ptr)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="realloc")
|
||||
realloc :: proc "c" (ptr: rawptr, new_size: uint) -> rawptr {
|
||||
context = g_ctx
|
||||
// -1 for the old_size, assumed to be wrapped with the mem.Compat_Allocator to get the right size.
|
||||
// Note that realloc does not actually care about alignment and is allowed to just align it to something
|
||||
// else than the original allocation.
|
||||
ptr, err := runtime.non_zero_mem_resize(ptr, -1, int(new_size))
|
||||
assert(err != nil, "realloc failure")
|
||||
return raw_data(ptr)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="qsort")
|
||||
qsort :: proc "c" (base: rawptr, num: uint, size: uint, cmp: proc "c" (a, b: rawptr) -> i32) {
|
||||
context = g_ctx
|
||||
|
||||
Inputs :: struct {
|
||||
base: rawptr,
|
||||
num: uint,
|
||||
size: uint,
|
||||
cmp: proc "c" (a, b: rawptr) -> i32,
|
||||
}
|
||||
|
||||
sort.sort({
|
||||
collection = &Inputs{base, num, size, cmp},
|
||||
len = proc(it: sort.Interface) -> int {
|
||||
inputs := (^Inputs)(it.collection)
|
||||
return int(inputs.num)
|
||||
},
|
||||
less = proc(it: sort.Interface, i, j: int) -> bool {
|
||||
inputs := (^Inputs)(it.collection)
|
||||
a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size)))
|
||||
b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size)))
|
||||
return inputs.cmp(a, b) < 0
|
||||
},
|
||||
swap = proc(it: sort.Interface, i, j: int) {
|
||||
inputs := (^Inputs)(it.collection)
|
||||
|
||||
a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size)))
|
||||
b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size)))
|
||||
|
||||
slice.ptr_swap_non_overlapping(a, b, int(inputs.size))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="atoi")
|
||||
atoi :: proc "c" (str: cstring) -> i32 {
|
||||
return i32(atoll(str))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="atol")
|
||||
atol :: proc "c" (str: cstring) -> c.long {
|
||||
return c.long(atoll(str))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="atoll")
|
||||
atoll :: proc "c" (str: cstring) -> c.longlong {
|
||||
context = g_ctx
|
||||
|
||||
sstr := string(str)
|
||||
sstr = strings.trim_left_space(sstr)
|
||||
i, _ := strconv.parse_i64_of_base(sstr, 10)
|
||||
return c.longlong(i)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="atof")
|
||||
atof :: proc "c" (str: cstring) -> f64 {
|
||||
context = g_ctx
|
||||
|
||||
sstr := string(str)
|
||||
sstr = strings.trim_left_space(sstr)
|
||||
f, _ := strconv.parse_f64(sstr)
|
||||
return f
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strtol")
|
||||
strtol :: proc "c" (str: cstring, str_end: ^cstring, base: i32) -> c.long {
|
||||
context = g_ctx
|
||||
|
||||
sstr := string(str)
|
||||
sstr = strings.trim_left_space(sstr)
|
||||
|
||||
n: int
|
||||
i, _ := strconv.parse_i64_of_base(sstr, int(base), &n)
|
||||
str_end ^= cstring(raw_data(sstr)[n:])
|
||||
return c.long(clamp(i, i64(min(c.long)), i64(max(c.long))))
|
||||
}
|
||||
Vendored
+111
@@ -0,0 +1,111 @@
|
||||
package odin_libc
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:c"
|
||||
import "core:strings"
|
||||
import "core:mem"
|
||||
|
||||
// NOTE: already defined by Odin.
|
||||
// void *memcpy(void *, const void *, size_t);
|
||||
// void *memset(void *, int, size_t);
|
||||
|
||||
@(require, linkage="strong", link_name="memcmp")
|
||||
memcmp :: proc "c" (lhs: [^]byte, rhs: [^]byte, count: uint) -> i32 {
|
||||
icount := int(count)
|
||||
assert_contextless(icount >= 0)
|
||||
return i32(mem.compare(lhs[:icount], rhs[:icount]))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strlen")
|
||||
strlen :: proc "c" (str: cstring) -> c.ulong {
|
||||
return c.ulong(len(str))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strchr")
|
||||
strchr :: proc "c" (str: cstring, ch: i32) -> cstring {
|
||||
bch := u8(ch)
|
||||
sstr := string(str)
|
||||
if bch == 0 {
|
||||
return cstring(raw_data(sstr)[len(sstr):])
|
||||
}
|
||||
|
||||
idx := strings.index_byte(sstr, bch)
|
||||
if idx < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cstring(raw_data(sstr)[idx:])
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strrchr")
|
||||
strrchr :: proc "c" (str: cstring, ch: i32) -> cstring {
|
||||
bch := u8(ch)
|
||||
sstr := string(str)
|
||||
if bch == 0 {
|
||||
return cstring(raw_data(sstr)[len(sstr):])
|
||||
}
|
||||
|
||||
idx := strings.last_index_byte(sstr, bch)
|
||||
if idx < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cstring(raw_data(sstr)[idx:])
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strncpy")
|
||||
strncpy :: proc "c" (dst: [^]byte, src: cstring, count: uint) -> cstring {
|
||||
icount := int(count)
|
||||
assert_contextless(icount >= 0)
|
||||
cnt := min(len(src), icount)
|
||||
intrinsics.mem_copy_non_overlapping(dst, rawptr(src), cnt)
|
||||
intrinsics.mem_zero(dst, icount-cnt)
|
||||
return cstring(dst)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strcpy")
|
||||
strcpy :: proc "c" (dst: [^]byte, src: cstring) -> cstring {
|
||||
intrinsics.mem_copy_non_overlapping(dst, rawptr(src), len(src)+1)
|
||||
return cstring(dst)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strcspn")
|
||||
strcspn :: proc "c" (dst: cstring, src: cstring) -> uint {
|
||||
context = g_ctx
|
||||
sdst := string(dst)
|
||||
idx := strings.index_any(sdst, string(src))
|
||||
if idx == -1 {
|
||||
return len(sdst)
|
||||
}
|
||||
return uint(idx)
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strncmp")
|
||||
strncmp :: proc "c" (lhs: cstring, rhs: cstring, count: uint) -> i32 {
|
||||
icount := int(count)
|
||||
assert_contextless(icount >= 0)
|
||||
lhss := strings.string_from_null_terminated_ptr(([^]byte)(lhs), icount)
|
||||
rhss := strings.string_from_null_terminated_ptr(([^]byte)(rhs), icount)
|
||||
return i32(strings.compare(lhss, rhss))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strcmp")
|
||||
strcmp :: proc "c" (lhs: cstring, rhs: cstring) -> i32 {
|
||||
return i32(strings.compare(string(lhs), string(rhs)))
|
||||
}
|
||||
|
||||
@(require, linkage="strong", link_name="strstr")
|
||||
strstr :: proc "c" (str: cstring, substr: cstring) -> cstring {
|
||||
if substr == "" {
|
||||
return str
|
||||
}
|
||||
|
||||
idx := strings.index(string(str), string(substr))
|
||||
if idx < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cstring(([^]byte)(str)[idx:])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user