From 519c850f175623680c3fca91b75b62f77d3cab5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= Date: Tue, 16 Jul 2024 11:49:38 +0800 Subject: [PATCH] llgo/c/hyper-related c lib --- _demo/netdb/netdb.go | 21 ++++++++++ _demo/select/select.go | 44 +++++++++++++++++++++ _demo/select/select2.go | 87 +++++++++++++++++++++++++++++++++++++++++ c/fddef/_wrap/fddef.c | 13 ++++++ c/fddef/fddef.go | 24 ++++++++++++ c/netdb/netdb.go | 29 ++++++++++++++ c/os/os.go | 4 ++ c/select/select.go | 22 +++++++++++ c/socket/socket.go | 11 ++++++ 9 files changed, 255 insertions(+) create mode 100644 _demo/netdb/netdb.go create mode 100644 _demo/select/select.go create mode 100644 _demo/select/select2.go create mode 100644 c/fddef/_wrap/fddef.c create mode 100644 c/fddef/fddef.go create mode 100644 c/netdb/netdb.go create mode 100644 c/select/select.go diff --git a/_demo/netdb/netdb.go b/_demo/netdb/netdb.go new file mode 100644 index 00000000..e0ebb3f7 --- /dev/null +++ b/_demo/netdb/netdb.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/netdb" + "github.com/goplus/llgo/c/socket" +) + +func main() { + var hints netdb.AddrInfo + hints.AiFamily = socket.AF_UNSPEC + hints.AiSockType = socket.SOCK_STREAM + + host := "httpbin.org" + port := "80" + + var result *netdb.AddrInfo + c.Printf(c.Str("%d\n"), netdb.Getaddrinfo(c.Str(host), c.Str(port), &hints, &result)) + + c.Printf(c.Str("%d\n"), netdb.Freeaddrinfo(result)) +} diff --git a/_demo/select/select.go b/_demo/select/select.go new file mode 100644 index 00000000..e7d64991 --- /dev/null +++ b/_demo/select/select.go @@ -0,0 +1,44 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/fddef" + "github.com/goplus/llgo/c/os" + _select "github.com/goplus/llgo/c/select" + "unsafe" +) + +func main() { + var readFds fddef.FdSet + + fddef.FdZero(&readFds) + + fddef.Fdset(0, &readFds) + + var tv _select.TimeVal + tv.TvSec = 5 + tv.TvUSec = 0 + + c.Printf(c.Str("Waiting for input on stdin...\n")) + ret := _select.Select(1, &readFds, nil, nil, &tv) + if ret == -1 { + c.Perror(c.Str("select error")) + c.Exit(1) + } else if ret == 0 { + c.Printf(c.Str("Timeout occurred! No data after 5 seconds.\n")) + } else { + if fddef.FdIsset(0, &readFds) != 0 { + var buffer [100]c.Char + n := os.Read(0, c.Pointer(&buffer[:][0]), unsafe.Sizeof(buffer)-1) + if n == -1 { + c.Perror(c.Str("read error")) + c.Exit(1) + } else if n == 0 { + c.Printf(c.Str("End of file\n")) + } else { + buffer[n] = c.Char(0) + c.Printf(c.Str("Read %ld bytes: %s\n"), n, &buffer[0]) + } + } + } +} diff --git a/_demo/select/select2.go b/_demo/select/select2.go new file mode 100644 index 00000000..417b5392 --- /dev/null +++ b/_demo/select/select2.go @@ -0,0 +1,87 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/fddef" + "github.com/goplus/llgo/c/os" + _select "github.com/goplus/llgo/c/select" + "github.com/goplus/llgo/c/socket" + "unsafe" +) + +const ( + SERVER_IP = "110.242.68.66" // Get the IP address by ping baidu.com + SERVER_PORT = 80 + BUFFER_SIZE = 4096 * 1024 +) + +func main() { + var server socket.SockaddrIn + + sendBuf := c.Str("GET / HTTP/1.1\r\nHost: baidu.com\r\n\r\n") + var recvBuf [BUFFER_SIZE]c.Char + var bytes_sent, bytes_received c.Int + + // create socket + sock := socket.Socket(socket.AF_INET, socket.SOCK_STREAM, 0) + if sock < 0 { + c.Perror(c.Str("Socket creation failed")) + return + } + + // set server addr + c.Memset(c.Pointer(&server), 0, unsafe.Sizeof(server)) + server.Family = socket.AF_INET + server.Port = socket.Htons(SERVER_PORT) + server.Addr.Addr = socket.InetAddr(c.Str(SERVER_IP)) + + // connect to server + if socket.Connect(sock, (*socket.SockAddr)(c.Pointer(&server)), c.Uint(unsafe.Sizeof(server))) < 0 { + c.Perror(c.Str("Connect failed")) + return + } + + var writefds, readfds fddef.FdSet + var timeout _select.TimeVal + + // Monitor socket writes + fddef.FdZero(&writefds) + fddef.Fdset(sock, &writefds) + timeout.TvSec = 10 + timeout.TvUSec = 0 + // Use select to monitor the readiness of writes + if _select.Select(sock+1, nil, &writefds, nil, &timeout) > 0 { + if fddef.FdIsset(sock, &writefds) != 0 { + bytes_sent = c.Int(socket.Send(sock, c.Pointer(sendBuf), c.Strlen(sendBuf), 0)) + if bytes_sent < 0 { + c.Perror(c.Str("send failed")) + return + } + } + } else { + c.Perror(c.Str("Select write error")) + return + } + + // Monitor socket reads + fddef.FdZero(&readfds) + fddef.Fdset(sock, &readfds) + + // Use select to monitor the readiness of the read operation + if _select.Select(sock+1, &readfds, nil, nil, &timeout) > 0 { + if fddef.FdIsset(sock, &writefds) != -1 { + bytes_received = c.Int(socket.Recv(sock, c.Pointer(&recvBuf[:][0]), BUFFER_SIZE-1, 0)) + if bytes_received < 0 { + c.Perror(c.Str("receive failed")) + return + } + recvBuf[bytes_received] = c.Char(0) + c.Printf(c.Str("Received:\n%s\n"), &recvBuf[0]) + } + } else { + c.Perror(c.Str("Select read error")) + return + } + + os.Close(sock) +} \ No newline at end of file diff --git a/c/fddef/_wrap/fddef.c b/c/fddef/_wrap/fddef.c new file mode 100644 index 00000000..d7f4999e --- /dev/null +++ b/c/fddef/_wrap/fddef.c @@ -0,0 +1,13 @@ +#include + +int fd_isset(int n, fd_set *fd) { + return FD_ISSET(n, fd); +} + +void fdSet(int n, fd_set *fd) { + FD_SET(n, fd); +} + +void fd_zero(fd_set *fd) { + FD_ZERO(fd); +} \ No newline at end of file diff --git a/c/fddef/fddef.go b/c/fddef/fddef.go new file mode 100644 index 00000000..791aadd5 --- /dev/null +++ b/c/fddef/fddef.go @@ -0,0 +1,24 @@ +package fddef + +import ( + "github.com/goplus/llgo/c" + _ "unsafe" +) + +const ( + LLGoFiles = "_wrap/fddef.c" + LLGoPackage = "link: c" +) + +type FdSet struct { + Unused [8]byte +} + +//go:linkname FdZero C.fd_zero +func FdZero(fdSet *FdSet) + +//go:linkname Fdset C.fdSet +func Fdset(fd c.Int, fdSet *FdSet) + +//go:linkname FdIsset C.fd_isset +func FdIsset(fd c.Int, fdSet *FdSet) c.Int diff --git a/c/netdb/netdb.go b/c/netdb/netdb.go new file mode 100644 index 00000000..a2db1b7b --- /dev/null +++ b/c/netdb/netdb.go @@ -0,0 +1,29 @@ +package netdb + +import ( + "github.com/goplus/llgo/c/socket" + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +const ( + LLGoPackage = "decl" +) + +type AddrInfo struct { + AiFlags c.Int + AiFamily c.Int + AiSockType c.Int + AiProtocol c.Int + AiAddrLen c.Uint + AiCanOnName *c.Char + AiAddr *socket.SockAddr + AiNext *AddrInfo +} + +//go:linkname Getaddrinfo C.getaddrinfo +func Getaddrinfo(host *c.Char, port *c.Char, addrInfo *AddrInfo, result **AddrInfo) c.Int + +//go:linkname Freeaddrinfo C.freeaddrinfo +func Freeaddrinfo(addrInfo *AddrInfo) c.Int diff --git a/c/os/os.go b/c/os/os.go index 583cf631..aa4f147f 100644 --- a/c/os/os.go +++ b/c/os/os.go @@ -58,6 +58,10 @@ const ( O_TRUNC = 0x00000400 ) +const ( + EAGAIN = 35 +) + type ( ModeT C.mode_t UidT C.uid_t diff --git a/c/select/select.go b/c/select/select.go new file mode 100644 index 00000000..eebcdda9 --- /dev/null +++ b/c/select/select.go @@ -0,0 +1,22 @@ +package _select + +import ( + "github.com/goplus/llgo/c/fddef" + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +const ( + LLGoPackage = "decl" +) + +// (TODO) merge to timeval +// +type TimeVal struct { + TvSec c.Long + TvUSec c.Int +} + +//go:linkname Select C.select +func Select(n c.Int, r *fddef.FdSet, w *fddef.FdSet, e *fddef.FdSet, timeout *TimeVal) c.Int diff --git a/c/socket/socket.go b/c/socket/socket.go index d019cf62..12c8affa 100644 --- a/c/socket/socket.go +++ b/c/socket/socket.go @@ -142,3 +142,14 @@ func SwapInt16(data uint16) uint16 { func Htons(x uint16) uint16 { return SwapInt16(x) } + +// (TODO) merge to netdb +// +//go:linkname InetAddr C.inet_addr +func InetAddr(s *c.Char) c.Uint + +//go:linkname Send C.send +func Send(c.Int, c.Pointer, uintptr, c.Int) c.Long + +//go:linkname Recv C.recv +func Recv(c.Int, c.Pointer, uintptr, c.Int) c.Long