2011-12-22 15:19:25 +00:00
|
|
|
/*
|
|
|
|
* \brief String utility functions
|
|
|
|
* \author Norman Feske
|
2012-08-02 14:39:16 +00:00
|
|
|
* \author Sebastian Sumpf
|
2011-12-22 15:19:25 +00:00
|
|
|
* \date 2006-05-10
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 20:44:47 +00:00
|
|
|
* Copyright (C) 2006-2013 Genode Labs GmbH
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _INCLUDE__UTIL__STRING_H_
|
|
|
|
#define _INCLUDE__UTIL__STRING_H_
|
|
|
|
|
|
|
|
#include <base/stdint.h>
|
|
|
|
#include <util/misc_math.h>
|
2012-08-02 14:39:16 +00:00
|
|
|
#include <cpu/string.h>
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
namespace Genode {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine length of null-terminated string
|
|
|
|
*/
|
|
|
|
inline size_t strlen(const char *s)
|
|
|
|
{
|
|
|
|
size_t res = 0;
|
|
|
|
for (; s && *s; s++, res++);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare two strings
|
|
|
|
*
|
|
|
|
* \param len maximum number of characters to compare,
|
|
|
|
* default is unlimited
|
|
|
|
*
|
|
|
|
* \retval 0 strings are equal
|
|
|
|
* \retval >0 s1 is higher than s2
|
|
|
|
* \retval <0 s1 is lower than s2
|
|
|
|
*/
|
|
|
|
inline int strcmp(const char *s1, const char *s2, size_t len = ~0UL)
|
|
|
|
{
|
|
|
|
for (; *s1 && *s1 == *s2 && len; s1++, s2++, len--) ;
|
|
|
|
return len ? *s1 - *s2 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-10 14:43:55 +00:00
|
|
|
/**
|
|
|
|
* Simple memmove
|
|
|
|
*
|
|
|
|
* \param dst destination memory block
|
|
|
|
* \param src source memory block
|
|
|
|
* \param size number of bytes to move
|
|
|
|
*
|
|
|
|
* \return pointer to destination memory block
|
|
|
|
*/
|
|
|
|
inline void *memmove(void *dst, const void *src, size_t size)
|
|
|
|
{
|
|
|
|
char *d = (char *)dst, *s = (char *)src;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (s > d)
|
|
|
|
for (i = 0; i < size; i++, *d++ = *s++);
|
|
|
|
else
|
|
|
|
for (s += size - 1, d += size - 1, i = size; i-- > 0; *d-- = *s--);
|
|
|
|
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 15:19:25 +00:00
|
|
|
/**
|
|
|
|
* Copy memory block
|
|
|
|
*
|
|
|
|
* \param dst destination memory block
|
|
|
|
* \param src source memory block
|
|
|
|
* \param size number of bytes to copy
|
|
|
|
*
|
|
|
|
* \return pointer to destination memory block
|
|
|
|
*/
|
|
|
|
inline void *memcpy(void *dst, const void *src, size_t size)
|
|
|
|
{
|
|
|
|
char *d = (char *)dst, *s = (char *)src;
|
|
|
|
size_t i;
|
|
|
|
|
2013-01-10 14:43:55 +00:00
|
|
|
/* check for overlap */
|
|
|
|
if ((d + size > s) && (s + size > d))
|
|
|
|
return memmove(dst, src, size);
|
|
|
|
|
2012-08-02 14:39:16 +00:00
|
|
|
/* try cpu specific version first */
|
|
|
|
if ((i = size - memcpy_cpu(dst, src, size)) == size)
|
|
|
|
return dst;
|
|
|
|
|
|
|
|
d += i; s += i; size -= i;
|
|
|
|
|
|
|
|
/* copy eight byte chunks */
|
|
|
|
for (i = size >> 3; i > 0; i--, *d++ = *s++,
|
|
|
|
*d++ = *s++,
|
|
|
|
*d++ = *s++,
|
|
|
|
*d++ = *s++,
|
|
|
|
*d++ = *s++,
|
|
|
|
*d++ = *s++,
|
|
|
|
*d++ = *s++,
|
|
|
|
*d++ = *s++);
|
|
|
|
|
|
|
|
/* copy left over */
|
|
|
|
for (i = 0; i < (size & 0x7); i++, *d++ = *s++);
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy string
|
|
|
|
*
|
|
|
|
* \param dst destination buffer
|
|
|
|
* \param src buffer holding the null-terminated source string
|
|
|
|
* \param size maximum number of characters to copy
|
|
|
|
* \return pointer to destination string
|
|
|
|
*
|
|
|
|
* This function is not fully compatible to the C standard, in particular
|
|
|
|
* there is no zero-padding if the length of 'src' is smaller than 'size'.
|
|
|
|
* Furthermore, in contrast to the libc version, this function always
|
|
|
|
* produces a null-terminated string in the 'dst' buffer if the 'size'
|
|
|
|
* argument is greater than 0.
|
|
|
|
*/
|
|
|
|
inline char *strncpy(char *dst, const char *src, size_t size)
|
|
|
|
{
|
|
|
|
/* sanity check for corner case of a zero-size destination buffer */
|
|
|
|
if (size == 0) return dst;
|
|
|
|
|
|
|
|
/* backup original 'dst' for the use as return value */
|
|
|
|
char *orig_dst = dst;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy characters from 'src' to 'dst' respecting the 'size' limit.
|
|
|
|
* In each iteration, the 'size' variable holds the maximum remaining
|
|
|
|
* size. We have to leave at least one character free to add the null
|
|
|
|
* termination afterwards.
|
|
|
|
*/
|
|
|
|
while ((size-- > 1UL) && *src)
|
|
|
|
*dst++ = *src++;
|
|
|
|
|
|
|
|
/* append null termination to the destination buffer */
|
|
|
|
*dst = 0;
|
|
|
|
|
|
|
|
return orig_dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare memory blocks
|
|
|
|
*
|
2013-01-22 09:51:32 +00:00
|
|
|
* \retval 0 memory blocks are equal
|
|
|
|
* \retval <0 first memory block is less than second one
|
|
|
|
* \retval >0 first memory block is greater than second one
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
|
|
|
inline int memcmp(const void *p0, const void *p1, size_t size)
|
|
|
|
{
|
2013-01-22 09:51:32 +00:00
|
|
|
const unsigned char *c0 = (const unsigned char *)p0;
|
|
|
|
const unsigned char *c1 = (const unsigned char *)p1;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < size; i++)
|
2013-01-22 09:51:32 +00:00
|
|
|
if (c0[i] != c1[i]) return c0[i] - c1[i];
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Memset
|
|
|
|
*/
|
|
|
|
inline void *memset(void *dst, int i, size_t size)
|
|
|
|
{
|
|
|
|
while (size--) ((char *)dst)[size] = i;
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert ASCII character to digit
|
|
|
|
*
|
|
|
|
* \param hex consider hexadecimals
|
|
|
|
* \return digit or -1 on error
|
|
|
|
*/
|
|
|
|
inline int digit(char c, bool hex = false)
|
|
|
|
{
|
|
|
|
if (c >= '0' && c <= '9') return c - '0';
|
|
|
|
if (hex && c >= 'a' && c <= 'f') return c - 'a' + 10;
|
|
|
|
if (hex && c >= 'A' && c <= 'F') return c - 'A' + 10;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if character is a letter
|
|
|
|
*/
|
|
|
|
inline bool is_letter(char c)
|
|
|
|
{
|
|
|
|
return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if character is a digit
|
|
|
|
*/
|
|
|
|
inline bool is_digit(char c, bool hex = false)
|
|
|
|
{
|
|
|
|
return (digit(c, hex) >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if character is whitespace
|
|
|
|
*/
|
|
|
|
inline bool is_whitespace(char c)
|
|
|
|
{
|
|
|
|
return (c == '\t' || c == ' ' || c == '\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert ASCII string to another type
|
|
|
|
*
|
|
|
|
* \param T destination type of conversion
|
|
|
|
* \param s null-terminated source string
|
|
|
|
* \param result destination pointer to conversion result
|
|
|
|
* \param base base, autodetected if set to 0
|
|
|
|
* \return number of consumed characters
|
|
|
|
*
|
|
|
|
* Please note that 'base' and 's_max_len' are not evaluated by all
|
|
|
|
* template specializations.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
inline size_t ascii_to(const char *s, T *result, unsigned base = 0);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read unsigned long value from string
|
|
|
|
*/
|
|
|
|
template <>
|
|
|
|
inline size_t ascii_to<unsigned long>(const char *s, unsigned long *result,
|
|
|
|
unsigned base)
|
|
|
|
{
|
|
|
|
unsigned long i = 0, value = 0;
|
|
|
|
|
|
|
|
if (!*s) return i;
|
|
|
|
|
|
|
|
/* autodetect hexadecimal base, default is a base of 10 */
|
|
|
|
if (base == 0) {
|
|
|
|
|
|
|
|
/* read '0x' prefix */
|
|
|
|
if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
|
|
|
s += 2; i += 2;
|
|
|
|
base = 16;
|
|
|
|
} else
|
|
|
|
base = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read number */
|
|
|
|
for (int d; ; s++, i++) {
|
|
|
|
|
|
|
|
/* read digit, stop when hitting a non-digit character */
|
|
|
|
if ((d = digit(*s, base == 16)) < 0) break;
|
|
|
|
|
|
|
|
/* append digit to integer value */
|
|
|
|
value = value*base + d;
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = value;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read unsigned int value from string
|
|
|
|
*/
|
|
|
|
template <>
|
|
|
|
inline size_t ascii_to<unsigned int>(const char *s, unsigned int *result,
|
|
|
|
unsigned base)
|
|
|
|
{
|
|
|
|
unsigned long result_long = 0;
|
|
|
|
size_t ret = ascii_to<unsigned long>(s, &result_long, base);
|
|
|
|
*result = result_long;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read signed long value from string
|
|
|
|
*/
|
|
|
|
template <>
|
|
|
|
inline size_t ascii_to<long>(const char *s, long *result, unsigned base)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
/* read sign */
|
|
|
|
int sign = (*s == '-') ? -1 : 1;
|
|
|
|
|
|
|
|
if (*s == '-' || *s == '+') { s++; i++; }
|
|
|
|
|
|
|
|
int j = 0;
|
|
|
|
unsigned long value = 0;
|
|
|
|
|
|
|
|
j = ascii_to(s, &value, base);
|
|
|
|
|
|
|
|
if (!j) return i;
|
|
|
|
|
|
|
|
*result = sign*value;
|
|
|
|
return i + j;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper of 'size_t' for selecting 'ascii_to' specialization
|
|
|
|
*/
|
|
|
|
class Number_of_bytes
|
|
|
|
{
|
|
|
|
size_t _n;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor
|
|
|
|
*/
|
|
|
|
Number_of_bytes() : _n(0) { }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor, to be used implicitly via assignment operator
|
|
|
|
*/
|
|
|
|
Number_of_bytes(size_t n) : _n(n) { }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert number of bytes to 'size_t' value
|
|
|
|
*/
|
|
|
|
operator size_t() const { return _n; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read 'Number_of_bytes' value from string and handle the size suffixes
|
|
|
|
*
|
|
|
|
* This function scales the resulting size value according to the suffixes
|
|
|
|
* for G (2^30), M (2^20), and K (2^10) if present.
|
|
|
|
*/
|
|
|
|
template <>
|
|
|
|
inline size_t ascii_to(const char *s, Number_of_bytes *result, unsigned)
|
|
|
|
{
|
|
|
|
unsigned long res = 0;
|
|
|
|
|
|
|
|
/* convert numeric part of string */
|
|
|
|
int i = ascii_to(s, &res, 0);
|
|
|
|
|
|
|
|
/* handle suffixes */
|
|
|
|
if (i > 0)
|
|
|
|
switch (s[i]) {
|
|
|
|
case 'G': res *= 1024;
|
|
|
|
case 'M': res *= 1024;
|
|
|
|
case 'K': res *= 1024; i++;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = res;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read double float value from string
|
|
|
|
*/
|
|
|
|
template <>
|
|
|
|
inline size_t ascii_to<double>(const char *s, double *result, unsigned)
|
|
|
|
{
|
|
|
|
double v = 0.0; /* decimal part */
|
|
|
|
double d = 0.1; /* power of fractional digit */
|
|
|
|
bool neg = false; /* sign */
|
|
|
|
int i = 0; /* character counter */
|
|
|
|
|
|
|
|
if (s[i] == '-') {
|
|
|
|
neg = true;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse decimal part of number */
|
|
|
|
for (; s[i] && is_digit(s[i]); i++)
|
|
|
|
v = 10*v + digit(s[i], false);
|
|
|
|
|
|
|
|
/* if no fractional part exists, return current value */
|
|
|
|
if (s[i] != '.') {
|
|
|
|
*result = neg ? -v : v;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip comma */
|
|
|
|
i++;
|
|
|
|
|
|
|
|
/* parse fractional part of number */
|
|
|
|
for (; s[i] && is_digit(s[i]); i++, d *= 0.1)
|
|
|
|
v += d*digit(s[i], false);
|
|
|
|
|
|
|
|
*result = neg ? -v : v;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check for end of quotation
|
|
|
|
*
|
|
|
|
* Checks if next character is non-backslashed quotation mark.
|
|
|
|
*/
|
|
|
|
inline bool end_of_quote(const char *s) {
|
|
|
|
return s[0] != '\\' && s[1] == '\"'; }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unpack quoted string
|
|
|
|
*
|
|
|
|
* \param src source string including the quotation marks ("...")
|
|
|
|
* \param dst destination buffer
|
|
|
|
*
|
|
|
|
* \return number of characters or negative error code
|
|
|
|
*/
|
|
|
|
inline int unpack_string(const char *src, char *dst, int dst_len)
|
|
|
|
{
|
|
|
|
/* check if quoted string */
|
|
|
|
if (*src != '"') return -1;
|
|
|
|
|
|
|
|
src++;
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
for (; *src && !end_of_quote(src - 1) && (i < dst_len - 1); i++) {
|
|
|
|
|
|
|
|
/* transform '\"' to '"' */
|
|
|
|
if (src[0] == '\\' && src[1] == '\"') {
|
|
|
|
*dst++ = '"';
|
|
|
|
src += 2;
|
|
|
|
} else
|
|
|
|
*dst++ = *src++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write terminating null */
|
|
|
|
*dst = 0;
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
2013-09-19 18:35:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Buffer that contains a null-terminated string
|
|
|
|
*
|
|
|
|
* \param SIZE buffer size
|
|
|
|
*/
|
|
|
|
template <size_t SIZE>
|
|
|
|
class String
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
char _buf[SIZE];
|
|
|
|
size_t _length;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
constexpr static size_t size() { return SIZE; }
|
|
|
|
|
|
|
|
String() : _length(0) { }
|
|
|
|
|
|
|
|
String(char const *str) : _length(min(strlen(str) + 1, SIZE))
|
|
|
|
{
|
|
|
|
strncpy(_buf, str, _length);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool valid() const {
|
|
|
|
return (_length <= SIZE) && (_buf[_length - 1] == '\0'); }
|
|
|
|
|
|
|
|
char const *string() const { return valid() ? _buf : ""; }
|
|
|
|
};
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* _INCLUDE__UTIL__STRING_H_ */
|