From 498920b0061c235b20017fdd03b3e75db70f532d Mon Sep 17 00:00:00 2001 From: Craig Carnell <1188869+cscd98@users.noreply.github.com> Date: Tue, 11 Aug 2026 21:04:35 +0100 Subject: [PATCH 1/2] libretro: update libretro-common from RA --- .../libretro-common/compat/compat_fnmatch.c | 2 +- .../libretro-common/compat/compat_getopt.c | 84 +- .../libretro-common/compat/compat_ifaddrs.c | 162 +- .../compat/compat_posix_string.c | 29 +- .../libretro-common/compat/compat_snprintf.c | 32 +- .../compat/compat_strcasestr.c | 63 +- src/deps/libretro-common/compat/compat_strl.c | 53 +- src/deps/libretro-common/compat/fopen_utf8.c | 130 +- .../libretro-common/encodings/encoding_utf.c | 900 +- src/deps/libretro-common/file/file_path.c | 1632 ++-- .../include/compat/apple_compat.h | 1 + .../libretro-common/include/compat/fnmatch.h | 5 + .../libretro-common/include/compat/getopt.h | 14 +- .../libretro-common/include/compat/ifaddrs.h | 13 + .../include/compat/intrinsics.h | 24 +- .../libretro-common/include/compat/msvc.h | 4 +- .../include/compat/posix_string.h | 37 +- .../include/compat/strcasestr.h | 31 +- .../libretro-common/include/compat/strl.h | 35 +- .../libretro-common/include/compat/zconf.h | 483 - .../libretro-common/include/compat/zconf.h.in | 483 - .../libretro-common/include/compat/zlib.h | 1772 ---- .../libretro-common/include/compat/zutil.h | 253 - .../libretro-common/include/encodings/utf.h | 249 +- .../libretro-common/include/file/file_path.h | 494 +- src/deps/libretro-common/include/libretro.h | 8581 +++++++++++++---- src/deps/libretro-common/include/memmap.h | 123 +- .../libretro-common/include/retro_common.h | 12 +- .../include/retro_common_api.h | 33 +- .../include/retro_environment.h | 3 - .../libretro-common/include/retro_inline.h | 6 + .../include/retro_miscellaneous.h | 330 +- .../include/string/stdstring.h | 365 +- src/deps/libretro-common/include/time/rtime.h | 21 +- src/deps/libretro-common/include/vfs/vfs.h | 42 +- .../include/vfs/vfs_implementation.h | 27 + src/deps/libretro-common/string/stdstring.c | 683 +- src/deps/libretro-common/time/rtime.c | 336 +- 38 files changed, 10848 insertions(+), 6699 deletions(-) delete mode 100644 src/deps/libretro-common/include/compat/zconf.h delete mode 100644 src/deps/libretro-common/include/compat/zconf.h.in delete mode 100644 src/deps/libretro-common/include/compat/zlib.h delete mode 100644 src/deps/libretro-common/include/compat/zutil.h diff --git a/src/deps/libretro-common/compat/compat_fnmatch.c b/src/deps/libretro-common/compat/compat_fnmatch.c index 8e46ce8..f1066c3 100644 --- a/src/deps/libretro-common/compat/compat_fnmatch.c +++ b/src/deps/libretro-common/compat/compat_fnmatch.c @@ -24,7 +24,7 @@ #include -/* Implemnentation of fnmatch(3) so it can be +/* Implementation of fnmatch(3) so it can be * distributed to non *nix platforms. * * No flags are implemented ATM. diff --git a/src/deps/libretro-common/compat/compat_getopt.c b/src/deps/libretro-common/compat/compat_getopt.c index 742cb92..d7cccf6 100644 --- a/src/deps/libretro-common/compat/compat_getopt.c +++ b/src/deps/libretro-common/compat/compat_getopt.c @@ -35,8 +35,6 @@ #include #include -#include - char *optarg; int optind, opterr, optopt; @@ -110,54 +108,66 @@ static int parse_short(const char *optstring, char * const *argv) return optarg ? opt[0] : '?'; } + /* If we see additional characters, + * and they don't take arguments, this + * means we have multiple flags in one. */ if (embedded_arg) - { - /* If we see additional characters, - * and they don't take arguments, this - * means we have multiple flags in one. */ memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1); - return opt[0]; - } + else + optind++; - optind++; return opt[0]; } static int parse_long(const struct option *longopts, char * const *argv) { - size_t indice; - const struct option *opt = NULL; - for (indice = 0; longopts[indice].name; indice++) + const char *arg = &argv[0][2]; + const char *eq = strchr(arg, '='); + size_t len = eq ? (size_t)(eq - arg) : strlen(arg); + + for (; longopts->name; longopts++) { - if (!strcmp(longopts[indice].name, &argv[0][2])) + const char *n = longopts->name; + const char *a = arg; + size_t rem = len; + + while (rem && *n == *a) { - opt = &longopts[indice]; - break; + n++; + a++; + rem--; } - } - if (!opt) - return '?'; + if (rem || *n) + continue; - /* getopt_long has an "optional" arg, but we don't bother with that. */ - if (opt->has_arg && !argv[1]) - return '?'; - - if (opt->has_arg) - { - optarg = argv[1]; - optind += 2; - } - else - optind++; + if (longopts->has_arg) + { + if (eq) + { + optarg = (char *)(eq + 1); + optind++; + } + else if (argv[1]) + { + optarg = argv[1]; + optind += 2; + } + else + return '?'; + } + else + optind++; - if (opt->flag) - { - *opt->flag = opt->val; - return 0; + if (longopts->flag) + { + *longopts->flag = longopts->val; + return 0; + } + return longopts->val; } - return opt->val; + return '?'; } static void shuffle_block(char **begin, char **last, char **end) @@ -165,8 +175,6 @@ static void shuffle_block(char **begin, char **last, char **end) ptrdiff_t len = last - begin; const char **tmp = (const char**)calloc(len, sizeof(const char*)); - retro_assert(tmp); - memcpy((void*)tmp, begin, len * sizeof(const char*)); memmove(begin, last, (end - last) * sizeof(const char*)); memcpy(end - len, tmp, len * sizeof(const char*)); @@ -179,8 +187,6 @@ int getopt_long(int argc, char *argv[], { int short_index, long_index; - (void)longindex; - if (optind == 0) optind = 1; @@ -208,8 +214,6 @@ int getopt_long(int argc, char *argv[], long_index = 0; } - retro_assert(short_index == 0 || long_index == 0); - if (short_index == 0) return parse_short(optstring, &argv[optind]); if (long_index == 0) diff --git a/src/deps/libretro-common/compat/compat_ifaddrs.c b/src/deps/libretro-common/compat/compat_ifaddrs.c index 4a270b6..fc40b12 100644 --- a/src/deps/libretro-common/compat/compat_ifaddrs.c +++ b/src/deps/libretro-common/compat/compat_ifaddrs.c @@ -22,6 +22,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include @@ -74,11 +75,11 @@ static int netlink_send(int p_socket, int p_request) memset(&l_data, 0, sizeof(l_data)); - l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); - l_data.m_hdr.nlmsg_type = p_request; - l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; - l_data.m_hdr.nlmsg_pid = 0; - l_data.m_hdr.nlmsg_seq = p_socket; + l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + l_data.m_hdr.nlmsg_type = p_request; + l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + l_data.m_hdr.nlmsg_pid = 0; + l_data.m_hdr.nlmsg_seq = p_socket; l_data.m_msg.rtgen_family = AF_UNSPEC; memset(&l_addr, 0, sizeof(l_addr)); @@ -89,8 +90,8 @@ static int netlink_send(int p_socket, int p_request) static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) { struct msghdr l_msg; - struct iovec l_iov = { p_buffer, p_len }; struct sockaddr_nl l_addr; + struct iovec l_iov = { p_buffer, p_len }; for (;;) { @@ -117,9 +118,10 @@ static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) return -1; return l_result; } + return 0; } -static struct nlmsghdr *getNetlinkResponse(int p_socket, +static struct nlmsghdr *ifaddrs_get_netlink_response(int p_socket, int *p_size, int *p_done) { size_t l_size = 4096; @@ -152,8 +154,8 @@ static struct nlmsghdr *getNetlinkResponse(int p_socket, NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read)) { - if ( (pid_t)l_hdr->nlmsg_pid != l_pid || - (int)l_hdr->nlmsg_seq != p_socket) + if ( (pid_t)l_hdr->nlmsg_pid != l_pid + || (int)l_hdr->nlmsg_seq != p_socket) continue; if (l_hdr->nlmsg_type == NLMSG_DONE) @@ -175,7 +177,8 @@ static struct nlmsghdr *getNetlinkResponse(int p_socket, } } -static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size) +static NetlinkList *ifaddr_new_list_item(struct nlmsghdr *p_data, + unsigned int p_size) { NetlinkList *l_item = (NetlinkList*)malloc(sizeof(NetlinkList)); if (!l_item) @@ -187,7 +190,7 @@ static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size) return l_item; } -static void freeResultList(NetlinkList *p_list) +static void ifaddr_free_result_list(NetlinkList *p_list) { NetlinkList *l_cur; @@ -200,7 +203,7 @@ static void freeResultList(NetlinkList *p_list) } } -static NetlinkList *getResultList(int p_socket, int p_request) +static NetlinkList *ifaddrs_get_resultlist(int p_socket, int p_request) { int l_size; NetlinkList *l_list = NULL; @@ -213,11 +216,12 @@ static NetlinkList *getResultList(int p_socket, int p_request) while (!l_done) { NetlinkList *l_item = NULL; - struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done); + struct nlmsghdr *l_hdr = ifaddrs_get_netlink_response( + p_socket, &l_size, &l_done); if (!l_hdr) goto error; - l_item = newListItem(l_hdr, l_size); + l_item = ifaddr_new_list_item(l_hdr, l_size); if (!l_item) goto error; @@ -231,16 +235,13 @@ static NetlinkList *getResultList(int p_socket, int p_request) return l_list; error: - freeResultList(l_list); + ifaddr_free_result_list(l_list); return NULL; } -static size_t maxSize(size_t a, size_t b) -{ - return (a > b ? a : b); -} +static size_t ifaddrs_max_size(size_t a, size_t b) { return (a > b ? a : b); } -static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) +static size_t ifaddrs_calc_addr_len(sa_family_t p_family, int p_dataSize) { switch(p_family) { @@ -249,15 +250,16 @@ static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) case AF_INET6: return sizeof(struct sockaddr_in6); case AF_PACKET: - return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); + return ifaddrs_max_size(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); default: break; } - return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); + return ifaddrs_max_size(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); } -static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size) +static void ifaddrs_make_sock_addr(sa_family_t p_family, + struct sockaddr *p_dest, void *p_data, size_t p_size) { switch(p_family) { @@ -278,7 +280,8 @@ static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_ p_dest->sa_family = p_family; } -static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry) +static void ifaddrs_add_to_end(struct ifaddrs **p_resultList, + struct ifaddrs *p_entry) { if (!*p_resultList) *p_resultList = p_entry; @@ -291,8 +294,10 @@ static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry) } } -static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) +static int ifaddrs_interpret_link(struct nlmsghdr *p_hdr, + struct ifaddrs **p_resultList) { + char *l_index, *l_addr, *l_name, *l_data; struct ifaddrs *l_entry = NULL; struct rtattr *l_rta = NULL; struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr); @@ -309,7 +314,7 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) { case IFLA_ADDRESS: case IFLA_BROADCAST: - l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize)); + l_addrSize += NLMSG_ALIGN(ifaddrs_calc_addr_len(AF_PACKET, l_rtaDataSize)); break; case IFLA_IFNAME: l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); @@ -329,10 +334,10 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) memset(l_entry, 0, sizeof(struct ifaddrs)); l_entry->ifa_name = ""; - char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs); - char *l_name = l_index + sizeof(int); - char *l_addr = l_name + l_nameSize; - char *l_data = l_addr + l_addrSize; + l_index = ((char *)l_entry) + sizeof(struct ifaddrs); + l_name = l_index + sizeof(int); + l_addr = l_name + l_nameSize; + l_data = l_addr + l_addrSize; /* save the interface index so we can look * it up when handling the addresses. */ @@ -353,19 +358,19 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) case IFLA_ADDRESS: case IFLA_BROADCAST: { - size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize); - makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); + size_t l_addr_len = ifaddrs_calc_addr_len(AF_PACKET, l_rtaDataSize); + ifaddrs_make_sock_addr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index; ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type; if (l_rta->rta_type == IFLA_ADDRESS) l_entry->ifa_addr = (struct sockaddr *)l_addr; else l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; - l_addr += NLMSG_ALIGN(l_addrLen); - break; + l_addr += NLMSG_ALIGN(l_addr_len); } + break; case IFLA_IFNAME: - strncpy(l_name, l_rtaData, l_rtaDataSize); + memcpy(l_name, l_rtaData, l_rtaDataSize); l_name[l_rtaDataSize] = '\0'; l_entry->ifa_name = l_name; break; @@ -378,11 +383,11 @@ static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) } } - addToEnd(p_resultList, l_entry); + ifaddrs_add_to_end(p_resultList, l_entry); return 0; } -static struct ifaddrs *findInterface(int p_index, +static struct ifaddrs *ifaddrs_find_interface(int p_index, struct ifaddrs **p_links, int p_numLinks) { int l_num = 0; @@ -403,16 +408,18 @@ static struct ifaddrs *findInterface(int p_index, return NULL; } -static int interpretAddr(struct nlmsghdr *p_hdr, +static int ifaddrs_interpret_addr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks) { + char *l_name, *l_addr; struct rtattr *l_rta; + struct ifaddrs *l_entry; size_t l_rtaSize; size_t l_nameSize = 0; size_t l_addrSize = 0; int l_addedNetmask = 0; struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr); - struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks); + struct ifaddrs *l_interface = ifaddrs_find_interface(l_info->ifa_index, p_resultList, p_numLinks); if (l_info->ifa_family == AF_PACKET) return 0; @@ -431,11 +438,11 @@ static int interpretAddr(struct nlmsghdr *p_hdr, if ((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask) { /* make room for netmask */ - l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + l_addrSize += NLMSG_ALIGN(ifaddrs_calc_addr_len(l_info->ifa_family, l_rtaDataSize)); l_addedNetmask = 1; } case IFA_BROADCAST: - l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + l_addrSize += NLMSG_ALIGN(ifaddrs_calc_addr_len(l_info->ifa_family, l_rtaDataSize)); break; case IFA_LABEL: l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); @@ -445,15 +452,15 @@ static int interpretAddr(struct nlmsghdr *p_hdr, } } - struct ifaddrs *l_entry = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); + l_entry = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); if (!l_entry) return -1; memset(l_entry, 0, sizeof(struct ifaddrs)); l_entry->ifa_name = (l_interface ? l_interface->ifa_name : ""); - char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs); - char *l_addr = l_name + l_nameSize; + l_name = ((char *)l_entry) + sizeof(struct ifaddrs); + l_addr = l_name + l_nameSize; l_entry->ifa_flags = l_info->ifa_flags; if (l_interface) @@ -463,7 +470,7 @@ static int interpretAddr(struct nlmsghdr *p_hdr, for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { - void *l_rtaData = RTA_DATA(l_rta); + void *l_rtaData = RTA_DATA(l_rta); size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); switch(l_rta->rta_type) { @@ -471,22 +478,24 @@ static int interpretAddr(struct nlmsghdr *p_hdr, case IFA_BROADCAST: case IFA_LOCAL: { - size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize); - makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); + size_t l_addrLen = ifaddrs_calc_addr_len(l_info->ifa_family, l_rtaDataSize); + ifaddrs_make_sock_addr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); if (l_info->ifa_family == AF_INET6) { - if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData)) + if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) + || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData)) ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index; } if (l_rta->rta_type == IFA_ADDRESS) { - /* apparently in a point-to-point network IFA_ADDRESS - * contains the dest address and IFA_LOCAL contains the local address */ + /* Apparently in a point-to-point network IFA_ADDRESS + * contains the dest address and IFA_LOCAL contains + * the local address */ if (l_entry->ifa_addr) l_entry->ifa_dstaddr = (struct sockaddr *)l_addr; else - l_entry->ifa_addr = (struct sockaddr *)l_addr; + l_entry->ifa_addr = (struct sockaddr *)l_addr; } else if (l_rta->rta_type == IFA_LOCAL) { @@ -500,8 +509,7 @@ static int interpretAddr(struct nlmsghdr *p_hdr, break; } case IFA_LABEL: - strncpy(l_name, l_rtaData, l_rtaDataSize); - l_name[l_rtaDataSize] = '\0'; + strlcpy(l_name, l_rtaData, l_rtaDataSize + 1); l_entry->ifa_name = l_name; break; default: @@ -509,9 +517,9 @@ static int interpretAddr(struct nlmsghdr *p_hdr, } } - if (l_entry->ifa_addr && - ( l_entry->ifa_addr->sa_family == AF_INET - || l_entry->ifa_addr->sa_family == AF_INET6)) + if ( l_entry->ifa_addr + && ( l_entry->ifa_addr->sa_family == AF_INET + || l_entry->ifa_addr->sa_family == AF_INET6)) { unsigned i; char l_mask[16]; @@ -522,22 +530,22 @@ static int interpretAddr(struct nlmsghdr *p_hdr, l_mask[0] = '\0'; - for (i=0; i<(l_prefix/8); ++i) + for (i = 0; i < (l_prefix/8); ++i) l_mask[i] = 0xff; if (l_prefix % 8) l_mask[i] = 0xff << (8 - (l_prefix % 8)); - makeSockaddr(l_entry->ifa_addr->sa_family, + ifaddrs_make_sock_addr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8); l_entry->ifa_netmask = (struct sockaddr *)l_addr; } - addToEnd(p_resultList, l_entry); + ifaddrs_add_to_end(p_resultList, l_entry); return 0; } -static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, - struct ifaddrs **p_resultList) +static int ifaddrs_interpret_links(int p_socket, + NetlinkList *p_netlinkList, struct ifaddrs **p_resultList) { int l_numLinks = 0; pid_t l_pid = getpid(); @@ -548,10 +556,10 @@ static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, unsigned int l_nlsize = p_netlinkList->m_size; for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); - l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) + l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) { - if ( (pid_t)l_hdr->nlmsg_pid != l_pid || - (int)l_hdr->nlmsg_seq != p_socket) + if ( (pid_t)l_hdr->nlmsg_pid != l_pid + || (int)l_hdr->nlmsg_seq != p_socket) continue; if (l_hdr->nlmsg_type == NLMSG_DONE) @@ -559,7 +567,7 @@ static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, if (l_hdr->nlmsg_type == RTM_NEWLINK) { - if (interpretLink(l_hdr, p_resultList) == -1) + if (ifaddrs_interpret_link(l_hdr, p_resultList) == -1) return -1; ++l_numLinks; } @@ -568,7 +576,8 @@ static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, return l_numLinks; } -static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, +static int ifaddrs_interpret_addrs(int p_socket, + NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks) { pid_t l_pid = getpid(); @@ -589,7 +598,7 @@ static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, if (l_hdr->nlmsg_type == RTM_NEWADDR) { - if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1) + if (ifaddrs_interpret_addr(l_hdr, p_resultList, p_numLinks) == -1) return -1; } } @@ -599,44 +608,43 @@ static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, int getifaddrs(struct ifaddrs **ifap) { + int l_numLinks; NetlinkList *l_linkResults; NetlinkList *l_addrResults; - int l_numLinks; int l_socket = 0; int l_result = 0; if (!ifap) return -1; *ifap = NULL; - l_socket = netlink_socket(); if (l_socket < 0) return -1; - l_linkResults = getResultList(l_socket, RTM_GETLINK); + l_linkResults = ifaddrs_get_resultlist(l_socket, RTM_GETLINK); if (!l_linkResults) { close(l_socket); return -1; } - l_addrResults = getResultList(l_socket, RTM_GETADDR); + l_addrResults = ifaddrs_get_resultlist(l_socket, RTM_GETADDR); if (!l_addrResults) { close(l_socket); - freeResultList(l_linkResults); + ifaddr_free_result_list(l_linkResults); return -1; } - l_numLinks = interpretLinks(l_socket, l_linkResults, ifap); + l_numLinks = ifaddrs_interpret_links(l_socket, l_linkResults, ifap); - if ( l_numLinks == -1 || - interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) + if ( l_numLinks == -1 + || ifaddrs_interpret_addrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) l_result = -1; - freeResultList(l_linkResults); - freeResultList(l_addrResults); + ifaddr_free_result_list(l_linkResults); + ifaddr_free_result_list(l_addrResults); close(l_socket); return l_result; } diff --git a/src/deps/libretro-common/compat/compat_posix_string.c b/src/deps/libretro-common/compat/compat_posix_string.c index 6a2f07e..b0ea26a 100644 --- a/src/deps/libretro-common/compat/compat_posix_string.c +++ b/src/deps/libretro-common/compat/compat_posix_string.c @@ -37,12 +37,27 @@ #include +/* ASCII case folding, not tolower. + * + * tolower takes an int that must be representable as unsigned char or + * EOF. A plain char sign-extends any byte above 0x7F into a negative + * value, so passing one straight in is undefined - and these are + * compared on file paths and user text, where such bytes are ordinary. + * glibc happens to tolerate it because its table is offset for exactly + * this mistake; nothing guarantees the next libc will. + * + * Folding only A-Z is also what the callers mean. Anything wider makes + * a comparison depend on the user's locale, which for extensions, + * driver names and identifiers is not wanted. */ +#define COMPAT_LOWER(c) \ + (((c) >= 'A' && (c) <= 'Z') ? ((c) + ('a' - 'A')) : (c)) + int retro_strcasecmp__(const char *a, const char *b) { while (*a && *b) { - int a_ = tolower(*a); - int b_ = tolower(*b); + int a_ = COMPAT_LOWER((unsigned char)*a); + int b_ = COMPAT_LOWER((unsigned char)*b); if (a_ != b_) return a_ - b_; @@ -51,17 +66,16 @@ int retro_strcasecmp__(const char *a, const char *b) b++; } - return tolower(*a) - tolower(*b); + return COMPAT_LOWER((unsigned char)*a) - COMPAT_LOWER((unsigned char)*b); } char *retro_strdup__(const char *orig) { - size_t len = strlen(orig) + 1; - char *ret = (char*)malloc(len); + size_t _len = strlen(orig) + 1; + char *ret = (char*)malloc(_len); if (!ret) return NULL; - - strlcpy(ret, orig, len); + memcpy(ret, orig, _len); return ret; } @@ -100,5 +114,4 @@ char *retro_strtok_r__(char *str, const char *delim, char **saveptr) return first; } - #endif diff --git a/src/deps/libretro-common/compat/compat_snprintf.c b/src/deps/libretro-common/compat/compat_snprintf.c index d7320cc..1d27041 100644 --- a/src/deps/libretro-common/compat/compat_snprintf.c +++ b/src/deps/libretro-common/compat/compat_snprintf.c @@ -35,12 +35,12 @@ static int c89_vscprintf_retro__(const char *fmt, va_list pargs) { - int retval; + int _len; va_list argcopy; va_copy(argcopy, pargs); - retval = vsnprintf(NULL, 0, fmt, argcopy); + _len = vsnprintf(NULL, 0, fmt, argcopy); va_end(argcopy); - return retval; + return _len; } #endif @@ -48,36 +48,30 @@ static int c89_vscprintf_retro__(const char *fmt, va_list pargs) int c99_vsnprintf_retro__(char *s, size_t len, const char *fmt, va_list ap) { - int count = -1; - + int _len = -1; if (len != 0) { #if (_MSC_VER <= 1310) - count = _vsnprintf(s, len - 1, fmt, ap); + _len = _vsnprintf(s, len - 1, fmt, ap); #else - count = _vsnprintf_s(s, len, len - 1, fmt, ap); + _len = _vsnprintf_s(s, len, len - 1, fmt, ap); #endif } - - if (count == -1) - count = _vscprintf(fmt, ap); - + if (_len == -1) + _len = _vscprintf(fmt, ap); /* there was no room for a NULL, so truncate the last character */ - if (count == len && len) + if (_len == len && len) s[len - 1] = '\0'; - - return count; + return _len; } int c99_snprintf_retro__(char *s, size_t len, const char *fmt, ...) { - int count; + int _len; va_list ap; - va_start(ap, fmt); - count = c99_vsnprintf_retro__(s, len, fmt, ap); + _len = c99_vsnprintf_retro__(s, len, fmt, ap); va_end(ap); - - return count; + return _len; } #endif diff --git a/src/deps/libretro-common/compat/compat_strcasestr.c b/src/deps/libretro-common/compat/compat_strcasestr.c index 4129dab..ebc9fa9 100644 --- a/src/deps/libretro-common/compat/compat_strcasestr.c +++ b/src/deps/libretro-common/compat/compat_strcasestr.c @@ -24,6 +24,19 @@ #include +/* ASCII case folding, done here rather than through tolower. + * + * tolower takes an int whose value must be representable as unsigned + * char or EOF; passing a plain char sign-extends anything above 0x7F + * into a negative value, which is undefined. This is called on + * arbitrary bytes - file paths, HTTP responses, config text - so that + * was reachable. Folding only A-Z is also what every caller here + * means: extensions, window manager names, config keys and header + * fields are all ASCII, and locale-dependent folding of anything else + * would make the result depend on the user's locale. */ +#define STRCASESTR_LOWER(c) \ + (((c) >= 'A' && (c) <= 'Z') ? ((c) + ('a' - 'A')) : (c)) + /* Pretty much strncasecmp. */ static int casencmp(const char *a, const char *b, size_t n) { @@ -31,8 +44,8 @@ static int casencmp(const char *a, const char *b, size_t n) for (i = 0; i < n; i++) { - int a_lower = tolower(a[i]); - int b_lower = tolower(b[i]); + int a_lower = STRCASESTR_LOWER((unsigned char)a[i]); + int b_lower = STRCASESTR_LOWER((unsigned char)b[i]); if (a_lower != b_lower) return a_lower - b_lower; } @@ -40,19 +53,47 @@ static int casencmp(const char *a, const char *b, size_t n) return 0; } -char *strcasestr_retro__(const char *haystack, const char *needle) +/* Let memchr find the candidate positions. + * + * Comparing at every offset means folding every byte of the haystack, + * which measured some sixty times slower than strstr on the same + * inputs - enough to matter where this is called per file over a + * directory. Only the first character of the needle decides whether + * an offset is worth examining, and memchr finds those far faster than + * a byte loop can, so it does the skipping and the fold only runs + * where a match is actually possible. A needle whose first character + * has no case of its own - a digit, a punctuation mark - costs one + * memchr rather than two. */ +char *compat_strcasestr(const char *haystack, const char *needle) { - size_t i, search_off; - size_t hay_len = strlen(haystack); - size_t needle_len = strlen(needle); + size_t nlen = strlen(needle); + size_t hlen = strlen(haystack); + const char *end = haystack + hlen; + int lo; + int up; - if (needle_len > hay_len) + if (!nlen) + return (char*)haystack; + if (nlen > hlen) return NULL; - search_off = hay_len - needle_len; - for (i = 0; i <= search_off; i++) - if (!casencmp(haystack + i, needle, needle_len)) - return (char*)haystack + i; + lo = STRCASESTR_LOWER((unsigned char)needle[0]); + up = (lo >= 'a' && lo <= 'z') ? (lo - ('a' - 'A')) : lo; + + while ((size_t)(end - haystack) >= nlen) + { + size_t left = (size_t)(end - haystack); + const char *a = (const char*)memchr(haystack, lo, left); + const char *b = (up != lo) + ? (const char*)memchr(haystack, up, left) : NULL; + const char *p = !a ? b : (!b ? a : (a < b ? a : b)); + + if (!p || (size_t)(end - p) < nlen) + return NULL; + if (!casencmp(p + 1, needle + 1, nlen - 1)) + return (char*)p; + haystack = p + 1; + } return NULL; } diff --git a/src/deps/libretro-common/compat/compat_strl.c b/src/deps/libretro-common/compat/compat_strl.c index 3172310..f3da412 100644 --- a/src/deps/libretro-common/compat/compat_strl.c +++ b/src/deps/libretro-common/compat/compat_strl.c @@ -20,50 +20,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include -#include - #include /* Implementation of strlcpy()/strlcat() based on OpenBSD. */ -#ifndef __MACH__ - -size_t strlcpy(char *dest, const char *source, size_t size) +#if !(defined(__MACH__) && defined(__APPLE__)) +size_t strlcpy(char *s, const char *in, size_t len) { - size_t src_size = 0; - size_t n = size; - - if (n) - while (--n && (*dest++ = *source++)) src_size++; - - if (!n) + size_t src_len = strlen(in); + if (len) { - if (size) *dest = '\0'; - while (*source++) src_size++; + size_t cpy_len = src_len < len - 1 ? src_len : len - 1; + memcpy(s, in, cpy_len); + s[cpy_len] = '\0'; } - - return src_size; + return src_len; } -size_t strlcat(char *dest, const char *source, size_t size) +/* The destination scan is bounded by 'len': 's' is not required to + * contain a NUL within the first 'len' bytes. When it does not, no + * bytes are written and the return value is len + strlen(source), + * matching OpenBSD, Darwin and glibc. */ +size_t strlcat(char *s, const char *source, size_t len) { - size_t len = strlen(dest); - - dest += len; - - if (len > size) - size = 0; - else - size -= len; - - return len + strlcpy(dest, source, size); + const char *nul = (const char*)memchr(s, 0, len); + size_t dst_len = nul ? (size_t)(nul - s) : len; + if (dst_len == len) + return len + strlen(source); + return dst_len + strlcpy(s + dst_len, source, len - dst_len); } #endif - -char *strldup(const char *s, size_t n) -{ - char *dst = (char*)malloc(sizeof(char) * (n + 1)); - strlcpy(dst, s, n); - return dst; -} diff --git a/src/deps/libretro-common/compat/fopen_utf8.c b/src/deps/libretro-common/compat/fopen_utf8.c index 85abb59..e07edfe 100644 --- a/src/deps/libretro-common/compat/fopen_utf8.c +++ b/src/deps/libretro-common/compat/fopen_utf8.c @@ -23,41 +23,127 @@ #include #include #include +#include #include - -#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) -#ifndef LEGACY_WIN32 -#define LEGACY_WIN32 -#endif +#include +#if defined(__WINRT__) +#include +#include +#include +#include +#include #endif #ifdef _WIN32 #undef fopen -void *fopen_utf8(const char * filename, const char * mode) +#if defined(LEGACY_WIN32) || defined(LEGACY_WIN32_RUNTIME) +static void *fopen_utf8_ansi(const char * filename, const char * mode) { -#if defined(LEGACY_WIN32) - FILE *ret = NULL; char * filename_local = utf8_to_local_string_alloc(filename); - - if (!filename_local) - return NULL; - ret = fopen(filename_local, mode); if (filename_local) + { + FILE *ret = fopen(filename_local, mode); free(filename_local); - return ret; -#else - wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); - wchar_t * mode_w = utf8_to_utf16_string_alloc(mode); - FILE* ret = NULL; + return ret; + } + return NULL; +} +#endif - if (filename_w && mode_w) - ret = _wfopen(filename_w, mode_w); +#if !defined(LEGACY_WIN32) || defined(LEGACY_WIN32_RUNTIME) +static void *fopen_utf8_wide(const char * filename, const char * mode) +{ + wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); if (filename_w) + { + FILE *ret = NULL; +#if defined(__WINRT__) + HANDLE file_handle = INVALID_HANDLE_VALUE; + DWORD desired_access = 0; + DWORD creation_disposition = OPEN_EXISTING; + int open_flags = O_BINARY; + int fd = -1; + bool append = mode && strchr(mode, 'a'); + bool write = mode && strchr(mode, 'w'); + bool plus = mode && strchr(mode, '+'); + wchar_t *path = filename_w; + + while (*path) + { + if (*path == L'/') + *path = L'\\'; + path++; + } + + if (mode && strchr(mode, 'r')) + desired_access |= GENERIC_READ; + if (write || append || plus) + desired_access |= GENERIC_WRITE; + if (plus) + desired_access |= GENERIC_READ; + + if (append) + creation_disposition = OPEN_ALWAYS; + else if (write) + creation_disposition = CREATE_ALWAYS; + + if (plus) + open_flags |= O_RDWR; + else if (append || write) + open_flags |= O_WRONLY; + else + open_flags |= O_RDONLY; + + if (append) + open_flags |= O_APPEND; + if (write || append) + open_flags |= O_CREAT; + if (write) + open_flags |= O_TRUNC; + + file_handle = CreateFile2FromAppW(filename_w, desired_access, + FILE_SHARE_READ | FILE_SHARE_WRITE, + creation_disposition, NULL); + if (file_handle != INVALID_HANDLE_VALUE) + { + fd = _open_osfhandle((intptr_t)file_handle, open_flags); + if (fd != -1) + ret = _fdopen(fd, mode); + + if (!ret) + { + if (fd != -1) + _close(fd); + else + CloseHandle(file_handle); + } + } +#else + wchar_t *mode_w = utf8_to_utf16_string_alloc(mode); + if (mode_w) + { + ret = _wfopen(filename_w, mode_w); + free(mode_w); + } +#endif free(filename_w); - if (mode_w) - free(mode_w); - return ret; + return ret; + } + return NULL; +} +#endif + +void *fopen_utf8(const char * filename, const char * mode) +{ +#if defined(LEGACY_WIN32_RUNTIME) + if (win32_needs_local_encoding()) + return fopen_utf8_ansi(filename, mode); + return fopen_utf8_wide(filename, mode); +#elif defined(LEGACY_WIN32) + return fopen_utf8_ansi(filename, mode); +#else + return fopen_utf8_wide(filename, mode); #endif } #endif diff --git a/src/deps/libretro-common/encodings/encoding_utf.c b/src/deps/libretro-common/encodings/encoding_utf.c index 2760824..1d8aca2 100644 --- a/src/deps/libretro-common/encodings/encoding_utf.c +++ b/src/deps/libretro-common/encodings/encoding_utf.c @@ -39,86 +39,251 @@ #define UTF8_WALKBYTE(string) (*((*(string))++)) -static unsigned leading_ones(uint8_t c) -{ - unsigned ones = 0; - while (c & 0x80) - { - ones++; - c <<= 1; - } - - return ones; -} - -/* Simple implementation. Assumes the sequence is - * properly synchronized and terminated. */ - +/* Number of leading 1-bits of a byte, which for a lead byte is the + * length in bytes of the sequence it introduces: 0 for ASCII, 1 for a + * continuation byte, 2..4 for the valid leads, 5..7 for invalid ones. + * Replaces a leading_ones() bit-counting loop. + * + * Used by the decoders, which need the exact value. The scanners that + * only need a length deliberately do not use it: indexing this table + * puts a second dependent load in their pointer advance, which costs + * more than the branch it removes. */ +static const uint8_t utf8_lut[256] = { + /* 0x00..0x7F: 0 leading ones (ASCII) */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + /* 0x80..0xBF: 1 leading one (continuation byte) */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + /* 0xC0..0xDF: 2 leading ones (2-byte sequence) */ + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + /* 0xE0..0xEF: 3 leading ones (3-byte sequence) */ + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + /* 0xF0..0xF7: 4 leading ones (4-byte sequence) */ + 4,4,4,4,4,4,4,4, + /* 0xF8..0xFB: 5 leading ones */ + 5,5,5,5, + /* 0xFC..0xFD: 6 leading ones */ + 6,6, + /* 0xFE..0xFF: 7+ leading ones (invalid) */ + 7,7 +}; + +/** + * utf8_conv_utf32: + * + * Simple implementation. Assumes the sequence is + * properly synchronized and terminated. + * + * Optimized: replaced leading_ones() loop with LUT, + * fast-path for ASCII, and unrolled continuation-byte reads. + **/ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars, const char *in, size_t in_size) { - unsigned i; size_t ret = 0; while (in_size && out_chars) { - unsigned extra, shift; uint32_t c; - uint8_t first = *in++; - unsigned ones = leading_ones(first); + uint8_t first; + unsigned ones; + + /* Fast path: batch ASCII characters. + * + * Same word test as the utf8len counting loop: a byte is ASCII + * iff bit 7 is clear, so one masked 64-bit compare clears eight + * bytes at a time. The loads go through memcpy, so alignment + * does not matter and no strict-aliasing rule is broken. Widening + * to uint32_t is done per byte, which is endian neutral. + * + * The word loop is only entered when the next byte is ASCII, so + * multibyte-dense text does not pay a wide load and test on + * every character. */ + if ((uint8_t)*in < 0x80) + { + while (in_size >= 8 && out_chars >= 8) + { + uint64_t w; + memcpy(&w, in, sizeof(w)); + if (w & 0x8080808080808080ULL) + break; + out[0] = (uint8_t)in[0]; + out[1] = (uint8_t)in[1]; + out[2] = (uint8_t)in[2]; + out[3] = (uint8_t)in[3]; + out[4] = (uint8_t)in[4]; + out[5] = (uint8_t)in[5]; + out[6] = (uint8_t)in[6]; + out[7] = (uint8_t)in[7]; + in += 8; + out += 8; + in_size -= 8; + out_chars -= 8; + ret += 8; + } - if (ones > 6 || ones == 1) /* Invalid or desync. */ - break; + while (in_size && out_chars && (uint8_t)*in < 0x80) + { + *out++ = (uint8_t)*in++; + in_size--; + out_chars--; + ret++; + } - extra = ones ? ones - 1 : ones; - if (1 + extra > in_size) /* Overflow. */ - break; + if (!in_size || !out_chars) + break; + } - shift = (extra - 1) * 6; - c = (first & ((1 << (7 - ones)) - 1)) << (6 * extra); + first = (uint8_t)*in++; - for (i = 0; i < extra; i++, in++, shift -= 6) - c |= (*in & 0x3f) << shift; + /* Dispatch on the lead byte with direct comparisons instead of + * the LUT: the table indexing puts a dependent load on the + * critical path of every multibyte character, and the compare + * chain resolves 2- and 3-byte leads (the common cases) first. */ + if (first < 0xE0) + { + if (first < 0xC0) /* Continuation byte: desync. */ + break; + if (in_size < 2) /* Not enough data. */ + break; + c = ((uint32_t)(first & 0x1F) << 6) + | ((uint8_t)*in++ & 0x3F); + in_size -= 2; + } + else if (first < 0xF0) + { + if (in_size < 3) + break; + c = (uint32_t)(first & 0x0F) << 6; + c = (c | ((uint8_t)*in++ & 0x3F)) << 6; + c = c | ((uint8_t)*in++ & 0x3F); + in_size -= 3; + } + else if (first < 0xF8) + { + if (in_size < 4) + break; + c = (uint32_t)(first & 0x07) << 6; + c = (c | ((uint8_t)*in++ & 0x3F)) << 6; + c = (c | ((uint8_t)*in++ & 0x3F)) << 6; + c = c | ((uint8_t)*in++ & 0x3F); + in_size -= 4; + } + else + { + /* 5/6-byte forms and 0xFE/0xFF: invalid UTF-8. Decode the + * 5/6-byte shapes as before (garbage in, garbage out), stop + * on 0xFE/0xFF. */ + unsigned i; + ones = utf8_lut[first]; + if (ones > 6) + break; + if (ones > in_size) + break; + c = first & ((1 << (7 - ones)) - 1); + for (i = 0; i < ones - 1; i++) + c = (c << 6) | ((uint8_t)*in++ & 0x3F); + in_size -= ones; + } - *out++ = c; - in_size -= 1 + extra; + *out++ = c; out_chars--; ret++; } - return ret; } +/** + * utf16_conv_utf8: + * + * Leaf function. + * + * Optimized: separated counting-only path (out==NULL) from + * encoding path to eliminate per-byte branch on `out`. + * Added explicit fast-path for BMP 2-byte and 3-byte encodings. + **/ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, const uint16_t *in, size_t in_size) { - size_t out_pos = 0; - size_t in_pos = 0; - static const - uint8_t utf8_limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + size_t out_pos = 0; + size_t in_pos = 0; + + if (!out) + { + /* Counting-only pass: no stores, + no per-byte `if (out)` branches */ + for (;;) + { + uint32_t value; + if (in_pos == in_size) + { + *out_chars = out_pos; + return true; + } + value = in[in_pos++]; + if (value < 0x80) + { + out_pos++; + continue; + } + + if (value >= 0xD800 && value < 0xE000) + { + uint32_t c2; + if (value >= 0xDC00 || in_pos == in_size) + break; + c2 = in[in_pos++]; + if (c2 < 0xDC00 || c2 >= 0xE000) + break; + value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; + } + + if (value < 0x800) + out_pos += 2; + else if (value < 0x10000) + out_pos += 3; + else + out_pos += 4; + } + *out_chars = out_pos; + return false; + } + + /* Encoding pass */ for (;;) { - unsigned num_adds; uint32_t value; - if (in_pos == in_size) { *out_chars = out_pos; return true; } - value = in[in_pos++]; - if (value < 0x80) + + /* Batch ASCII run: avoid per-char branch into multi-byte path */ + while (in_pos < in_size && in[in_pos] < 0x80) + out[out_pos++] = (uint8_t)in[in_pos++]; + + if (in_pos == in_size) { - if (out) - out[out_pos] = (char)value; - out_pos++; - continue; + *out_chars = out_pos; + return true; } + value = in[in_pos++]; + if (value >= 0xD800 && value < 0xE000) { uint32_t c2; - if (value >= 0xDC00 || in_pos == in_size) break; c2 = in[in_pos++]; @@ -127,65 +292,99 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; } - for (num_adds = 1; num_adds < 5; num_adds++) - if (value < (((uint32_t)1) << (num_adds * 5 + 6))) - break; - if (out) - out[out_pos] = (char)(utf8_limits[num_adds - 1] - + (value >> (6 * num_adds))); - out_pos++; - do + if (value < 0x800) + { + /* 2-byte sequence */ + out[out_pos] = (uint8_t)(0xC0 | (value >> 6)); + out[out_pos + 1] = (uint8_t)(0x80 | (value & 0x3F)); + out_pos += 2; + } + else if (value < 0x10000) { - num_adds--; - if (out) - out[out_pos] = (char)(0x80 - + ((value >> (6 * num_adds)) & 0x3F)); - out_pos++; - }while (num_adds != 0); + /* 3-byte sequence */ + out[out_pos] = (uint8_t)(0xE0 | (value >> 12)); + out[out_pos + 1] = (uint8_t)(0x80 | ((value >> 6) & 0x3F)); + out[out_pos + 2] = (uint8_t)(0x80 | (value & 0x3F)); + out_pos += 3; + } + else + { + /* 4-byte sequence */ + out[out_pos] = (uint8_t)(0xF0 | (value >> 18)); + out[out_pos + 1] = (uint8_t)(0x80 | ((value >> 12) & 0x3F)); + out[out_pos + 2] = (uint8_t)(0x80 | ((value >> 6) & 0x3F)); + out[out_pos + 3] = (uint8_t)(0x80 | (value & 0x3F)); + out_pos += 4; + } } *out_chars = out_pos; return false; } -/* Acts mostly like strlcpy. +/** + * utf8cpy: + * + * Acts mostly like strlcpy. * * Copies the given number of UTF-8 characters, - * but at most d_len bytes. + * but at most @len bytes. + * + * Always NULL terminates. Does not copy half a character. + * @s is assumed valid UTF-8. + * Use only if @chars is considerably less than @len. * - * Always NULL terminates. - * Does not copy half a character. + * Nothing is written when @len is 0, since there is no room even + * for the terminator. * - * Returns number of bytes. 's' is assumed valid UTF-8. - * Use only if 'chars' is considerably less than 'd_len'. */ -size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars) + * @return Number of bytes. + **/ +size_t utf8cpy(char *s, size_t len, const char *in, size_t chars) { - const uint8_t *sb = (const uint8_t*)s; + size_t byte_count; + const uint8_t *sb = (const uint8_t*)in; const uint8_t *sb_org = sb; - if (!s) + if (!in || !len) return 0; while (*sb && chars-- > 0) { + /* Stepping over continuation bytes stops at the terminator by + * itself, so a truncated sequence cannot overrun the buffer. */ sb++; while ((*sb & 0xC0) == 0x80) sb++; } - if ((size_t)(sb - sb_org) > d_len-1 /* NUL */) + if ((size_t)(sb - sb_org) > len - 1) { - sb = sb_org + d_len-1; - while ((*sb & 0xC0) == 0x80) + sb = sb_org + len - 1; + /* @in may itself begin with continuation bytes; do not scan + * backwards out of the buffer looking for a lead byte. */ + while (sb > sb_org && (*sb & 0xC0) == 0x80) sb--; } - memcpy(d, sb_org, sb-sb_org); - d[sb-sb_org] = '\0'; - - return sb-sb_org; + byte_count = (size_t)(sb - sb_org); + memcpy(s, sb_org, byte_count); + s[byte_count] = '\0'; + return byte_count; } +/** + * utf8skip: + * + * Leaf function. + * + * Optimized: comparison dispatch on the lead byte (no dependent LUT + * load on the per-character path), NUL-guarded stepping over + * multibyte sequences, and ASCII runs skipped eight characters per + * masked 64-bit word test. The word test requires every byte to be + * ASCII and non-NUL, so it can neither overshoot the terminator nor + * miscount characters. memcpy load: alignment/aliasing safe, endian + * neutral. + **/ const char *utf8skip(const char *str, size_t chars) { const uint8_t *strb = (const uint8_t*)str; @@ -195,115 +394,309 @@ const char *utf8skip(const char *str, size_t chars) do { - strb++; - while ((*strb & 0xC0)==0x80) + uint8_t b = *strb; + if (!b) + break; + if (b < 0xC0) + { + /* ASCII or lone continuation byte: one char, one byte. */ + strb++; + if (b < 0x80) + { + /* Batch the rest of an ASCII run. The current character + * is consumed by the --chars below, so batch only while + * more than one character remains in the budget. + * + * Byte steps rather than a wide word test: utf8skip's + * contract is a NUL-terminated string with no length, so + * an 8-byte load could read past a terminator that falls + * inside the word - on a tightly sized allocation that is + * a read beyond the end of the buffer, even though the + * zero test would stop the cursor before consuming those + * bytes. Stepping a byte at a time stops exactly at the + * terminator or the first multibyte lead and still skips + * the per-character dispatch for the run; only the load + * width changes. */ + while (chars > 1) + { + uint8_t nb = *strb; + if (nb == 0 || nb >= 0x80) + break; + strb++; + chars--; + } + } + } + else if (b < 0xE0) + { + strb++; + if (*strb) + strb++; + } + else if (b < 0xF0) + { + strb++; + if (*strb) + { + strb++; + if (*strb) + strb++; + } + } + else if (b < 0xF8) + { strb++; - chars--; - }while (chars); + if (*strb) + { + strb++; + if (*strb) + { + strb++; + if (*strb) + strb++; + } + } + } + else + { + /* Invalid 5/6/7-lead: step over utf8_lut[b] bytes stopping + * at NUL, exactly as the LUT loop did. */ + unsigned ones = utf8_lut[b]; + unsigned i; + for (i = 0; i < ones && strb[i]; i++) + ; + strb += i; + } + } while (--chars); return (const char*)strb; } +/** + * utf8len: + * + * Leaf function. + **/ size_t utf8len(const char *string) { + const unsigned char *p; + size_t n; size_t ret = 0; if (!string) return 0; - while (*string) + p = (const unsigned char*)string; + n = strlen(string); + + /* Byte at a time up to the first aligned address. */ + while (n && (((size_t)p & 7) != 0)) { - if ((*string & 0xC0) != 0x80) + if ((*p & 0xC0) != 0x80) ret++; - string++; + p++; + n--; + } + + /* The length is known, so the word loop needs no terminator test + * and never reads a byte the caller did not supply. */ + while (n >= 8) + { + uint64_t w; + uint64_t c; + memcpy(&w, p, sizeof(w)); + /* A continuation byte is the pattern 10xxxxxx: bit 7 set and + * bit 6 clear. Both halves of the test stay inside their own + * byte, so this is endian neutral. */ + c = (w & ~(w << 1) & 0x8080808080808080ULL) >> 7; + /* Horizontal sum. Every byte of c is 0 or 1 and the running + * total tops out at 8, so the folds cannot carry out of a + * byte and no 64-bit multiply is needed. */ + c += c >> 32; + c += c >> 16; + c += c >> 8; + ret += 8 - (size_t)(c & 0xFF); + p += 8; + n -= 8; + } + + while (n) + { + if ((*p & 0xC0) != 0x80) + ret++; + p++; + n--; } return ret; } -/* Does not validate the input, returns garbage if it's not UTF-8. */ +/** + * utf8_walk: + * + * Does not validate the input, but never reads or steps past a + * terminating NUL, even mid-sequence: a truncated multibyte tail + * previously read up to three bytes beyond the terminator and left + * the cursor past it, walking a while (*str) caller out of the + * buffer. + * + * Leaf function. + * + * @return Returns garbage if it's not UTF-8. + **/ uint32_t utf8_walk(const char **string) { - uint8_t first = UTF8_WALKBYTE(string); - uint32_t ret = 0; + const uint8_t *s = (const uint8_t*)*string; + uint8_t first = *s++; + uint8_t b; + uint32_t ret; - if (first < 128) + if (first < 0x80) + { + *string = (const char*)s; return first; + } - ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F); - if (first >= 0xE0) + /* Dispatch on the lead byte with direct comparisons, matching + * utf8_conv_utf32: the LUT indexing put a dependent load on the + * critical path of every glyph decoded by the per-frame text + * renderers, and the compare chain resolves the common 2- and + * 3-byte leads first. Continuation and 5-byte-plus leads take the + * final branch and decode to garbage, as before. + * + * Each continuation read tests the byte it already loaded against + * NUL before consuming it; the branch is never taken on valid + * input, and on a truncated tail the cursor parks at the + * terminator with a partial (garbage) return. */ + if (first < 0xE0) + { + if (first < 0xC0) + /* Lone continuation byte: desync. Do not consume another + * byte, or a caller iterating with while (*str) could be + * carried past the terminator. Same masked-garbage return + * as the LUT path produced. */ + ret = first & 0x3F; + else + { + ret = first & 0x1F; + if ((b = *s) != 0) + { + s++; + ret = (ret << 6) | (b & 0x3F); + } + } + } + else if (first < 0xF0) { - ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F); - if (first >= 0xF0) + ret = first & 0x0F; + if ((b = *s) != 0) { - ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F); - return ret | (first & 7) << 18; + s++; + ret = (ret << 6) | (b & 0x3F); + if ((b = *s) != 0) + { + s++; + ret = (ret << 6) | (b & 0x3F); + } } - return ret | (first & 15) << 12; } + else if (first < 0xF8) + { + ret = first & 0x07; + if ((b = *s) != 0) + { + s++; + ret = (ret << 6) | (b & 0x3F); + if ((b = *s) != 0) + { + s++; + ret = (ret << 6) | (b & 0x3F); + if ((b = *s) != 0) + { + s++; + ret = (ret << 6) | (b & 0x3F); + } + } + } + } + else + ret = first & ((1 << (7 - utf8_lut[first])) - 1); - return ret | (first & 31) << 6; + *string = (const char*)s; + return ret; } static bool utf16_to_char(uint8_t **utf_data, size_t *dest_len, const uint16_t *in) { - unsigned len = 0; - - while (in[len] != '\0') - len++; - - utf16_conv_utf8(NULL, dest_len, in, len); - *dest_len += 1; - *utf_data = (uint8_t*)malloc(*dest_len); - if (*utf_data == 0) - return false; - - return utf16_conv_utf8(*utf_data, dest_len, in, len); + const uint16_t *p = in; + /* Find length in a single scan */ + while (*p != 0) + p++; + { + size_t in_len = (size_t)(p - in); + /* Single pass with a worst-case allocation instead of the + * count-then-encode double pass: a UTF-16 unit encodes to at + * most three UTF-8 bytes (a surrogate pair is two units for + * four bytes, i.e. two bytes per unit), so 3n + 1 always fits + * and the counting pass cost half the throughput of this + * function. The buffer is short-lived - the only caller copies + * out of it and frees it immediately. */ + if (in_len > (((size_t)-1) - 1) / 3) + return false; + if ((*utf_data = (uint8_t*)malloc(3 * in_len + 1)) != 0) + return utf16_conv_utf8(*utf_data, dest_len, in, in_len); + } + return false; } +/** + * utf16_to_char_string: + **/ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len) { - size_t dest_len = 0; - uint8_t *utf16_data = NULL; - bool ret = utf16_to_char(&utf16_data, &dest_len, in); - + size_t _len = 0; + uint8_t *utf16_data = NULL; + bool ret = utf16_to_char(&utf16_data, &_len, in); if (ret) { - utf16_data[dest_len] = 0; + utf16_data[_len] = 0; strlcpy(s, (const char*)utf16_data, len); } - free(utf16_data); - utf16_data = NULL; - + utf16_data = NULL; return ret; } #if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) -/* Returned pointer MUST be freed by the caller if non-NULL. */ +/** + * mb_to_mb_string_alloc: + * + * @return Returned pointer MUST be freed by the caller if non-NULL. + **/ static char *mb_to_mb_string_alloc(const char *str, enum CodePage cp_in, enum CodePage cp_out) { wchar_t *path_buf_wide = NULL; int path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0); - /* Windows 95 will return 0 from these functions with + /* Windows 95 will return 0 from these functions with * a UTF8 codepage set without MSLU. * * From an unknown MSDN version (others omit this info): - * - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later: + * - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later: * Translate using UTF-8. When this is set, dwFlags must be zero. - * - Windows 95: Under the Microsoft Layer for Unicode, + * - Windows 95: Under the Microsoft Layer for Unicode, * MultiByteToWideChar also supports CP_UTF7 and CP_UTF8. */ if (!path_buf_wide_len) return strdup(str); - path_buf_wide = (wchar_t*) - calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t)); - - if (path_buf_wide) + /* +1 element for the terminator; the old expression added + * sizeof(wchar_t) ELEMENTS (a byte count used as an element + * count), harmlessly over-allocating. */ + if ((path_buf_wide = (wchar_t*) + calloc((size_t)path_buf_wide_len + 1, sizeof(wchar_t)))) { MultiByteToWideChar(cp_in, 0, str, -1, path_buf_wide, path_buf_wide_len); @@ -316,7 +709,7 @@ static char *mb_to_mb_string_alloc(const char *str, if (path_buf_len) { char *path_buf = (char*) - calloc(path_buf_len + sizeof(char), sizeof(char)); + calloc((size_t)path_buf_len + 1, sizeof(char)); if (path_buf) { @@ -347,45 +740,75 @@ static char *mb_to_mb_string_alloc(const char *str, } #endif -/* Returned pointer MUST be freed by the caller if non-NULL. */ +/** + * utf8_to_local_string_alloc: + * + * @return Returned pointer MUST be freed by the caller if non-NULL. + **/ char* utf8_to_local_string_alloc(const char *str) { if (str && *str) - { #if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL); #else - /* assume string needs no modification if not on Windows */ - return strdup(str); + return strdup(str); /* Assume string needs no modification if not on Windows */ #endif - } return NULL; } -/* Returned pointer MUST be freed by the caller if non-NULL. */ -char* local_to_utf8_string_alloc(const char *str) +/** + * local_to_utf8_string: + * + * The guard is the one in local_to_utf8_string_alloc() below: where it + * resolves to a plain copy there is nothing to convert, so there is + * nothing to allocate either. + **/ +bool local_to_utf8_string(const char *in, char *s, size_t len) { - if (str && *str) + if (!s || !len) + return false; + s[0] = '\0'; + if (!in || !*in) + return true; +#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) { + char *tmp = mb_to_mb_string_alloc(in, CODEPAGE_LOCAL, CODEPAGE_UTF8); + if (!tmp) + return false; + strlcpy(s, tmp, len); + free(tmp); + } +#else + strlcpy(s, in, len); +#endif + return true; +} + +/** + * local_to_utf8_string_alloc: + * + * @return Returned pointer MUST be freed by the caller if non-NULL. + **/ +char *local_to_utf8_string_alloc(const char *str) +{ + if (str && *str) #if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) - return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8); + return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8); #else - /* assume string needs no modification if not on Windows */ - return strdup(str); + return strdup(str); /* Assume string needs no modification if not on Windows */ #endif - } - return NULL; + return NULL; } -/* Returned pointer MUST be freed by the caller if non-NULL. */ -wchar_t* utf8_to_utf16_string_alloc(const char *str) +/** + * utf8_to_utf16_string_alloc: + * + * @return Returned pointer MUST be freed by the caller if non-NULL. + **/ +wchar_t *utf8_to_utf16_string_alloc(const char *str) { #ifdef _WIN32 - int len = 0; - int out_len = 0; -#else - size_t len = 0; - size_t out_len = 0; + int _len = 0; #endif wchar_t *buf = NULL; @@ -393,69 +816,105 @@ wchar_t* utf8_to_utf16_string_alloc(const char *str) return NULL; #ifdef _WIN32 - len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); - - if (len) + if ((_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) { - buf = (wchar_t*)calloc(len, sizeof(wchar_t)); - - if (!buf) + if (!(buf = (wchar_t*)calloc(_len, sizeof(wchar_t)))) return NULL; - out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len); + if ((MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, _len)) < 0) + { + free(buf); + return NULL; + } } else { - /* fallback to ANSI codepage instead */ - len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); - - if (len) + /* Fallback to ANSI codepage instead */ + if ((_len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0))) { - buf = (wchar_t*)calloc(len, sizeof(wchar_t)); - - if (!buf) + if (!(buf = (wchar_t*)calloc(_len, sizeof(wchar_t)))) return NULL; - out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len); + if ((MultiByteToWideChar(CP_ACP, 0, str, -1, buf, _len)) < 0) + { + free(buf); + return NULL; + } } } - - if (out_len < 0) - { - free(buf); - return NULL; - } #else - /* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */ - len = mbstowcs(NULL, str, 0) + 1; - - if (len) + /* Locale-independent conversion. mbstowcs only decodes UTF-8 when + * the active locale says so: under the default C/POSIX locale + * (headless machines, containers, any process that never calls + * setlocale) it fails on the first non-ASCII byte and this + * function returned NULL. Decode with the in-house converter + * instead: exact UTF-32 into 32-bit wchar_t, UTF-16 with + * surrogate pairs when wchar_t is 16-bit. Scalars above U+10FFFF + * (only reachable from invalid input) become U+FFFD on the + * 16-bit path. */ { - buf = (wchar_t*)calloc(len, sizeof(wchar_t)); + size_t n8 = strlen(str); + uint32_t *u32 = (uint32_t*)malloc(n8 * sizeof(uint32_t)); - if (!buf) + if (!u32) return NULL; - out_len = mbstowcs(buf, str, len); - } + { + size_t n32 = utf8_conv_utf32(u32, n8, str, n8); + size_t i; - if (out_len == (size_t)-1) - { - free(buf); - return NULL; + if (sizeof(wchar_t) == 2) + { + /* Worst case two units per scalar, plus terminator */ + if ((buf = (wchar_t*)malloc((2 * n32 + 1) * sizeof(wchar_t)))) + { + size_t o = 0; + for (i = 0; i < n32; i++) + { + uint32_t cp = u32[i]; + if (cp < 0x10000) + buf[o++] = (wchar_t)cp; + else if (cp <= 0x10FFFF) + { + cp -= 0x10000; + buf[o++] = (wchar_t)(0xD800 | (cp >> 10)); + buf[o++] = (wchar_t)(0xDC00 | (cp & 0x3FF)); + } + else + buf[o++] = (wchar_t)0xFFFD; + } + buf[o] = 0; + } + } + else + { + if ((buf = (wchar_t*)malloc((n32 + 1) * sizeof(wchar_t)))) + { + for (i = 0; i < n32; i++) + buf[i] = (wchar_t)u32[i]; + buf[n32] = 0; + } + } + } + + free(u32); } #endif return buf; } -/* Returned pointer MUST be freed by the caller if non-NULL. */ -char* utf16_to_utf8_string_alloc(const wchar_t *str) +/** + * utf16_to_utf8_string_alloc: + * + * @return Returned pointer MUST be freed by the caller if non-NULL. + **/ +char *utf16_to_utf8_string_alloc(const wchar_t *str) { #ifdef _WIN32 - int len = 0; + int _len = 0; #else - size_t len = 0; + size_t _len = 0; #endif char *buf = NULL; @@ -465,46 +924,93 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str) #ifdef _WIN32 { UINT code_page = CP_UTF8; - len = WideCharToMultiByte(code_page, - 0, str, -1, NULL, 0, NULL, NULL); /* fallback to ANSI codepage instead */ - if (!len) + if (!(_len = WideCharToMultiByte(code_page, + 0, str, -1, NULL, 0, NULL, NULL))) { code_page = CP_ACP; - len = WideCharToMultiByte(code_page, + _len = WideCharToMultiByte(code_page, 0, str, -1, NULL, 0, NULL, NULL); } - buf = (char*)calloc(len, sizeof(char)); - - if (!buf) + if (!(buf = (char*)calloc(_len, sizeof(char)))) return NULL; if (WideCharToMultiByte(code_page, - 0, str, -1, buf, len, NULL, NULL) < 0) + 0, str, -1, buf, _len, NULL, NULL) < 0) { free(buf); return NULL; } } #else - /* NOTE: For now, assume non-Windows platforms' - * locale is already UTF-8. */ - len = wcstombs(NULL, str, 0) + 1; - - if (len) + /* Locale-independent conversion; see utf8_to_utf16_string_alloc. + * wcstombs had the same C/POSIX-locale failure on non-ASCII. + * 32-bit wchar_t is re-expressed as UTF-16 (exact for valid + * scalars) so the existing count-then-encode converter can do the + * encoding; unpaired surrogates or out-of-range values make it + * bail, and NULL is returned as the old code did for input + * wcstombs could not represent. */ { - buf = (char*)calloc(len, sizeof(char)); + size_t in_len = 0; + const wchar_t *p = str; + uint16_t *u16; + + while (*p++) + in_len++; - if (!buf) + /* Worst case two units per wchar */ + if (!(u16 = (uint16_t*)malloc((2 * in_len) * sizeof(uint16_t)))) return NULL; - if (wcstombs(buf, str, len) == (size_t)-1) { - free(buf); - return NULL; + size_t n16 = 0; + size_t i; + bool ok = true; + + if (sizeof(wchar_t) == 2) + { + for (i = 0; i < in_len; i++) + u16[n16++] = (uint16_t)str[i]; + } + else + { + for (i = 0; i < in_len; i++) + { + uint32_t cp = (uint32_t)str[i]; + if (cp < 0x10000) + u16[n16++] = (uint16_t)cp; + else if (cp <= 0x10FFFF) + { + cp -= 0x10000; + u16[n16++] = (uint16_t)(0xD800 | (cp >> 10)); + u16[n16++] = (uint16_t)(0xDC00 | (cp & 0x3FF)); + } + else + { + ok = false; + break; + } + } + } + + if (ok && utf16_conv_utf8(NULL, &_len, u16, n16)) + { + if ((buf = (char*)malloc(_len + 1))) + { + if (utf16_conv_utf8((uint8_t*)buf, &_len, u16, n16)) + buf[_len] = '\0'; + else + { + free(buf); + buf = NULL; + } + } + } } + + free(u16); } #endif diff --git a/src/deps/libretro-common/file/file_path.c b/src/deps/libretro-common/file/file_path.c index f3e32b9..0b853ca 100644 --- a/src/deps/libretro-common/file/file_path.c +++ b/src/deps/libretro-common/file/file_path.c @@ -24,15 +24,14 @@ #include #include #include -#include +#include +#include #include #include #include -#include #include -#include #include