mt-lang 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/docs/index.html +4 -3
  3. data/lib/milk_tea/base.rb +1 -1
  4. data/std/asset_pack.mt +267 -0
  5. data/std/async/libuv_runtime.mt +539 -0
  6. data/std/async/mailbox.mt +191 -0
  7. data/std/async/runtime.mt +87 -0
  8. data/std/async.mt +87 -0
  9. data/std/base64.mt +185 -0
  10. data/std/behavior_tree.mt +396 -0
  11. data/std/binary.mt +314 -0
  12. data/std/binary_heap.mt +157 -0
  13. data/std/bitset.mt +239 -0
  14. data/std/box2d.mt +535 -0
  15. data/std/bytes.mt +46 -0
  16. data/std/c/box2d.mt +1254 -0
  17. data/std/c/cgltf.mt +647 -0
  18. data/std/c/cjson.mt +106 -0
  19. data/std/c/crypto.mt +11 -0
  20. data/std/c/crypto_support.h +51 -0
  21. data/std/c/ctype.mt +19 -0
  22. data/std/c/ctype_bindgen.h +29 -0
  23. data/std/c/curl.mt +1223 -0
  24. data/std/c/enet.mt +473 -0
  25. data/std/c/errno.mt +41 -0
  26. data/std/c/errno_bindgen.h +53 -0
  27. data/std/c/flecs.mt +2945 -0
  28. data/std/c/fs.linux.mt +43 -0
  29. data/std/c/fs.windows.mt +43 -0
  30. data/std/c/fs_support.h +954 -0
  31. data/std/c/gl.mt +2065 -0
  32. data/std/c/gl_registry_helpers.h +10748 -0
  33. data/std/c/glfw.mt +499 -0
  34. data/std/c/libc.mt +167 -0
  35. data/std/c/libuv.mt +1491 -0
  36. data/std/c/math.mt +22 -0
  37. data/std/c/math_bindgen.h +31 -0
  38. data/std/c/miniaudio.mt +3420 -0
  39. data/std/c/pcre2.mt +735 -0
  40. data/std/c/process.mt +58 -0
  41. data/std/c/process_support.h +1147 -0
  42. data/std/c/raygui.mt +1602 -0
  43. data/std/c/raylib.mt +1245 -0
  44. data/std/c/raymath.mt +163 -0
  45. data/std/c/rlgl.mt +434 -0
  46. data/std/c/rpng.mt +60 -0
  47. data/std/c/rres.mt +187 -0
  48. data/std/c/sdl3.mt +4132 -0
  49. data/std/c/sqlite3.mt +951 -0
  50. data/std/c/stb_image.mt +52 -0
  51. data/std/c/stb_image_resize2.mt +128 -0
  52. data/std/c/stb_image_write.mt +19 -0
  53. data/std/c/stb_rect_pack.mt +38 -0
  54. data/std/c/stb_truetype.mt +175 -0
  55. data/std/c/stb_vorbis.mt +31 -0
  56. data/std/c/stdio.mt +90 -0
  57. data/std/c/steamworks.h +5769 -0
  58. data/std/c/steamworks.mt +4771 -0
  59. data/std/c/string.mt +16 -0
  60. data/std/c/string_bindgen.h +36 -0
  61. data/std/c/sync.mt +33 -0
  62. data/std/c/sync_support.h +219 -0
  63. data/std/c/terminal.mt +24 -0
  64. data/std/c/terminal_support.h +408 -0
  65. data/std/c/time.mt +26 -0
  66. data/std/c/tls.mt +24 -0
  67. data/std/c/tls_support.h +626 -0
  68. data/std/c/tracy.mt +53 -0
  69. data/std/c/zlib.mt +20 -0
  70. data/std/c/zlib_support.h +239 -0
  71. data/std/c/zstd.mt +231 -0
  72. data/std/cell.mt +60 -0
  73. data/std/cgltf.mt +115 -0
  74. data/std/cjson.mt +90 -0
  75. data/std/cli.mt +768 -0
  76. data/std/cookie.mt +198 -0
  77. data/std/counter.mt +160 -0
  78. data/std/crypto.mt +74 -0
  79. data/std/cstring.mt +13 -0
  80. data/std/ctype.mt +52 -0
  81. data/std/curl/runtime.mt +261 -0
  82. data/std/curl.mt +38 -0
  83. data/std/deque.mt +348 -0
  84. data/std/encoding.mt +93 -0
  85. data/std/enet.mt +133 -0
  86. data/std/env.mt +41 -0
  87. data/std/errno.mt +47 -0
  88. data/std/flecs.mt +978 -0
  89. data/std/fmt.mt +271 -0
  90. data/std/fs.linux.mt +696 -0
  91. data/std/fs.windows.mt +711 -0
  92. data/std/fsm.mt +267 -0
  93. data/std/gl.mt +2062 -0
  94. data/std/glfw.mt +471 -0
  95. data/std/goap.mt +342 -0
  96. data/std/graph.mt +521 -0
  97. data/std/gzip.mt +77 -0
  98. data/std/hash.mt +429 -0
  99. data/std/http/server.mt +810 -0
  100. data/std/http.mt +1227 -0
  101. data/std/intern.mt +53 -0
  102. data/std/jobs.mt +288 -0
  103. data/std/json.mt +627 -0
  104. data/std/libc.mt +34 -0
  105. data/std/libuv.mt +554 -0
  106. data/std/linear_algebra.mt +243 -0
  107. data/std/linked_map.mt +372 -0
  108. data/std/linked_map_view.mt +49 -0
  109. data/std/linked_set.mt +117 -0
  110. data/std/log.mt +69 -0
  111. data/std/map.mt +418 -0
  112. data/std/math.mt +24 -0
  113. data/std/mem/arena.mt +137 -0
  114. data/std/mem/endian.mt +51 -0
  115. data/std/mem/heap.mt +298 -0
  116. data/std/mem/pool.mt +164 -0
  117. data/std/mem/stack.mt +47 -0
  118. data/std/mem/tracking.mt +119 -0
  119. data/std/miniaudio.mt +1299 -0
  120. data/std/multiset.mt +191 -0
  121. data/std/net/channel.mt +727 -0
  122. data/std/net/clock.mt +237 -0
  123. data/std/net/discovery.mt +277 -0
  124. data/std/net/lobby.mt +813 -0
  125. data/std/net/manager.mt +539 -0
  126. data/std/net/mux.mt +839 -0
  127. data/std/net/nat.mt +122 -0
  128. data/std/net/packet.mt +219 -0
  129. data/std/net/punch.mt +162 -0
  130. data/std/net/rpc.mt +119 -0
  131. data/std/net/session.mt +1157 -0
  132. data/std/net/stun.mt +252 -0
  133. data/std/net/sync.mt +183 -0
  134. data/std/net/turn.mt +459 -0
  135. data/std/net.mt +2944 -0
  136. data/std/oauth2.mt +408 -0
  137. data/std/option.mt +51 -0
  138. data/std/ordered_map.mt +497 -0
  139. data/std/ordered_set.mt +348 -0
  140. data/std/path.mt +368 -0
  141. data/std/pcre2/runtime.mt +35 -0
  142. data/std/pcre2.mt +78 -0
  143. data/std/priority_queue.mt +55 -0
  144. data/std/process.mt +837 -0
  145. data/std/queue.mt +55 -0
  146. data/std/random.mt +146 -0
  147. data/std/raygui.mt +99 -0
  148. data/std/raylib/debug_console.mt +71 -0
  149. data/std/raylib/easing.mt +388 -0
  150. data/std/raylib/packed_assets.mt +255 -0
  151. data/std/raylib/runtime.mt +30 -0
  152. data/std/raylib/tracy_gpu.mt +37 -0
  153. data/std/raylib.mt +1510 -0
  154. data/std/raymath.mt +153 -0
  155. data/std/result.mt +89 -0
  156. data/std/rlgl.mt +268 -0
  157. data/std/rpng.mt +46 -0
  158. data/std/rres.mt +36 -0
  159. data/std/sdl3/runtime.mt +54 -0
  160. data/std/sdl3.mt +1731 -0
  161. data/std/serialize.mt +68 -0
  162. data/std/set.mt +124 -0
  163. data/std/spatial.mt +176 -0
  164. data/std/sqlite3.mt +151 -0
  165. data/std/stack.mt +55 -0
  166. data/std/stb_image.mt +47 -0
  167. data/std/stb_image_resize2.mt +41 -0
  168. data/std/stb_image_write.mt +17 -0
  169. data/std/stb_rect_pack.mt +15 -0
  170. data/std/stb_truetype.mt +77 -0
  171. data/std/stb_vorbis.mt +16 -0
  172. data/std/stdio.mt +88 -0
  173. data/std/steamworks.mt +1542 -0
  174. data/std/str.mt +293 -0
  175. data/std/string.mt +234 -0
  176. data/std/sync.mt +194 -0
  177. data/std/tar.mt +704 -0
  178. data/std/terminal.mt +1002 -0
  179. data/std/testing.mt +266 -0
  180. data/std/thread.mt +120 -0
  181. data/std/time.mt +105 -0
  182. data/std/tls.mt +616 -0
  183. data/std/toml.mt +1310 -0
  184. data/std/tracy.mt +42 -0
  185. data/std/uri.mt +118 -0
  186. data/std/url.mt +372 -0
  187. data/std/vec.mt +433 -0
  188. data/std/zstd.mt +94 -0
  189. metadata +187 -2
