blob: 717262033c1f1056bfb5f9fc4e7905031dcd80a7 [file] [log] [blame]
// Copyright 2019 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/base/string_view.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "iree/base/api.h"
static inline size_t iree_min_host_size(iree_host_size_t a,
iree_host_size_t b) {
return a < b ? a : b;
}
// Here to ensure that we don't pull in locale-specific code:
static bool iree_isupper(char c) { return (unsigned)c - 'A' < 26; }
static bool iree_islower(char c) { return (unsigned)c - 'a' < 26; }
static inline char iree_toupper(char c) {
return iree_islower(c) ? (c & 0x5F) : c;
}
static inline char iree_tolower(char c) {
return iree_isupper(c) ? (c | 32) : c;
}
IREE_API_EXPORT bool iree_string_view_equal(iree_string_view_t lhs,
iree_string_view_t rhs) {
if (lhs.size != rhs.size) return false;
for (iree_host_size_t i = 0; i < lhs.size; ++i) {
if (lhs.data[i] != rhs.data[i]) return false;
}
return true;
}
IREE_API_EXPORT bool iree_string_view_equal_case(iree_string_view_t lhs,
iree_string_view_t rhs) {
if (lhs.size != rhs.size) return false;
for (iree_host_size_t i = 0; i < lhs.size; ++i) {
if (iree_tolower(lhs.data[i]) != iree_tolower(rhs.data[i])) return false;
}
return true;
}
IREE_API_EXPORT int iree_string_view_compare(iree_string_view_t lhs,
iree_string_view_t rhs) {
iree_host_size_t min_size = iree_min_host_size(lhs.size, rhs.size);
int cmp = strncmp(lhs.data, rhs.data, min_size);
if (cmp != 0) {
return cmp;
} else if (lhs.size == rhs.size) {
return 0;
}
return lhs.size < rhs.size ? -1 : 1;
}
IREE_API_EXPORT iree_host_size_t iree_string_view_find_char(
iree_string_view_t value, char c, iree_host_size_t pos) {
if (iree_string_view_is_empty(value) || pos >= value.size) {
return IREE_STRING_VIEW_NPOS;
}
const char* result =
(const char*)(memchr(value.data + pos, c, value.size - pos));
return result != NULL ? result - value.data : IREE_STRING_VIEW_NPOS;
}
IREE_API_EXPORT iree_host_size_t iree_string_view_find_first_of(
iree_string_view_t value, iree_string_view_t s, iree_host_size_t pos) {
if (iree_string_view_is_empty(value) || iree_string_view_is_empty(s)) {
return IREE_STRING_VIEW_NPOS;
}
if (s.size == 1) {
// Avoid the cost of the lookup table for a single-character search.
return iree_string_view_find_char(value, s.data[0], pos);
}
bool lookup_table[UCHAR_MAX + 1] = {0};
for (iree_host_size_t i = 0; i < s.size; ++i) {
lookup_table[(uint8_t)s.data[i]] = true;
}
for (iree_host_size_t i = pos; i < value.size; ++i) {
if (lookup_table[(uint8_t)value.data[i]]) {
return i;
}
}
return IREE_STRING_VIEW_NPOS;
}
IREE_API_EXPORT iree_host_size_t iree_string_view_find_last_of(
iree_string_view_t value, iree_string_view_t s, iree_host_size_t pos) {
if (iree_string_view_is_empty(value) || iree_string_view_is_empty(s)) {
return IREE_STRING_VIEW_NPOS;
}
bool lookup_table[UCHAR_MAX + 1] = {0};
for (iree_host_size_t i = 0; i < s.size; ++i) {
lookup_table[(uint8_t)s.data[i]] = true;
}
pos = iree_min(pos, value.size) + 1;
iree_host_size_t i = pos;
while (i != 0) {
--i;
if (lookup_table[(uint8_t)value.data[i]]) {
return i;
}
}
return IREE_STRING_VIEW_NPOS;
}
IREE_API_EXPORT bool iree_string_view_starts_with(iree_string_view_t value,
iree_string_view_t prefix) {
if (!value.data || !prefix.data || !prefix.size || prefix.size > value.size) {
return false;
}
return strncmp(value.data, prefix.data, prefix.size) == 0;
}
IREE_API_EXPORT bool iree_string_view_ends_with(iree_string_view_t value,
iree_string_view_t suffix) {
if (!value.data || !suffix.data || !suffix.size || suffix.size > value.size) {
return false;
}
return strncmp(value.data + value.size - suffix.size, suffix.data,
suffix.size) == 0;
}
IREE_API_EXPORT iree_string_view_t
iree_string_view_remove_prefix(iree_string_view_t value, iree_host_size_t n) {
if (n >= value.size) {
return iree_string_view_empty();
}
return iree_make_string_view(value.data + n, value.size - n);
}
IREE_API_EXPORT iree_string_view_t
iree_string_view_remove_suffix(iree_string_view_t value, iree_host_size_t n) {
if (n >= value.size) {
return iree_string_view_empty();
}
return iree_make_string_view(value.data, value.size - n);
}
IREE_API_EXPORT iree_string_view_t iree_string_view_strip_prefix(
iree_string_view_t value, iree_string_view_t prefix) {
if (iree_string_view_starts_with(value, prefix)) {
return iree_string_view_remove_prefix(value, prefix.size);
}
return value;
}
IREE_API_EXPORT iree_string_view_t iree_string_view_strip_suffix(
iree_string_view_t value, iree_string_view_t suffix) {
if (iree_string_view_ends_with(value, suffix)) {
return iree_string_view_remove_suffix(value, suffix.size);
}
return value;
}
IREE_API_EXPORT bool iree_string_view_consume_prefix(
iree_string_view_t* value, iree_string_view_t prefix) {
if (iree_string_view_starts_with(*value, prefix)) {
*value = iree_string_view_remove_prefix(*value, prefix.size);
return true;
}
return false;
}
IREE_API_EXPORT bool iree_string_view_consume_suffix(
iree_string_view_t* value, iree_string_view_t suffix) {
if (iree_string_view_ends_with(*value, suffix)) {
*value = iree_string_view_remove_suffix(*value, suffix.size);
return true;
}
return false;
}
IREE_API_EXPORT iree_string_view_t
iree_string_view_trim(iree_string_view_t value) {
if (iree_string_view_is_empty(value)) return value;
iree_host_size_t start = 0;
iree_host_size_t end = value.size - 1;
while (value.size > 0 && start <= end) {
if (isspace(value.data[start])) {
start++;
} else {
break;
}
}
while (end > start) {
if (isspace(value.data[end])) {
--end;
} else {
break;
}
}
return iree_make_string_view(value.data + start, end - start + 1);
}
IREE_API_EXPORT iree_string_view_t iree_string_view_substr(
iree_string_view_t value, iree_host_size_t pos, iree_host_size_t n) {
pos = iree_min_host_size(pos, value.size);
n = iree_min_host_size(n, value.size - pos);
return iree_make_string_view(value.data + pos, n);
}
IREE_API_EXPORT intptr_t iree_string_view_split(iree_string_view_t value,
char split_char,
iree_string_view_t* out_lhs,
iree_string_view_t* out_rhs) {
*out_lhs = iree_string_view_empty();
*out_rhs = iree_string_view_empty();
if (!value.data || !value.size) {
return -1;
}
const void* first_ptr = memchr(value.data, split_char, value.size);
if (!first_ptr) {
*out_lhs = value;
return -1;
}
intptr_t offset = (intptr_t)((const char*)(first_ptr)-value.data);
if (out_lhs) {
out_lhs->data = value.data;
out_lhs->size = offset;
}
if (out_rhs) {
out_rhs->data = value.data + offset + 1;
out_rhs->size = value.size - offset - 1;
}
return offset;
}
IREE_API_EXPORT void iree_string_view_replace_char(iree_string_view_t value,
char old_char,
char new_char) {
char* p = (char*)value.data;
for (iree_host_size_t i = 0; i < value.size; ++i) {
if (p[i] == old_char) p[i] = new_char;
}
}
static bool iree_string_view_match_pattern_impl(iree_string_view_t value,
iree_string_view_t pattern) {
iree_host_size_t next_char_index = iree_string_view_find_first_of(
pattern, iree_make_cstring_view("*?"), /*pos=*/0);
if (next_char_index == IREE_STRING_VIEW_NPOS) {
return iree_string_view_equal(value, pattern);
} else if (next_char_index > 0) {
iree_string_view_t value_prefix =
iree_string_view_substr(value, 0, next_char_index);
iree_string_view_t pattern_prefix =
iree_string_view_substr(pattern, 0, next_char_index);
if (!iree_string_view_equal(value_prefix, pattern_prefix)) {
return false;
}
value =
iree_string_view_substr(value, next_char_index, IREE_STRING_VIEW_NPOS);
pattern = iree_string_view_substr(pattern, next_char_index,
IREE_STRING_VIEW_NPOS);
}
if (iree_string_view_is_empty(value) && iree_string_view_is_empty(pattern)) {
return true;
}
char pattern_char = pattern.data[0];
if (pattern_char == '*' && pattern.size > 1 &&
iree_string_view_is_empty(value)) {
return false;
} else if (pattern_char == '*' && pattern.size == 1) {
return true;
} else if (pattern_char == '?' || value.data[0] == pattern_char) {
return iree_string_view_match_pattern_impl(
iree_string_view_substr(value, 1, IREE_STRING_VIEW_NPOS),
iree_string_view_substr(pattern, 1, IREE_STRING_VIEW_NPOS));
} else if (pattern_char == '*') {
return iree_string_view_match_pattern_impl(
value,
iree_string_view_substr(pattern, 1, IREE_STRING_VIEW_NPOS)) ||
iree_string_view_match_pattern_impl(
iree_string_view_substr(value, 1, IREE_STRING_VIEW_NPOS),
pattern);
}
return false;
}
IREE_API_EXPORT bool iree_string_view_match_pattern(
iree_string_view_t value, iree_string_view_t pattern) {
return iree_string_view_match_pattern_impl(value, pattern);
}
IREE_API_EXPORT iree_host_size_t iree_string_view_append_to_buffer(
iree_string_view_t source_value, iree_string_view_t* target_value,
char* buffer) {
// Do not copy zero-sized values to avoid passing NULLs to memcpy. The source
// and destination pointers are required to be valid and non-NULL by the C
// standard.
if (source_value.size > 0) {
memcpy(buffer, source_value.data, source_value.size);
}
target_value->data = buffer;
target_value->size = source_value.size;
return source_value.size;
}
// NOTE: these implementations aren't great due to the enforced memcpy we
// perform. These _should_ never be on a hot path, though, so this keeps our
// code size small.
IREE_API_EXPORT bool iree_string_view_atoi_int32(iree_string_view_t value,
int32_t* out_value) {
// Copy to scratch memory with a NUL terminator.
char temp[16] = {0};
if (value.size >= IREE_ARRAYSIZE(temp)) return false;
memcpy(temp, value.data, value.size);
// Attempt to parse.
errno = 0;
char* end = NULL;
long parsed_value = strtol(temp, &end, 0);
if (temp == end) return false;
if ((parsed_value == LONG_MIN || parsed_value == LONG_MAX) &&
errno == ERANGE) {
return false;
}
*out_value = (int32_t)parsed_value;
return parsed_value != 0 || errno == 0;
}
IREE_API_EXPORT bool iree_string_view_atoi_uint32(iree_string_view_t value,
uint32_t* out_value) {
// Copy to scratch memory with a NUL terminator.
char temp[16] = {0};
if (value.size >= IREE_ARRAYSIZE(temp)) return false;
memcpy(temp, value.data, value.size);
// Attempt to parse.
errno = 0;
char* end = NULL;
unsigned long parsed_value = strtoul(temp, &end, 0);
if (temp == end) return false;
if (parsed_value == ULONG_MAX && errno == ERANGE) return false;
*out_value = (uint32_t)parsed_value;
return parsed_value != 0 || errno == 0;
}
IREE_API_EXPORT bool iree_string_view_atoi_int64(iree_string_view_t value,
int64_t* out_value) {
// Copy to scratch memory with a NUL terminator.
char temp[32] = {0};
if (value.size >= IREE_ARRAYSIZE(temp)) return false;
memcpy(temp, value.data, value.size);
// Attempt to parse.
errno = 0;
char* end = NULL;
long long parsed_value = strtoll(temp, &end, 0);
if (temp == end) return false;
if ((parsed_value == LLONG_MIN || parsed_value == LLONG_MAX) &&
errno == ERANGE) {
return false;
}
*out_value = (int64_t)parsed_value;
return parsed_value != 0 || errno == 0;
}
IREE_API_EXPORT bool iree_string_view_atoi_uint64(iree_string_view_t value,
uint64_t* out_value) {
// Copy to scratch memory with a NUL terminator.
char temp[32] = {0};
if (value.size >= IREE_ARRAYSIZE(temp)) return false;
memcpy(temp, value.data, value.size);
// Attempt to parse.
errno = 0;
char* end = NULL;
unsigned long long parsed_value = strtoull(temp, &end, 0);
if (temp == end) return false;
if (parsed_value == ULLONG_MAX && errno == ERANGE) return false;
*out_value = (uint64_t)parsed_value;
return parsed_value != 0 || errno == 0;
}
IREE_API_EXPORT bool iree_string_view_atof(iree_string_view_t value,
float* out_value) {
// Copy to scratch memory with a NUL terminator.
char temp[32] = {0};
if (value.size >= IREE_ARRAYSIZE(temp)) return false;
memcpy(temp, value.data, value.size);
// Attempt to parse.
errno = 0;
char* end = NULL;
*out_value = strtof(temp, &end);
if (temp == end) return false;
return *out_value != 0 || errno == 0;
}
IREE_API_EXPORT bool iree_string_view_atod(iree_string_view_t value,
double* out_value) {
// Copy to scratch memory with a NUL terminator.
char temp[32] = {0};
if (value.size >= IREE_ARRAYSIZE(temp)) return false;
memcpy(temp, value.data, value.size);
// Attempt to parse.
errno = 0;
char* end = NULL;
*out_value = strtod(temp, &end);
if (temp == end) return false;
return *out_value != 0 || errno == 0;
}
static bool iree_string_view_parse_hex_nibble(char c, uint8_t* out_b) {
if ('0' <= c && c <= '9') {
*out_b = (uint8_t)(c - '0');
return true;
} else if ('a' <= c && c <= 'f') {
*out_b = (uint8_t)(c - 'a' + 10);
return true;
} else if ('A' <= c && c <= 'F') {
*out_b = (uint8_t)(c - 'A' + 10);
return true;
}
return false;
}
IREE_API_EXPORT bool iree_string_view_parse_hex_bytes(
iree_string_view_t value, iree_host_size_t buffer_length,
uint8_t* out_buffer) {
// Strip leading/trailing whitespace.
value = iree_string_view_trim(value);
// Try to consume all bytes.
for (iree_host_size_t i = 0; i < buffer_length; ++i) {
// Strip interior whitespace/-'s.
if (i > 0 && value.size) {
char c = value.data[0];
if (c == ' ' || c == '-') {
value = iree_string_view_remove_prefix(value, 1);
}
}
// Ensure there are two nibbles.
if (value.size < 2) return false;
// Hex nibbles to byte; fail if invalid characters.
uint8_t b0 = 0, b1 = 0;
if (!iree_string_view_parse_hex_nibble(value.data[0], &b0) ||
!iree_string_view_parse_hex_nibble(value.data[1], &b1)) {
return false;
}
out_buffer[i] = (b0 << 4) + b1;
// Eat the nibbles.
value = iree_string_view_remove_prefix(value, 2);
}
// Should have consumed all characters.
return iree_string_view_is_empty(value);
}
IREE_API_EXPORT iree_status_t iree_string_view_parse_device_size(
iree_string_view_t value, iree_device_size_t* out_size) {
// TODO(benvanik): probably worth to-lowering here on the size. Having copies
// of all the string view utils for just this case is code size overkill. For
// now only accept lazy lowercase.
iree_device_size_t scale = 1;
if (iree_string_view_consume_suffix(&value, IREE_SV("kb"))) {
scale = 1000;
} else if (iree_string_view_consume_suffix(&value, IREE_SV("kib"))) {
scale = 1024;
} else if (iree_string_view_consume_suffix(&value, IREE_SV("mb"))) {
scale = 1000 * 1000;
} else if (iree_string_view_consume_suffix(&value, IREE_SV("mib"))) {
scale = 1024 * 1024;
} else if (iree_string_view_consume_suffix(&value, IREE_SV("gb"))) {
scale = 1000 * 1000 * 1000;
} else if (iree_string_view_consume_suffix(&value, IREE_SV("gib"))) {
scale = 1024 * 1024 * 1024;
}
uint64_t size = 0;
if (!iree_string_view_atoi_uint64(value, &size)) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"size must be an integer, got '%.*s'",
(int)value.size, value.data);
}
size *= scale;
if (size > IREE_DEVICE_SIZE_MAX) {
return iree_make_status(
IREE_STATUS_OUT_OF_RANGE,
"size parsed (%" PRIu64
") is out of the representable range of iree_device_size_t (%" PRIu64
")",
size, (uint64_t)IREE_DEVICE_SIZE_MAX);
}
*out_size = size;
return iree_ok_status();
}