| 1 | module builtin |
| 2 | |
| 3 | // vstrlen returns the V length of the C string `s` (0 terminator is not counted). |
| 4 | // The C string is expected to be a &u8 pointer. |
| 5 | @[inline; unsafe] |
| 6 | pub fn vstrlen(s &u8) int { |
| 7 | return int(unsafe { C.strlen(&char(s)) }) |
| 8 | } |
| 9 | |
| 10 | // vstrlen_char returns the V length of the C string `s` (0 terminator is not counted). |
| 11 | // The C string is expected to be a &char pointer. |
| 12 | @[inline; unsafe] |
| 13 | pub fn vstrlen_char(s &char) int { |
| 14 | return int(unsafe { C.strlen(s) }) |
| 15 | } |
| 16 | |
| 17 | // vmemcpy copies n bytes from memory area src to memory area dest. |
| 18 | // The memory areas *MUST NOT OVERLAP*. Use vmemmove, if the memory |
| 19 | // areas do overlap. vmemcpy returns a pointer to `dest`. |
| 20 | @[inline; unsafe] |
| 21 | pub fn vmemcpy(dest voidptr, const_src voidptr, n isize) voidptr { |
| 22 | $if trace_vmemcpy ? { |
| 23 | C.fprintf(C.stderr, c'vmemcpy dest: %p src: %p n: %6ld\n', dest, const_src, n) |
| 24 | } |
| 25 | $if trace_vmemcpy_nulls ? { |
| 26 | if dest == unsafe { 0 } || const_src == unsafe { 0 } { |
| 27 | C.fprintf(C.stderr, c'vmemcpy null pointers passed, dest: %p src: %p n: %6ld\n', dest, |
| 28 | const_src, n) |
| 29 | print_backtrace() |
| 30 | } |
| 31 | } |
| 32 | if n == 0 || u64(dest) <= 0xFFFF || u64(const_src) <= 0xFFFF { |
| 33 | return dest |
| 34 | } |
| 35 | unsafe { |
| 36 | return C.memcpy(dest, const_src, n) |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | // vmemmove copies n bytes from memory area `src` to memory area `dest`. |
| 41 | // The memory areas *MAY* overlap: copying takes place as though the bytes |
| 42 | // in `src` are first copied into a temporary array that does not overlap |
| 43 | // `src` or `dest`, and the bytes are then copied from the temporary array |
| 44 | // to `dest`. vmemmove returns a pointer to `dest`. |
| 45 | @[inline; unsafe] |
| 46 | pub fn vmemmove(dest voidptr, const_src voidptr, n isize) voidptr { |
| 47 | $if trace_vmemmove ? { |
| 48 | C.fprintf(C.stderr, c'vmemmove dest: %p src: %p n: %6ld\n', dest, const_src, n) |
| 49 | } |
| 50 | if n == 0 || u64(dest) <= 0xFFFF || u64(const_src) <= 0xFFFF { |
| 51 | return dest |
| 52 | } |
| 53 | unsafe { |
| 54 | return C.memmove(dest, const_src, n) |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | // vmemcmp compares the first n bytes (each interpreted as unsigned char) |
| 59 | // of the memory areas s1 and s2. It returns an integer less than, equal to, |
| 60 | // or greater than zero, if the first n bytes of s1 is found, respectively, |
| 61 | // to be less than, to match, or be greater than the first n bytes of s2. |
| 62 | // For a nonzero return value, the sign is determined by the sign of the |
| 63 | // difference between the first pair of bytes (interpreted as unsigned char) |
| 64 | // that differ in s1 and s2. |
| 65 | // If n is zero, the return value is zero. |
| 66 | // Do NOT use vmemcmp to compare security critical data, such as cryptographic |
| 67 | // secrets, because the required CPU time depends on the number of equal bytes. |
| 68 | // You should use a function that performs comparisons in constant time for |
| 69 | // this. |
| 70 | @[inline; unsafe] |
| 71 | pub fn vmemcmp(const_s1 voidptr, const_s2 voidptr, n isize) int { |
| 72 | $if trace_vmemcmp ? { |
| 73 | C.fprintf(C.stderr, c'vmemcmp s1: %p s2: %p n: %6ld\n', const_s1, const_s2, n) |
| 74 | } |
| 75 | if n == 0 || u64(const_s1) <= 0xFFFF || u64(const_s2) <= 0xFFFF { |
| 76 | return 0 |
| 77 | } |
| 78 | unsafe { |
| 79 | return C.memcmp(const_s1, const_s2, n) |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | // vmemset fills the first `n` bytes of the memory area pointed to by `s`, |
| 84 | // with the constant byte `c`. It returns a pointer to the memory area `s`. |
| 85 | @[inline; unsafe] |
| 86 | pub fn vmemset(s voidptr, c int, n isize) voidptr { |
| 87 | $if trace_vmemset ? { |
| 88 | C.fprintf(C.stderr, c'vmemset s: %p c: %d n: %6ld\n', s, c, n) |
| 89 | } |
| 90 | $if trace_vmemset_nulls ? { |
| 91 | if s == unsafe { 0 } { |
| 92 | C.fprintf(C.stderr, c'vmemset null pointers passed s: %p, c: %6d, n: %6ld\n', s, c, n) |
| 93 | print_backtrace() |
| 94 | } |
| 95 | } |
| 96 | if n == 0 || u64(s) <= 0xFFFF { |
| 97 | return s |
| 98 | } |
| 99 | unsafe { |
| 100 | return C.memset(s, c, n) |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | type FnSortCB = fn (const_a voidptr, const_b voidptr) int |
| 105 | |
| 106 | @[inline; unsafe] |
| 107 | fn vsort_ptr_at(base voidptr, index usize, size usize) voidptr { |
| 108 | return unsafe { voidptr(&u8(base) + index * size) } |
| 109 | } |
| 110 | |
| 111 | @[unsafe] |
| 112 | fn vstable_sort_merge(source voidptr, dest voidptr, left usize, mid usize, right usize, size usize, sort_cb FnSortCB) { |
| 113 | mut left_index := left |
| 114 | mut right_index := mid |
| 115 | mut dest_index := left |
| 116 | for left_index < mid && right_index < right { |
| 117 | left_ptr := vsort_ptr_at(source, left_index, size) |
| 118 | right_ptr := vsort_ptr_at(source, right_index, size) |
| 119 | if sort_cb(left_ptr, right_ptr) <= 0 { |
| 120 | vmemcpy(vsort_ptr_at(dest, dest_index, size), left_ptr, isize(size)) |
| 121 | left_index++ |
| 122 | } else { |
| 123 | vmemcpy(vsort_ptr_at(dest, dest_index, size), right_ptr, isize(size)) |
| 124 | right_index++ |
| 125 | } |
| 126 | dest_index++ |
| 127 | } |
| 128 | if left_index < mid { |
| 129 | vmemcpy(vsort_ptr_at(dest, dest_index, size), vsort_ptr_at(source, left_index, size), |
| 130 | isize((mid - left_index) * size)) |
| 131 | } |
| 132 | if right_index < right { |
| 133 | vmemcpy(vsort_ptr_at(dest, dest_index, size), vsort_ptr_at(source, right_index, size), |
| 134 | isize((right - right_index) * size)) |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | @[inline; unsafe] |
| 139 | fn vqsort(base voidptr, nmemb usize, size usize, sort_cb FnSortCB) { |
| 140 | $if trace_vqsort ? { |
| 141 | C.fprintf(C.stderr, c'vqsort base: %p, nmemb: %6uld, size: %6uld, sort_cb: %p\n', base, |
| 142 | nmemb, size, sort_cb) |
| 143 | } |
| 144 | if nmemb < 2 || size == 0 { |
| 145 | return |
| 146 | } |
| 147 | $if trace_vqsort_nulls ? { |
| 148 | if base == unsafe { 0 } || voidptr(sort_cb) == unsafe { nil } { |
| 149 | C.fprintf(C.stderr, |
| 150 | c'vqsort null pointers passed base: %p, nmemb: %6uld, size: %6uld, sort_cb: %p\n', |
| 151 | base, nmemb, size, sort_cb) |
| 152 | print_backtrace() |
| 153 | } |
| 154 | } |
| 155 | total_size := isize(nmemb * size) |
| 156 | buffer := malloc(total_size) |
| 157 | defer { |
| 158 | free(buffer) |
| 159 | } |
| 160 | mut source := base |
| 161 | mut dest := voidptr(buffer) |
| 162 | mut width := usize(1) |
| 163 | for width < nmemb { |
| 164 | mut left := usize(0) |
| 165 | for left < nmemb { |
| 166 | mid := if left + width < nmemb { left + width } else { nmemb } |
| 167 | right := if left + width + width < nmemb { left + width + width } else { nmemb } |
| 168 | vstable_sort_merge(source, dest, left, mid, right, size, sort_cb) |
| 169 | left += width + width |
| 170 | } |
| 171 | mut tmp := source |
| 172 | source = dest |
| 173 | dest = tmp |
| 174 | width += width |
| 175 | } |
| 176 | if source != base { |
| 177 | vmemcpy(base, source, total_size) |
| 178 | } |
| 179 | } |
| 180 | |