DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / wasm / string.wam
1 (module $string
2
3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4
5 ;; Copy len bytes from src to dst
6 ;; Returns len
7 (func $memmove (param $dst i32 $src i32 $len i32)
8 (LET $idx 0)
9 (loop $copy
10 (i32.store8 (i32.add $idx $dst)
11 (i32.load8_u (i32.add $idx $src)))
12 (local.set $idx (i32.add 1 $idx))
13 (br_if $copy (i32.lt_u $idx $len))
14 )
15 )
16
17 (func $strlen (param $str i32) (result i32)
18 (LET $cur $str)
19 (loop $count
20 (if (i32.ne 0 (i32.load8_u $cur))
21 (then
22 (local.set $cur (i32.add $cur 1))
23 (br $count)))
24 )
25 (i32.sub $cur $str)
26 )
27
28 ;; Based on https://stackoverflow.com/a/25705264/471795
29 ;; This could be made much more efficient
30 (func $strstr (param $haystack i32 $needle i32) (result i32)
31 (LET $i 0
32 $needle_len ($strlen $needle)
33 $len ($strlen $haystack))
34
35 (if (i32.eq $needle_len 0) (return $haystack))
36
37 (local.set $i 0)
38 (block $done
39 (loop $loop
40 (if (i32.gt_s $i (i32.sub $len $needle_len)) (br $done))
41
42 (if (AND (i32.eq (i32.load8_u $haystack)
43 (i32.load8_u $needle))
44 (i32.eqz ($strncmp $haystack $needle $needle_len)))
45 (return $haystack))
46 (local.set $haystack (i32.add $haystack 1))
47 (local.set $i (i32.add $i 1))
48 (br $loop)
49 )
50 )
51 0
52 )
53
54 (func $atoi (param $str i32) (result i32)
55 (LET $acc 0
56 $i 0
57 $neg 0
58 $ch 0)
59 (block $done
60 (loop $loop
61 (local.set $ch (i32.load8_u (i32.add $str $i)))
62 (if (AND (i32.ne $ch (CHR "-"))
63 (OR (i32.lt_u $ch (CHR "0"))
64 (i32.gt_u $ch (CHR "9"))))
65 (br $done))
66 (local.set $i (i32.add $i 1))
67 (if (i32.eq $ch (CHR "-"))
68 (then
69 (local.set $neg 1))
70 (else
71 (local.set $acc (i32.add (i32.mul $acc 10)
72 (i32.sub $ch (CHR "0"))))))
73 (br $loop)
74 )
75 )
76 (if (result i32) $neg
77 (then (i32.sub 0 $acc))
78 (else $acc))
79 )
80
81 (func $strcmp (param $s1 i32 $s2 i32) (result i32)
82 (block $done
83 (loop $loop
84 (if (OR (i32.eqz (i32.load8_u $s1)) (i32.eqz (i32.load8_u $s2)))
85 (br $done))
86 (if (i32.ne (i32.load8_u $s1) (i32.load8_u $s2))
87 (br $done))
88 (local.set $s1 (i32.add $s1 1))
89 (local.set $s2 (i32.add $s2 1))
90 (br $loop)
91 )
92 )
93 (if (result i32) (i32.eq (i32.load8_u $s1) (i32.load8_u $s2))
94 (then 0)
95 (else
96 (if (result i32) (i32.lt_u (i32.load8_u $s1) (i32.load8_u $s2))
97 (then -1)
98 (else 1))))
99 )
100
101 (func $strncmp (param $s1 i32 $s2 i32 $len i32) (result i32)
102 (LET $i 0)
103 (if (i32.eq $len 0) (return 0))
104 (block $done
105 (loop $loop
106 (if (i32.ge_u $i $len) (br $done))
107 (if (i32.eqz (i32.load8_u (i32.add $i $s1))) (br $done))
108 (if (i32.ne (i32.load8_u (i32.add $i $s1))
109 (i32.load8_u (i32.add $i $s2))) (br $done))
110 (local.set $i (i32.add $i 1))
111 (br $loop)
112 )
113 )
114 (if (OR (i32.eq $i $len)
115 (i32.eq (i32.load8_u (i32.add $i $s1))
116 (i32.load8_u (i32.add $i $s2))))
117 (return 0))
118 (if (result i32) (i32.lt_u (i32.load8_u (i32.add $i $s1))
119 (i32.load8_u (i32.add $i $s2)))
120 (then -1)
121 (else 1))
122 )
123
124 ;; Writes new string to grass with all needles in haystack replaced.
125 ;; If the length of replace is equal to of less than needle then
126 ;; grass can be NULL.
127 ;; Returns length of grass.
128 (func $REPLACE3 (param $grass i32 $haystack i32
129 $needle0 i32 $replace0 i32
130 $needle1 i32 $replace1 i32
131 $needle2 i32 $replace2 i32) (result i32)
132 (LET $haystack_len ($strlen $haystack)
133 $src_str $haystack
134 $dst_str $grass
135 $s 0 $found_tmp 0 $found 0
136 $needle 0 $replace 0 $needle_len 0 $replace_len 0
137 $replace_s 0 $replace_len_s 0 $needle_len_s 0)
138
139 ;; in-place
140 (if (i32.eqz $grass)
141 (then
142 ;; check that we aren't expanding in place
143 (local.set $s 0)
144 (block $done
145 (loop $loop
146 (if (i32.ge_u $s 3) (br $done))
147 (local.set $needle (if (result i32) (i32.eq $s 0) $needle0
148 (if (result i32) (i32.eq $s 1) $needle1
149 $needle2)))
150 (local.set $replace (if (result i32) (i32.eq $s 0) $replace0
151 (if (result i32) (i32.eq $s 1) $replace1
152 $replace2)))
153 (local.set $needle_len ($strlen $needle))
154 (local.set $replace_len ($strlen $replace))
155 (if (i32.gt_u $replace_len $needle_len)
156 ($fatal 7 "REPLACE: invalid expanding in-place call\n"))
157 (local.set $s (i32.add $s 1))
158 (br $loop)
159 )
160 )
161 (local.set $grass $haystack)
162 (local.set $dst_str $grass)))
163
164 (block $done1
165 (loop $loop1
166 (if (i32.ge_s (i32.sub $src_str $haystack) $haystack_len)
167 (br $done1))
168
169 ;; Find the earliest match
170 (local.set $found 0)
171 (local.set $s 0)
172 (block $done2
173 (loop $loop2
174 (if (i32.ge_u $s 3) (br $done2))
175 (local.set $needle (if (result i32) (i32.eq $s 0) $needle0
176 (if (result i32) (i32.eq $s 1) $needle1
177 $needle2)))
178 (local.set $replace (if (result i32) (i32.eq $s 0) $replace0
179 (if (result i32) (i32.eq $s 1) $replace1
180 $replace2)))
181 (local.set $s (i32.add $s 1))
182 (local.set $found_tmp ($strstr $src_str $needle))
183 (if (i32.eqz $found_tmp) (br $loop2))
184 (if (OR (i32.eqz $found) (i32.lt_s $found_tmp $found))
185 (then
186 (local.set $found $found_tmp)
187 (local.set $needle_len_s ($strlen $needle))
188 (local.set $replace_s $replace)
189 (local.set $replace_len_s ($strlen $replace))))
190 (br $loop2)
191 )
192 )
193 (if (i32.eqz $found) (br $done1))
194 ;; copy before the match
195 ($memmove $dst_str $src_str (i32.add (i32.sub $found $src_str) 1))
196 (local.set $dst_str (i32.add $dst_str (i32.sub $found $src_str)))
197 ;; add the replace string
198 ($memmove $dst_str $replace_s (i32.add $replace_len_s 1))
199 (local.set $dst_str (i32.add $dst_str $replace_len_s))
200 ;; Move to after the match
201 (local.set $src_str (i32.add $found $needle_len_s))
202 (br $loop1)
203 )
204 )
205
206 ;; Copy the left-over
207 ($memmove $dst_str $src_str ($strlen $src_str))
208 (local.set $dst_str (i32.add $dst_str ($strlen $src_str)))
209 (i32.store8 $dst_str (CHR "\x00"))
210
211 (i32.sub $dst_str $grass)
212 )
213
214 )
215