aboutsummaryrefslogtreecommitdiff
path: root/core/os/dir_wasi.odin
blob: 05d74be313461191f987c06c70f3ba9ee971b3f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#+private
package os

import "core:slice"
import "base:intrinsics"
import "core:sys/wasm/wasi"

Read_Directory_Iterator_Impl :: struct {
	fullpath: [dynamic]byte,
	buf:      []byte,
	off:      int,
}

@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
	fimpl := (^File_Impl)(it.f.impl)

	buf := it.impl.buf[it.impl.off:]

	index = it.index
	it.index += 1

	for {
		if len(buf) < size_of(wasi.dirent_t) {
			return
		}

		entry := intrinsics.unaligned_load((^wasi.dirent_t)(raw_data(buf)))
		buf    = buf[size_of(wasi.dirent_t):]

		assert(len(buf) < int(entry.d_namlen))

		name := string(buf[:entry.d_namlen])
		buf = buf[entry.d_namlen:]
		it.impl.off += size_of(wasi.dirent_t) + int(entry.d_namlen)

		if name == "." || name == ".." {
			continue
		}

		n := len(fimpl.name)+1
		if alloc_err := non_zero_resize(&it.impl.fullpath, n+len(name)); alloc_err != nil {
			read_directory_iterator_set_error(it, name, alloc_err)
			ok = true
			return
		}
		copy(it.impl.fullpath[n:], name)

		stat, err := wasi.path_filestat_get(__fd(it.f), {}, name)
		if err != nil {
			// Can't stat, fill what we have from dirent.
			stat = {
				ino      = entry.d_ino,
				filetype = entry.d_type,
			}
			read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error(err))
		}

		fi = internal_stat(stat, string(it.impl.fullpath[:]))
		ok = true
		return
	}
}

_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
	// NOTE: Allow calling `init` to target a new directory with the same iterator.
	it.impl.off = 0

	if f == nil || f.impl == nil {
		read_directory_iterator_set_error(it, "", .Invalid_File)
		return
	}

	impl := (^File_Impl)(f.impl)

	buf: [dynamic]byte
	// NOTE: Allow calling `init` to target a new directory with the same iterator.
	if it.impl.buf != nil {
		buf = slice.into_dynamic(it.impl.buf)
	}
	buf.allocator = file_allocator()

	defer if it.err.err != nil { delete(buf) }

	for {
		if err := non_zero_resize(&buf, 512 if len(buf) == 0 else len(buf)*2); err != nil {
			read_directory_iterator_set_error(it, name(f), err)
			return
		}

		n, err := wasi.fd_readdir(__fd(f), buf[:], 0)
		if err != nil {
			read_directory_iterator_set_error(it, name(f), _get_platform_error(err))
			return
		}

		if n < len(buf) {
			non_zero_resize(&buf, n)
			break
		}

		assert(n == len(buf))
	}
	it.impl.buf = buf[:]

	// NOTE: Allow calling `init` to target a new directory with the same iterator.
	it.impl.fullpath.allocator = file_allocator()
	clear(&it.impl.fullpath)
	if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
		read_directory_iterator_set_error(it, name(f), err)
		return
	}

	append(&it.impl.fullpath, impl.name)
	append(&it.impl.fullpath, "/")

	return
}

_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
	delete(it.impl.buf, file_allocator())
	delete(it.impl.fullpath)
}