@@ -0,0 +1,348 @@
1
+ import std.mem.heap as heap
2
+
3
+ struct Node[T]:
4
+ value: T
5
+ left: ptr[Node[T]]?
6
+ right: ptr[Node[T]]?
7
+ parent: ptr[Node[T]]?
8
+ height: int
9
+
10
+ struct SearchResult[T]:
11
+ node: ptr[Node[T]]?
12
+ parent: ptr[Node[T]]?
13
+ compare: int
14
+ found: bool
15
+
16
+ public struct Iter[T]:
17
+ node: ptr[Node[T]]?
18
+
19
+ public struct OrderedSet[T]:
20
+ root: ptr[Node[T]]?
21
+ len: ptr_uint
22
+
23
+
24
+ extending OrderedSet[T]:
25
+ public static function create() -> OrderedSet[T]:
26
+ return OrderedSet[T](root = null, len = 0)
27
+
28
+
29
+ static function height(node: ptr[Node[T]]?) -> int:
30
+ if node == null:
31
+ return 0
32
+
33
+ unsafe:
34
+ return read(ptr[Node[T]]<-node).height
35
+
36
+
37
+ static function update_height(node: ptr[Node[T]]) -> void:
38
+ let left_height = unsafe: OrderedSet[T].height(read(node).left)
39
+ let right_height = unsafe: OrderedSet[T].height(read(node).right)
40
+ var next_height = left_height
41
+ if right_height > next_height:
42
+ next_height = right_height
43
+
44
+ unsafe:
45
+ read(node).height = next_height + 1
46
+
47
+
48
+ static function balance_factor(node: ptr[Node[T]]?) -> int:
49
+ if node == null:
50
+ return 0
51
+
52
+ unsafe:
53
+ return OrderedSet[T].height(read(ptr[Node[T]]<-node).left) - OrderedSet[T].height(read(ptr[Node[T]]<-node).right)
54
+
55
+
56
+ static function minimum(node: ptr[Node[T]]?) -> ptr[Node[T]]?:
57
+ var current = node
58
+ while current != null:
59
+ let left = unsafe: read(ptr[Node[T]]<-current).left else:
60
+ return current
61
+
62
+ current = left
63
+
64
+ return null
65
+
66
+
67
+ static function successor(node: ptr[Node[T]]?) -> ptr[Node[T]]?:
68
+ if node == null:
69
+ return null
70
+
71
+ let current = unsafe: ptr[Node[T]]<-node
72
+ let right = unsafe: read(current).right
73
+ if right != null:
74
+ return OrderedSet[T].minimum(right)
75
+
76
+ var child = node
77
+ var parent = unsafe: read(current).parent
78
+ while parent != null:
79
+ let parent_ptr = unsafe: ptr[Node[T]]<-parent
80
+ if unsafe: read(parent_ptr).left == child:
81
+ return parent
82
+
83
+ child = parent
84
+ parent = unsafe: read(parent_ptr).parent
85
+
86
+ return null
87
+
88
+
89
+ static function replace_child(
90
+ current: ref[OrderedSet[T]],
91
+ parent: ptr[Node[T]]?,
92
+ previous: ptr[Node[T]],
93
+ replacement: ptr[Node[T]]?
94
+ ) -> void:
95
+ if parent == null:
96
+ current.root = replacement
97
+ else:
98
+ unsafe:
99
+ let parent_ptr = ptr[Node[T]]<-parent
100
+ if read(parent_ptr).left == previous:
101
+ read(parent_ptr).left = replacement
102
+ else if read(parent_ptr).right == previous:
103
+ read(parent_ptr).right = replacement
104
+ else:
105
+ fatal(c"ordered_set.OrderedSet.replace_child missing previous child")
106
+
107
+ if replacement != null:
108
+ unsafe:
109
+ read(ptr[Node[T]]<-replacement).parent = parent
110
+
111
+
112
+ static function rotate_left(current: ref[OrderedSet[T]], node: ptr[Node[T]]) -> ptr[Node[T]]:
113
+ let pivot = unsafe: read(node).right else:
114
+ fatal(c"ordered_set.OrderedSet.rotate_left missing pivot")
115
+
116
+ let pivot_ptr = unsafe: ptr[Node[T]]<-pivot
117
+ let parent = unsafe: read(node).parent
118
+ let pivot_left = unsafe: read(pivot_ptr).left
119
+
120
+ unsafe:
121
+ read(node).right = pivot_left
122
+ if pivot_left != null:
123
+ unsafe:
124
+ read(ptr[Node[T]]<-pivot_left).parent = node
125
+
126
+ OrderedSet[T].replace_child(current, parent, node, pivot)
127
+
128
+ unsafe:
129
+ read(pivot_ptr).left = node
130
+ read(node).parent = pivot
131
+
132
+ OrderedSet[T].update_height(node)
133
+ OrderedSet[T].update_height(pivot_ptr)
134
+ return pivot_ptr
135
+
136
+
137
+ static function rotate_right(current: ref[OrderedSet[T]], node: ptr[Node[T]]) -> ptr[Node[T]]:
138
+ let pivot = unsafe: read(node).left else:
139
+ fatal(c"ordered_set.OrderedSet.rotate_right missing pivot")
140
+
141
+ let pivot_ptr = unsafe: ptr[Node[T]]<-pivot
142
+ let parent = unsafe: read(node).parent
143
+ let pivot_right = unsafe: read(pivot_ptr).right
144
+
145
+ unsafe:
146
+ read(node).left = pivot_right
147
+ if pivot_right != null:
148
+ unsafe:
149
+ read(ptr[Node[T]]<-pivot_right).parent = node
150
+
151
+ OrderedSet[T].replace_child(current, parent, node, pivot)
152
+
153
+ unsafe:
154
+ read(pivot_ptr).right = node
155
+ read(node).parent = pivot
156
+
157
+ OrderedSet[T].update_height(node)
158
+ OrderedSet[T].update_height(pivot_ptr)
159
+ return pivot_ptr
160
+
161
+
162
+ static function rebalance(current: ref[OrderedSet[T]], node: ptr[Node[T]]?) -> void:
163
+ var cursor = node
164
+ while cursor != null:
165
+ let cursor_ptr = unsafe: ptr[Node[T]]<-cursor
166
+ OrderedSet[T].update_height(cursor_ptr)
167
+
168
+ let balance = OrderedSet[T].balance_factor(cursor)
169
+ if balance > 1:
170
+ let left = unsafe: read(cursor_ptr).left
171
+ if OrderedSet[T].balance_factor(left) < 0:
172
+ if left == null:
173
+ fatal(c"ordered_set.OrderedSet.rebalance missing left child")
174
+ OrderedSet[T].rotate_left(current, unsafe: ptr[Node[T]]<-left)
175
+
176
+ let rotated = OrderedSet[T].rotate_right(current, cursor_ptr)
177
+ cursor = unsafe: read(rotated).parent
178
+ else if balance < -1:
179
+ let right = unsafe: read(cursor_ptr).right
180
+ if OrderedSet[T].balance_factor(right) > 0:
181
+ if right == null:
182
+ fatal(c"ordered_set.OrderedSet.rebalance missing right child")
183
+ OrderedSet[T].rotate_right(current, unsafe: ptr[Node[T]]<-right)
184
+
185
+ let rotated = OrderedSet[T].rotate_left(current, cursor_ptr)
186
+ cursor = unsafe: read(rotated).parent
187
+ else:
188
+ cursor = unsafe: read(cursor_ptr).parent
189
+
190
+
191
+ static function locate(current: OrderedSet[T], value: T) -> SearchResult[T]:
192
+ var parent: ptr[Node[T]]? = null
193
+ var node = current.root
194
+ var compare = 0
195
+ while node != null:
196
+ let node_ptr = unsafe: ptr[Node[T]]<-node
197
+ let stored = unsafe: const_ptr_of(read(node_ptr).value)
198
+ compare = order[T](value, stored)
199
+ if compare < 0:
200
+ parent = node
201
+ unsafe:
202
+ node = read(node_ptr).left
203
+ else if compare > 0:
204
+ parent = node
205
+ unsafe:
206
+ node = read(node_ptr).right
207
+ else:
208
+ return SearchResult[T](node = node, parent = parent, compare = 0, found = true)
209
+
210
+ return SearchResult[T](node = null, parent = parent, compare = compare, found = false)
211
+
212
+
213
+ static function release_subtree(node: ptr[Node[T]]?) -> void:
214
+ if node == null:
215
+ return
216
+
217
+ let node_ptr = unsafe: ptr[Node[T]]<-node
218
+ let left = unsafe: read(node_ptr).left
219
+ let right = unsafe: read(node_ptr).right
220
+ OrderedSet[T].release_subtree(left)
221
+ OrderedSet[T].release_subtree(right)
222
+ heap.release(node)
223
+
224
+
225
+ static function detach_node(current: ref[OrderedSet[T]], node: ptr[Node[T]]) -> ptr[Node[T]]?:
226
+ let left = unsafe: read(node).left
227
+ let right = unsafe: read(node).right
228
+ if left != null and right != null:
229
+ let successor = OrderedSet[T].minimum(right) else:
230
+ fatal(c"ordered_set.OrderedSet.detach_node missing successor")
231
+
232
+ unsafe:
233
+ read(node).value = read(ptr[Node[T]]<-successor).value
234
+
235
+ return OrderedSet[T].detach_node(current, unsafe: ptr[Node[T]]<-successor)
236
+
237
+ var child = left
238
+ if child == null:
239
+ child = right
240
+
241
+ let parent = unsafe: read(node).parent
242
+ OrderedSet[T].replace_child(current, parent, node, child)
243
+ heap.release(node)
244
+
245
+ if parent != null:
246
+ return parent
247
+
248
+ return child
249
+
250
+
251
+ public function len() -> ptr_uint:
252
+ return this.len
253
+
254
+
255
+ public function is_empty() -> bool:
256
+ return this.len == 0
257
+
258
+
259
+ public function get(value: T) -> const_ptr[T]?:
260
+ let location = OrderedSet[T].locate(this, value)
261
+ if not location.found:
262
+ return null
263
+
264
+ let candidate = location.node else:
265
+ fatal(c"ordered_set.OrderedSet.get missing node")
266
+
267
+ unsafe:
268
+ return const_ptr_of(read(ptr[Node[T]]<-candidate).value)
269
+
270
+
271
+ public function at(value: T) -> Option[T]:
272
+ let p = this.get(value) else:
273
+ return Option[T].none
274
+
275
+ unsafe:
276
+ return Option[T].some(value = read(ptr[T]<-p))
277
+
278
+
279
+ public function contains(value: T) -> bool:
280
+ return this.get(value) != null
281
+
282
+
283
+ public function iter() -> Iter[T]:
284
+ return Iter[T](node = OrderedSet[T].minimum(this.root))
285
+
286
+
287
+ public editable function clear() -> void:
288
+ OrderedSet[T].release_subtree(this.root)
289
+ this.root = null
290
+ this.len = 0
291
+
292
+
293
+ public editable function release() -> void:
294
+ this.clear()
295
+
296
+
297
+ public editable function insert(value: T) -> bool:
298
+ let location = OrderedSet[T].locate(this, value)
299
+ if location.found:
300
+ return false
301
+
302
+ let node = heap.must_alloc[Node[T]](1)
303
+ unsafe:
304
+ read(node) = Node[T](value = value, left = null, right = null, parent = location.parent, height = 1)
305
+
306
+ let parent = location.parent
307
+ if parent == null:
308
+ this.root = node
309
+ else:
310
+ unsafe:
311
+ let parent_ptr = ptr[Node[T]]<-parent
312
+ if location.compare < 0:
313
+ read(parent_ptr).left = node
314
+ else:
315
+ read(parent_ptr).right = node
316
+
317
+ this.len += 1
318
+ OrderedSet[T].rebalance(this, parent)
319
+ return true
320
+
321
+
322
+ public editable function remove(value: T) -> bool:
323
+ let location = OrderedSet[T].locate(this, value)
324
+ if not location.found:
325
+ return false
326
+
327
+ let node = location.node else:
328
+ fatal(c"ordered_set.OrderedSet.remove missing node")
329
+
330
+ let rebalance_from = OrderedSet[T].detach_node(this, unsafe: ptr[Node[T]]<-node)
331
+ this.len -= 1
332
+ OrderedSet[T].rebalance(this, rebalance_from)
333
+ return true
334
+
335
+
336
+ extending Iter[T]:
337
+ public function iter() -> Iter[T]:
338
+ return this
339
+
340
+
341
+ public editable function next() -> const_ptr[T]?:
342
+ let current = this.node else:
343
+ return null
344
+
345
+ this.node = OrderedSet[T].successor(current)
346
+
347
+ unsafe:
348
+ return const_ptr_of(read(ptr[Node[T]]<-current).value)
data/std/path.mt ADDED
@@ -0,0 +1,368 @@
1
+ import std.str as text_ops
2
+ import std.string as string
3
+ import std.vec as vec
4
+
5
+ struct Segment:
6
+ start: ptr_uint
7
+ len: ptr_uint
8
+
9
+
10
+ public function is_absolute(path: str) -> bool:
11
+ return root_length(path) != 0
12
+
13
+
14
+ public function normalize_separators(path: str) -> string.String:
15
+ var result = string.String.with_capacity(path.len)
16
+
17
+ var index: ptr_uint = 0
18
+ while index < path.len:
19
+ let value = path.byte_at(index)
20
+ if value == 92:
21
+ result.push_byte(47)
22
+ else:
23
+ result.push_byte(value)
24
+ index += 1
25
+
26
+ return result
27
+
28
+
29
+ public function join(left: str, right: str) -> string.String:
30
+ if left.len == 0:
31
+ return string.String.from_str(right)
32
+
33
+ if right.len == 0:
34
+ return string.String.from_str(left)
35
+
36
+ if is_absolute(right):
37
+ return string.String.from_str(right)
38
+
39
+ var result = string.String.from_str(left)
40
+ let last = result.as_str().byte_at(result.len() - 1)
41
+ if not is_separator(last):
42
+ result.append("/")
43
+
44
+ var start: ptr_uint = 0
45
+ while start < right.len and is_separator(right.byte_at(start)):
46
+ start += 1
47
+
48
+ if start == right.len:
49
+ return result
50
+
51
+ result.append(right.slice(start, right.len - start))
52
+ return result
53
+
54
+
55
+ public function relative_path(path: str, base: str) -> Option[string.String]:
56
+ var normalized_path = normalize_separators(path)
57
+ defer normalized_path.release()
58
+ var normalized_base = normalize_separators(base)
59
+ defer normalized_base.release()
60
+
61
+ let path_text = normalized_path.as_str()
62
+ let base_text = normalized_base.as_str()
63
+ let path_root = root_length(path_text)
64
+ let base_root = root_length(base_text)
65
+ if not roots_compatible(path_text, path_root, base_text, base_root):
66
+ return Option[string.String].none
67
+
68
+ var path_segments = vec.Vec[Segment].create()
69
+ defer path_segments.release()
70
+ collect_normalized_segments(path_text, path_root, ref_of(path_segments))
71
+
72
+ var base_segments = vec.Vec[Segment].create()
73
+ defer base_segments.release()
74
+ collect_normalized_segments(base_text, base_root, ref_of(base_segments))
75
+
76
+ var common: ptr_uint = 0
77
+ let shared_len = min_ptr_uint(path_segments.len(), base_segments.len())
78
+ while common < shared_len:
79
+ let path_segment_ptr = path_segments.get(common) else:
80
+ fatal(c"path.relative_path missing path segment")
81
+ let base_segment_ptr = base_segments.get(common) else:
82
+ fatal(c"path.relative_path missing base segment")
83
+
84
+ unsafe:
85
+ if not segments_equal(path_text, read(path_segment_ptr), base_text, read(base_segment_ptr)):
86
+ break
87
+
88
+ common += 1
89
+
90
+ var result = string.String.with_capacity(path_text.len + base_text.len)
91
+ var index: ptr_uint = common
92
+ while index < base_segments.len():
93
+ append_relative_segment(ref_of(result), "..")
94
+ index += 1
95
+
96
+ index = common
97
+ while index < path_segments.len():
98
+ let path_segment_ptr = path_segments.get(index) else:
99
+ fatal(c"path.relative_path missing path segment for append")
100
+
101
+ unsafe:
102
+ append_relative_segment(ref_of(result), segment_text(path_text, read(path_segment_ptr)))
103
+
104
+ index += 1
105
+
106
+ if result.is_empty():
107
+ result.append(".")
108
+
109
+ return Option[string.String].some(value= result)
110
+
111
+
112
+ public function is_within_root(path: str, root: str) -> bool:
113
+ var normalized_path = normalize_separators(path)
114
+ defer normalized_path.release()
115
+ var normalized_root = normalize_separators(root)
116
+ defer normalized_root.release()
117
+
118
+ let path_text = normalized_path.as_str()
119
+ let root_text = normalized_root.as_str()
120
+ let path_root = root_length(path_text)
121
+ let root_root = root_length(root_text)
122
+ if not roots_compatible(path_text, path_root, root_text, root_root):
123
+ return false
124
+
125
+ var path_segments = vec.Vec[Segment].create()
126
+ defer path_segments.release()
127
+ collect_normalized_segments(path_text, path_root, ref_of(path_segments))
128
+
129
+ var root_segments = vec.Vec[Segment].create()
130
+ defer root_segments.release()
131
+ collect_normalized_segments(root_text, root_root, ref_of(root_segments))
132
+
133
+ if path_segments.len() < root_segments.len():
134
+ return false
135
+
136
+ var index: ptr_uint = 0
137
+ while index < root_segments.len():
138
+ let path_segment_ptr = path_segments.get(index) else:
139
+ fatal(c"path.is_within_root missing path segment")
140
+ let root_segment_ptr = root_segments.get(index) else:
141
+ fatal(c"path.is_within_root missing root segment")
142
+
143
+ unsafe:
144
+ if not segments_equal(path_text, read(path_segment_ptr), root_text, read(root_segment_ptr)):
145
+ return false
146
+
147
+ index += 1
148
+
149
+ return true
150
+
151
+
152
+ public function basename(path: str) -> str:
153
+ if path.len == 0:
154
+ return "."
155
+
156
+ let root = root_length(path)
157
+ let stop = trim_trailing_separators(path, root)
158
+ if stop == root:
159
+ if root == 0:
160
+ return "."
161
+
162
+ return path.slice(0, root)
163
+
164
+ var index = stop
165
+ while index > root:
166
+ if is_separator(path.byte_at(index - 1)):
167
+ return path.slice(index, stop - index)
168
+ index -= 1
169
+
170
+ if root != 0:
171
+ return path.slice(root, stop - root)
172
+
173
+ return path.slice(0, stop)
174
+
175
+
176
+ public function dirname(path: str) -> str:
177
+ if path.len == 0:
178
+ return "."
179
+
180
+ let root = root_length(path)
181
+ let stop = trim_trailing_separators(path, root)
182
+ if stop == root:
183
+ if root == 0:
184
+ return "."
185
+
186
+ return path.slice(0, root)
187
+
188
+ var index = stop
189
+ while index > root:
190
+ if is_separator(path.byte_at(index - 1)):
191
+ var dir_stop = index - 1
192
+ while dir_stop > root and is_separator(path.byte_at(dir_stop - 1)):
193
+ dir_stop -= 1
194
+
195
+ if dir_stop == 0:
196
+ return "."
197
+
198
+ return path.slice(0, dir_stop)
199
+ index -= 1
200
+
201
+ if root != 0:
202
+ return path.slice(0, root)
203
+
204
+ return "."
205
+
206
+
207
+ public function extension(path: str) -> Option[str]:
208
+ let name = basename(path)
209
+ match extension_dot(name):
210
+ Option.some as payload:
211
+ return Option[str].some(value= name.slice(payload.value, name.len - payload.value))
212
+ Option.none:
213
+ return Option[str].none
214
+
215
+
216
+ public function stem(path: str) -> str:
217
+ let name = basename(path)
218
+ return name.slice(0, extension_dot(name).unwrap_or(name.len))
219
+
220
+
221
+ function root_length(path: str) -> ptr_uint:
222
+ if path.len == 0:
223
+ return 0
224
+
225
+ if is_separator(path.byte_at(0)):
226
+ return 1
227
+
228
+ if path.len >= 3 and is_ascii_letter(path.byte_at(0)) and path.byte_at(1) == 58 and is_separator(path.byte_at(2)):
229
+ return 3
230
+
231
+ return 0
232
+
233
+
234
+ function roots_compatible(left: str, left_root: ptr_uint, right: str, right_root: ptr_uint) -> bool:
235
+ if left_root != right_root:
236
+ return false
237
+
238
+ if left_root == 0:
239
+ return true
240
+
241
+ if left_root == 1:
242
+ return true
243
+
244
+ if left_root == 3 and left.byte_at(1) == 58 and right.byte_at(1) == 58:
245
+ return ascii_fold(left.byte_at(0)) == ascii_fold(right.byte_at(0))
246
+
247
+ var index: ptr_uint = 0
248
+ while index < left_root:
249
+ if left.byte_at(index) != right.byte_at(index):
250
+ return false
251
+
252
+ index += 1
253
+
254
+ return true
255
+
256
+
257
+ function ascii_fold(value: ubyte) -> ubyte:
258
+ if value >= 65 and value <= 90:
259
+ return value + 32
260
+
261
+ return value
262
+
263
+
264
+ function collect_normalized_segments(path: str, root: ptr_uint, output: ref[vec.Vec[Segment]]) -> void:
265
+ var index = root
266
+ while index < path.len:
267
+ while index < path.len and is_separator(path.byte_at(index)):
268
+ index += 1
269
+
270
+ if index == path.len:
271
+ return
272
+
273
+ let segment_start = index
274
+ while index < path.len and not is_separator(path.byte_at(index)):
275
+ index += 1
276
+
277
+ let segment_len = index - segment_start
278
+ if segment_is_current_directory(path, segment_start, segment_len):
279
+ continue
280
+
281
+ if segment_is_parent_directory(path, segment_start, segment_len):
282
+ let last_segment_ptr = output.last()
283
+ if last_segment_ptr != null:
284
+ unsafe:
285
+ let last_segment = read(last_segment_ptr)
286
+ if not segment_is_parent_directory(path, last_segment.start, last_segment.len):
287
+ match output.pop():
288
+ Option.some:
289
+ pass
290
+ Option.none:
291
+ fatal(c"path.collect_normalized_segments failed to pop segment")
292
+ continue
293
+
294
+ if root == 0:
295
+ output.push(Segment(start = segment_start, len = segment_len))
296
+
297
+ continue
298
+
299
+ output.push(Segment(start = segment_start, len = segment_len))
300
+
301
+
302
+ function segment_is_current_directory(path: str, start: ptr_uint, len: ptr_uint) -> bool:
303
+ return len == 1 and path.byte_at(start) == 46
304
+
305
+
306
+ function segment_is_parent_directory(path: str, start: ptr_uint, len: ptr_uint) -> bool:
307
+ return len == 2 and path.byte_at(start) == 46 and path.byte_at(start + 1) == 46
308
+
309
+
310
+ function segments_equal(left: str, left_segment: Segment, right: str, right_segment: Segment) -> bool:
311
+ if left_segment.len != right_segment.len:
312
+ return false
313
+
314
+ var index: ptr_uint = 0
315
+ while index < left_segment.len:
316
+ if left.byte_at(left_segment.start + index) != right.byte_at(right_segment.start + index):
317
+ return false
318
+
319
+ index += 1
320
+
321
+ return true
322
+
323
+
324
+ function segment_text(path: str, segment: Segment) -> str:
325
+ return path.slice(segment.start, segment.len)
326
+
327
+
328
+ function append_relative_segment(output: ref[string.String], segment: str) -> void:
329
+ if not output.is_empty():
330
+ output.append("/")
331
+
332
+ output.append(segment)
333
+
334
+
335
+ function min_ptr_uint(left: ptr_uint, right: ptr_uint) -> ptr_uint:
336
+ if left < right:
337
+ return left
338
+
339
+ return right
340
+
341
+
342
+ function trim_trailing_separators(path: str, minimum: ptr_uint) -> ptr_uint:
343
+ var stop = path.len
344
+ while stop > minimum and is_separator(path.byte_at(stop - 1)):
345
+ stop -= 1
346
+
347
+ return stop
348
+
349
+
350
+ function extension_dot(name: str) -> Option[ptr_uint]:
351
+ var index = name.len
352
+ while index > 0:
353
+ index -= 1
354
+ if name.byte_at(index) == 46:
355
+ if index == 0:
356
+ return Option[ptr_uint].none
357
+
358
+ return Option[ptr_uint].some(value= index)
359
+
360
+ return Option[ptr_uint].none
361
+
362
+
363
+ function is_separator(value: ubyte) -> bool:
364
+ return value == 47 or value == 92
365
+
366
+
367
+ function is_ascii_letter(value: ubyte) -> bool:
368
+ return (value >= 65 and value <= 90) or (value >= 97 and value <= 122)