#pragma once #include #include #include #include #include #include #include #include #include namespace gp{ using open_opt_flags = int; enum class open_options : open_opt_flags { append = O_APPEND, create = O_CREAT, async = O_ASYNC, direct = O_DIRECT, data_sync = O_DSYNC, file_sync = O_SYNC, exclusive = O_EXCL, no_access_time = O_NOATIME, no_follow = O_NOFOLLOW, read = O_RDONLY, write = O_WRONLY, #ifdef O_TEMP temporary = O_TEMP, #endif #ifdef O_TRUC truncate = O_TRUC, #endif }; using open_opt_mode = int; enum class open_modes : open_opt_mode { user_read = S_IRUSR, user_write = S_IWUSR, user_exec = S_IXUSR, group_read = S_IRGRP, group_write = S_IWGRP, group_exec = S_IXGRP, other_read = S_IROTH, other_write = S_IWOTH, other_exec = S_IXOTH, set_uid = S_ISUID, set_gid = S_ISGID, sticky_bit = S_ISVTX, }; enum class net_socket_domain : int { ip4 = AF_INET, ip6 = AF_INET6 }; enum class net_socket_protocol : int { tcp = SOCK_STREAM, udp = SOCK_DGRAM }; using socket_opt_flags = int; enum class net_socket_opt_flags : socket_opt_flags { non_blocking = SOCK_NONBLOCK, close_on_exec = SOCK_CLOEXEC, }; using socket_opt_flags = int; enum class unix_socket_opt_flags : socket_opt_flags { non_blocking = SOCK_NONBLOCK, close_on_exec = SOCK_CLOEXEC, }; class shared_fd { int fd; std::atomic_int* guard; int last_error; shared_fd(int fd_v) : fd(fd_v) , guard(new std::atomic_int{1}) , last_error(0) {} public: shared_fd() : fd(-1) , guard(nullptr) , last_error(0) {} shared_fd(const shared_fd& oth) : fd(oth.fd) , guard(oth.guard) , last_error(0) { if(guard) guard->fetch_add(1, std::memory_order_acq_rel); } shared_fd(shared_fd&& oth) : last_error(0) { fd=oth.fd; guard=oth.guard; oth.fd=-1; oth.guard=nullptr; } void operator=(const shared_fd& oth) { this->~shared_fd(); fd = oth.fd; guard = oth.guard; if(guard) guard->fetch_add(1, std::memory_order_acq_rel); } void operator=(shared_fd&& oth) { std::swap(fd,oth.fd); std::swap(guard,oth.guard); } static shared_fd open(const std::string& filename, const open_opt_flags& flags, const open_opt_mode& mode = 0) { shared_fd ret{::open(filename.c_str(), flags, mode)}; return ret; } static shared_fd create(const std::string& filename, const open_opt_mode& mode) { shared_fd ret{::creat(filename.c_str(), mode)}; return ret; } static shared_fd net_socket(const net_socket_domain& dom, const net_socket_protocol& proto, net_socket_opt_flags& flags) { shared_fd ret{::socket((int)dom, (int)proto, (int)flags)}; return ret; } static shared_fd unix_socket(const net_socket_protocol& proto, const socket_opt_flags flags) { shared_fd ret{::socket((int)AF_UNIX, (int)proto, (int)flags)}; return ret; } static std::pair unix_socketpair(const net_socket_protocol& proto, const socket_opt_flags flags) { std::pair ret; int fds[2]; auto result = ::socketpair((int)AF_UNIX, (int)proto, (int)flags, fds); if(result != 0) return ret; ret.first = shared_fd(fds[0]); ret.second = shared_fd(fds[1]); return ret; } bool is_valid() const { return guard && (fd>=0); } bool has_failed() const { return last_error; } std::string_view read(const std::string_view buffer) { int sz = ::read(fd, (void*)(buffer.begin()), buffer.size()); if(sz<0) last_error = errno; else last_error = 0; sz = sz<0?0:sz; return std::string_view(buffer.begin(), sz); } std::string_view write(const std::string_view buffer) { int sz = ::write(fd, (void*)(buffer.begin()), buffer.size()); if(sz<0) last_error = errno; else last_error = 0; sz = sz<0?0:sz; return std::string_view(buffer.begin()+sz, buffer.size()-sz); } ~shared_fd() { if(guard) if(guard->fetch_sub(1, std::memory_order_acq_rel) == 1) { assert(guard->load() == 0); assert(fd >= 0); delete guard; guard = nullptr; if(fd >= 0) { ::close(fd); } } } }; }