v2 / vlib / os / fd.c.v
153 lines · 138 sloc · 3.34 KB · 3fa016dfdd7946e63f87da0c25b2e58a7b6dd258
Raw
1module os
2
3// low level operations with file descriptors/handles
4
5$if !windows {
6 #include <sys/select.h>
7 #include <sys/ioctl.h>
8}
9
10$if windows {
11 #include <winsock2.h>
12}
13
14#flag windows -lws2_32
15
16// fd_close closes the file descriptor. It returns 0 on success.
17pub fn fd_close(fd int) int {
18 if fd == -1 {
19 return 0
20 }
21 return C.close(fd)
22}
23
24// fd_write writes the given string to the file descriptor.
25// It blocks until all the data in the string is written.
26pub fn fd_write(fd int, s string) {
27 if fd == -1 {
28 return
29 }
30 mut sp := s.str
31 mut remaining := s.len
32 for remaining > 0 {
33 written := int(C.write(fd, sp, remaining))
34 if written < 0 {
35 return
36 }
37 remaining = remaining - written
38 sp = unsafe { voidptr(usize(sp) + usize(written)) }
39 }
40}
41
42// fd_slurp reads all the remaining data from the file descriptor.
43pub fn fd_slurp(fd int) []string {
44 mut res := []string{}
45 if fd == -1 {
46 return res
47 }
48 for {
49 s, b := fd_read(fd, 4096)
50 if b <= 0 {
51 break
52 }
53 res << s
54 }
55 return res
56}
57
58// fd_read reads data from the file descriptor. It returns the read data, and how many bytes were read.
59// On signal interruption (EINTR), the read is retried so callers do not see spurious empty results
60// when a signal (for example a Boehm GC stop-the-world signal in a multi-threaded program) interrupts
61// a blocking read.
62pub fn fd_read(fd int, maxbytes int) (string, int) {
63 if fd == -1 {
64 return '', 0
65 }
66 unsafe {
67 mut buf := malloc_noscan(maxbytes + 1)
68 mut nbytes := 0
69 for {
70 $if !windows {
71 C.errno = 0
72 }
73 nbytes = int(C.read(fd, buf, maxbytes))
74 if nbytes >= 0 {
75 break
76 }
77 $if !windows {
78 if C.errno == C.EINTR {
79 continue
80 }
81 }
82 break
83 }
84 if nbytes < 0 {
85 free(buf)
86 return '', nbytes
87 }
88 buf[nbytes] = 0
89 return tos(buf, nbytes), nbytes
90 }
91}
92
93@[typedef]
94pub struct C.fd_set {}
95
96pub struct C.timeval {
97pub:
98 tv_sec u64
99 tv_usec u64
100}
101
102fn C.select(ndfs i32, readfds &C.fd_set, writefds &C.fd_set, exceptfds &C.fd_set, timeout &C.timeval) i32
103
104$if !windows {
105 fn C.ioctl(fd i32, request u64, args ...voidptr) i32
106}
107
108$if windows {
109 fn C._get_osfhandle(fd int) voidptr
110 fn C.PeekNamedPipe(hNamedPipe voidptr, lpBuffer voidptr, nBufferSize i32, lpBytesRead voidptr, lpTotalBytesAvail voidptr, lpBytesLeftThisMessage voidptr) bool
111}
112
113// These are C macros, but from the V's point of view, can be treated as C functions:
114fn C.FD_ZERO(fdset &C.fd_set)
115fn C.FD_SET(fd i32, fdset &C.fd_set)
116fn C.FD_ISSET(fd i32, fdset &C.fd_set) i32
117
118// fd_is_pending returns true, when there is pending data, waiting to be read from file descriptor `fd`.
119// If the file descriptor is closed, or if reading from it, will block (there is no data), fd_is_pending returns false.
120pub fn fd_is_pending(fd int) bool {
121 if fd == -1 {
122 return false
123 }
124 mut bytes_avail := int(0)
125 $if windows {
126 handle := voidptr(C._get_osfhandle(fd))
127 if handle != voidptr(-1) {
128 if C.PeekNamedPipe(handle, unsafe { nil }, int(0), unsafe { nil },
129 voidptr(&bytes_avail), unsafe { nil })
130 {
131 return bytes_avail > 0
132 }
133 }
134 } $else {
135 if C.ioctl(fd, u64(C.FIONREAD), &bytes_avail) == 0 {
136 return bytes_avail > 0
137 }
138 }
139 read_set := C.fd_set{}
140 C.FD_ZERO(&read_set)
141 C.FD_SET(fd, &read_set)
142 mut ts := C.timeval{
143 tv_sec: 0
144 tv_usec: 0
145 }
146 res := C.select(fd + 1, &read_set, C.NULL, C.NULL, &ts)
147 if res > 0 {
148 if C.FD_ISSET(fd, &read_set) != 0 {
149 return true
150 }
151 }
152 return false
153}
154