first commit for chrg
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
menu "klibc options"
|
||||
menu "rt_vsnprintf options"
|
||||
config RT_KLIBC_USING_LIBC_VSNPRINTF
|
||||
bool "Enable rt_vsnprintf to use libc vsnprintf"
|
||||
default n
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
bool "Enable rt_vsnprintf function to support long-long format"
|
||||
depends on !RT_KLIBC_USING_LIBC_VSNPRINTF
|
||||
default n
|
||||
help
|
||||
Support for the long long integral types (with the ll, z and t length modifiers for specifiers
|
||||
%d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported.
|
||||
|
||||
menuconfig RT_KLIBC_USING_VSNPRINTF_STANDARD
|
||||
bool "Enable standard rt_vsnprintf version"
|
||||
default y if ARCH_CPU_64BIT
|
||||
default n
|
||||
select RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
depends on !RT_KLIBC_USING_LIBC_VSNPRINTF
|
||||
help
|
||||
Standard version of rt_vsnprintf, which is full function but higher stack usage.
|
||||
|
||||
if RT_KLIBC_USING_VSNPRINTF_STANDARD
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
|
||||
bool "Support decimal notation floating point conversion specifiers (%f, %F)"
|
||||
default y
|
||||
help
|
||||
Support for the decimal notation floating point conversion specifiers (%f, %F)
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
|
||||
bool "Support exponential notation floating point conversion specifiers (%e, %g, %E, %G)"
|
||||
default y
|
||||
help
|
||||
Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G)
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
|
||||
bool "Support length write-back specifier (%n)"
|
||||
default y
|
||||
help
|
||||
Support for the length write-back specifier (%n)
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER
|
||||
bool "safety check: no NULL end string"
|
||||
default y if RT_USING_DEBUG
|
||||
default n
|
||||
help
|
||||
Be extra-safe, and don't assume format specifiers are completed correctly
|
||||
before the format string end.
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS
|
||||
bool "Support MSVC style integer specifiers"
|
||||
default n
|
||||
help
|
||||
the integer format specifiers used in Microsoft's Visual C++ (MSVC) compiler.
|
||||
These specifiers, like %I64d for 64-bit integers, deviate slightly from the standard
|
||||
C format specifiers and are specific to MSVC. They allow for controlled formatting of
|
||||
integers in printf()-like functions, accommodating different integer sizes and ensuring
|
||||
compatibility with MSVC's environment. It's important to note that these specifiers might
|
||||
not be recognized or function in other compilers due to their MSVC-specific nature.
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE
|
||||
int "'ntoa' conversion buffer size"
|
||||
default 32
|
||||
help
|
||||
'ntoa' conversion buffer size, this must be big enough to hold one converted
|
||||
numeric number including padded zeros (dynamically created on stack)
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE
|
||||
int "printing individual decimal numbers buffer size"
|
||||
default 32
|
||||
help
|
||||
size of the fixed (on-stack) buffer for printing individual decimal numbers.
|
||||
this must be big enough to hold one converted floating-point value including
|
||||
padded zeros.
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION
|
||||
int "floating point conversion specifiers"
|
||||
default 6
|
||||
help
|
||||
Default precision for the floating point conversion specifiers (the C standard sets this at 6)
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL
|
||||
int "integral nums printed as float in rt_vsnprint"
|
||||
default 9
|
||||
help
|
||||
According to the C languages standard, printf() and related functions must be able to print any
|
||||
integral number in floating-point notation, regardless of length, when using the %f specifier -
|
||||
possibly hundreds of characters, potentially overflowing your buffers. In this implementation,
|
||||
all values beyond this threshold are switched to exponential notation.
|
||||
|
||||
config RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS
|
||||
int "the number of terms in a Taylor series expansion of log_10(x)"
|
||||
default 4
|
||||
range 2 99
|
||||
help
|
||||
The number of terms in a Taylor series expansion of log_10(x) to
|
||||
use for approximation - including the power-zero term (i.e. the
|
||||
value at the point of expansion).
|
||||
|
||||
endif
|
||||
endmenu # rt_vsnprintf options
|
||||
|
||||
menu "rt_vsscanf options"
|
||||
config RT_KLIBC_USING_LIBC_VSSCANF
|
||||
bool "Enable rt_vsscanf to use libc vsscanf"
|
||||
default n
|
||||
endmenu # rt_vsscanf options
|
||||
|
||||
menu "rt_memset options"
|
||||
config RT_KLIBC_USING_USER_MEMSET
|
||||
bool "Enable rt_memset to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_MEMSET
|
||||
config RT_KLIBC_USING_LIBC_MEMSET
|
||||
bool "Enable rt_memset to use libc memset"
|
||||
default n
|
||||
|
||||
config RT_KLIBC_USING_TINY_MEMSET
|
||||
bool "Enable rt_memset to use tiny version"
|
||||
depends on !RT_KLIBC_USING_LIBC_MEMSET
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_memset options
|
||||
|
||||
menu "rt_memcpy options"
|
||||
config RT_KLIBC_USING_USER_MEMCPY
|
||||
bool "Enable rt_memcpy to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_MEMCPY
|
||||
config RT_KLIBC_USING_LIBC_MEMCPY
|
||||
bool "Enable rt_memcpy to use libc memcpy"
|
||||
default n
|
||||
|
||||
config RT_KLIBC_USING_TINY_MEMCPY
|
||||
bool "Enable rt_memcpy to use tiny version"
|
||||
depends on !RT_KLIBC_USING_LIBC_MEMCPY
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_memcpy options
|
||||
|
||||
menu "rt_memmove options"
|
||||
config RT_KLIBC_USING_USER_MEMMOVE
|
||||
bool "Enable rt_memmove to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_MEMMOVE
|
||||
config RT_KLIBC_USING_LIBC_MEMMOVE
|
||||
bool "Enable rt_memmove to use libc memmove"
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_memmove options
|
||||
|
||||
menu "rt_memcmp options"
|
||||
config RT_KLIBC_USING_USER_MEMCMP
|
||||
bool "Enable rt_memcmp to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_MEMCMP
|
||||
config RT_KLIBC_USING_LIBC_MEMCMP
|
||||
bool "Enable rt_memcmp to use libc memcmp"
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_memcmp options
|
||||
|
||||
menu "rt_strstr options"
|
||||
config RT_KLIBC_USING_USER_STRSTR
|
||||
bool "Enable rt_strstr to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_STRSTR
|
||||
config RT_KLIBC_USING_LIBC_STRSTR
|
||||
bool "Enable rt_strstr to use libc strstr"
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_strstr options
|
||||
|
||||
menu "rt_strcasecmp options"
|
||||
config RT_KLIBC_USING_USER_STRCASECMP
|
||||
bool "Enable rt_strcasecmp to use user-defined version"
|
||||
default n
|
||||
endmenu # rt_strcasecmp options
|
||||
|
||||
menu "rt_strncpy options"
|
||||
config RT_KLIBC_USING_USER_STRNCPY
|
||||
bool "Enable rt_strncpy to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_STRNCPY
|
||||
config RT_KLIBC_USING_LIBC_STRNCPY
|
||||
bool "Enable rt_strncpy to use libc strncpy"
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_strncpy options
|
||||
|
||||
menu "rt_strcpy options"
|
||||
config RT_KLIBC_USING_USER_STRCPY
|
||||
bool "Enable rt_strcpy to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_STRCPY
|
||||
config RT_KLIBC_USING_LIBC_STRCPY
|
||||
bool "Enable rt_strcpy to use libc strcpy"
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_strcpy options
|
||||
|
||||
menu "rt_strncmp options"
|
||||
config RT_KLIBC_USING_USER_STRNCMP
|
||||
bool "Enable rt_strncmp to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_STRNCMP
|
||||
config RT_KLIBC_USING_LIBC_STRNCMP
|
||||
bool "Enable rt_strncmp to use libc strncmp"
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_strncmp options
|
||||
|
||||
menu "rt_strcmp options"
|
||||
config RT_KLIBC_USING_USER_STRCMP
|
||||
bool "Enable rt_strcmp to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_STRCMP
|
||||
config RT_KLIBC_USING_LIBC_STRCMP
|
||||
bool "Enable rt_strcmp to use libc strcmp"
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_strcmp options
|
||||
|
||||
menu "rt_strlen options"
|
||||
config RT_KLIBC_USING_USER_STRLEN
|
||||
bool "Enable rt_strlen to use user-defined version"
|
||||
default n
|
||||
|
||||
if !RT_KLIBC_USING_USER_STRLEN
|
||||
config RT_KLIBC_USING_LIBC_STRLEN
|
||||
bool "Enable rt_strlen to use libc strlen"
|
||||
default n
|
||||
endif
|
||||
endmenu # rt_strlen options
|
||||
|
||||
menu "rt_strnlen options"
|
||||
config RT_KLIBC_USING_USER_STRNLEN
|
||||
bool "Enable rt_strnlen to use user-defined version"
|
||||
default n
|
||||
endmenu # rt_strnlen options
|
||||
|
||||
config RT_UTEST_TC_USING_KLIBC
|
||||
bool "Enable klibc utest cases"
|
||||
select RT_USING_UTEST
|
||||
default n
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,23 @@
|
||||
from building import *
|
||||
import os
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = ['kerrno.c', 'kstdio.c', 'kstring.c']
|
||||
|
||||
if not GetDepend(['RT_KLIBC_USING_LIBC_VSNPRINTF']):
|
||||
if GetDepend(['RT_KLIBC_USING_VSNPRINTF_STANDARD']):
|
||||
src += ['rt_vsnprintf_std.c']
|
||||
else:
|
||||
src += ['rt_vsnprintf_tiny.c']
|
||||
|
||||
if not GetDepend(['RT_KLIBC_USING_LIBC_VSSCANF']):
|
||||
src += ['rt_vsscanf.c']
|
||||
|
||||
group = DefineGroup('klibc', src, depend = [''])
|
||||
|
||||
list = os.listdir(cwd)
|
||||
for item in list:
|
||||
if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
|
||||
group = group + SConscript(os.path.join(item, 'SConscript'))
|
||||
|
||||
Return('group')
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-09-22 Meco Man the first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
/**
|
||||
* @brief A global variable used to store the error code.
|
||||
*
|
||||
* This volatile static integer is used to store the most recent error code globally.
|
||||
* Its volatile nature ensures that every read operation fetches the most current value,
|
||||
* providing real-time error status across different parts of the program.
|
||||
*/
|
||||
static volatile int __rt_errno;
|
||||
|
||||
/**
|
||||
* @struct _errno_str_t
|
||||
* @brief Structure for mapping error codes to corresponding error strings.
|
||||
*
|
||||
* This structure is used to create a mapping that associates an rt_err_t type error code
|
||||
* with a corresponding error description string.
|
||||
*/
|
||||
struct _errno_str_t
|
||||
{
|
||||
rt_err_t error; /**< Error code of type rt_err_t, representing different kinds of errors. */
|
||||
const char *str; /**< Pointer to the error description string. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An array containing mappings of error codes to their corresponding error strings.
|
||||
*
|
||||
* This array uses the _errno_str_t structure to define several error codes and their
|
||||
* corresponding error description strings. These mappings can be used at runtime
|
||||
* to provide more readable error information.
|
||||
*/
|
||||
static struct _errno_str_t rt_errno_strs[] =
|
||||
{
|
||||
{RT_EOK , "OK "}, /**< Operation successful. */
|
||||
{RT_ERROR , "ERROR "}, /**< General error. */
|
||||
{RT_ETIMEOUT, "ETIMOUT"}, /**< Operation timed out. */
|
||||
{RT_EFULL , "ERSFULL"}, /**< Resource is full. */
|
||||
{RT_EEMPTY , "ERSEPTY"}, /**< Resource is empty. */
|
||||
{RT_ENOMEM , "ENOMEM "}, /**< Not enough memory. */
|
||||
{RT_ENOSYS , "ENOSYS "}, /**< Function not implemented. */
|
||||
{RT_EBUSY , "EBUSY "}, /**< Resource is busy. */
|
||||
{RT_EIO , "EIO "}, /**< Input/output error. */
|
||||
{RT_EINTR , "EINTRPT"}, /**< Interrupted system call. */
|
||||
{RT_EINVAL , "EINVAL "}, /**< Invalid argument. */
|
||||
{RT_ENOENT , "ENOENT "}, /**< No such file or directory. */
|
||||
{RT_ENOSPC , "ENOSPC "}, /**< No space left on device. */
|
||||
{RT_EPERM , "EPERM "}, /**< Operation not permitted. */
|
||||
{RT_ETRAP , "ETRAP "}, /**< Trap error. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This function return a pointer to a string that contains the
|
||||
* message of error.
|
||||
*
|
||||
* @param error the errorno code
|
||||
* @return a point to error message string
|
||||
*/
|
||||
const char *rt_strerror(rt_err_t error)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (error < 0)
|
||||
error = -error;
|
||||
|
||||
for (i = 0; i < sizeof(rt_errno_strs) / sizeof(rt_errno_strs[0]); i++)
|
||||
{
|
||||
if (rt_errno_strs[i].error == error)
|
||||
return rt_errno_strs[i].str;
|
||||
}
|
||||
|
||||
return "EUNKNOW";
|
||||
}
|
||||
RTM_EXPORT(rt_strerror);
|
||||
|
||||
/**
|
||||
* @brief This function gets the global errno for the current thread.
|
||||
*
|
||||
* @return errno
|
||||
*/
|
||||
rt_err_t rt_get_errno(void)
|
||||
{
|
||||
rt_thread_t tid = RT_NULL;
|
||||
|
||||
if (rt_interrupt_get_nest() != 0)
|
||||
{
|
||||
/* it's in interrupt context */
|
||||
return __rt_errno;
|
||||
}
|
||||
|
||||
tid = rt_thread_self();
|
||||
if (tid == RT_NULL)
|
||||
{
|
||||
return __rt_errno;
|
||||
}
|
||||
|
||||
return tid->error;
|
||||
}
|
||||
RTM_EXPORT(rt_get_errno);
|
||||
|
||||
/**
|
||||
* @brief This function sets the global errno for the current thread.
|
||||
*
|
||||
* @param error is the errno shall be set.
|
||||
*/
|
||||
void rt_set_errno(rt_err_t error)
|
||||
{
|
||||
rt_thread_t tid = RT_NULL;
|
||||
|
||||
if (rt_interrupt_get_nest() != 0)
|
||||
{
|
||||
/* it's in interrupt context */
|
||||
__rt_errno = error;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tid = rt_thread_self();
|
||||
if (tid == RT_NULL)
|
||||
{
|
||||
__rt_errno = error;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tid->error = error;
|
||||
}
|
||||
RTM_EXPORT(rt_set_errno);
|
||||
|
||||
/**
|
||||
* @brief This function returns the address of the current thread errno.
|
||||
*
|
||||
* @return The errno address.
|
||||
*/
|
||||
int *_rt_errno(void)
|
||||
{
|
||||
rt_thread_t tid = RT_NULL;
|
||||
|
||||
if (rt_interrupt_get_nest() != 0)
|
||||
{
|
||||
return (int *)&__rt_errno;
|
||||
}
|
||||
|
||||
tid = rt_thread_self();
|
||||
if (tid != RT_NULL)
|
||||
{
|
||||
return (int *) & (tid->error);
|
||||
}
|
||||
|
||||
return (int *)&__rt_errno;
|
||||
}
|
||||
RTM_EXPORT(_rt_errno);
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-03-10 Meco Man the first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#if defined(RT_KLIBC_USING_LIBC_VSSCANF) || \
|
||||
defined(RT_KLIBC_USING_LIBC_VSNPRINTF)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief This function will fill a formatted string to buffer.
|
||||
*
|
||||
* @param buf is the buffer to save formatted string.
|
||||
*
|
||||
* @param size is the size of buffer.
|
||||
*
|
||||
* @param fmt is the format parameters.
|
||||
*
|
||||
* @return The number of characters actually written to buffer.
|
||||
*/
|
||||
int rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...)
|
||||
{
|
||||
rt_int32_t n = 0;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
n = rt_vsnprintf(buf, size, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return n;
|
||||
}
|
||||
RTM_EXPORT(rt_snprintf);
|
||||
|
||||
/**
|
||||
* @brief This function will fill a formatted string to buffer.
|
||||
*
|
||||
* @param buf is the buffer to save formatted string.
|
||||
*
|
||||
* @param format is the format parameters.
|
||||
*
|
||||
* @param arg_ptr is a list of variable parameters.
|
||||
*
|
||||
* @return The number of characters actually written to buffer.
|
||||
*/
|
||||
int rt_vsprintf(char *buf, const char *format, va_list arg_ptr)
|
||||
{
|
||||
return rt_vsnprintf(buf, (rt_size_t) - 1, format, arg_ptr);
|
||||
}
|
||||
RTM_EXPORT(rt_vsprintf);
|
||||
|
||||
/**
|
||||
* @brief This function will fill a formatted string to buffer
|
||||
*
|
||||
* @param buf the buffer to save formatted string.
|
||||
*
|
||||
* @param format is the format parameters.
|
||||
*
|
||||
* @return The number of characters actually written to buffer.
|
||||
*/
|
||||
int rt_sprintf(char *buf, const char *format, ...)
|
||||
{
|
||||
rt_int32_t n = 0;
|
||||
va_list arg_ptr;
|
||||
|
||||
va_start(arg_ptr, format);
|
||||
n = rt_vsprintf(buf, format, arg_ptr);
|
||||
va_end(arg_ptr);
|
||||
|
||||
return n;
|
||||
}
|
||||
RTM_EXPORT(rt_sprintf);
|
||||
|
||||
#ifdef RT_KLIBC_USING_LIBC_VSNPRINTF
|
||||
int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args)
|
||||
{
|
||||
return vsnprintf(buf, size, fmt, args);
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_LIBC_VSNPRINTF */
|
||||
RTM_EXPORT(rt_vsnprintf);
|
||||
|
||||
#ifdef RT_KLIBC_USING_LIBC_VSSCANF
|
||||
int rt_vsscanf(const char *buffer, const char *format, va_list ap)
|
||||
{
|
||||
return vsscanf(buffer, format, ap);
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_LIBC_VSSCANF */
|
||||
RTM_EXPORT(rt_vsscanf);
|
||||
|
||||
/**
|
||||
* @brief This function parses a formatted string from the input string.
|
||||
*
|
||||
* @param str the input string to be parsed.
|
||||
*
|
||||
* @param format the format string that specifies how to interpret the input.
|
||||
*
|
||||
* @return The number of input items successfully matched and assigned.
|
||||
*/
|
||||
int rt_sscanf(const char *str, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int rv;
|
||||
|
||||
va_start(ap, format);
|
||||
rv = rt_vsscanf(str, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return rv;
|
||||
}
|
||||
RTM_EXPORT(rt_sscanf);
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-03-10 Meco Man the first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
#if defined(RT_KLIBC_USING_LIBC_MEMSET) || \
|
||||
defined(RT_KLIBC_USING_LIBC_MEMCPY) || \
|
||||
defined(RT_KLIBC_USING_LIBC_MEMMOVE) || \
|
||||
defined(RT_KLIBC_USING_LIBC_MEMCMP) || \
|
||||
defined(RT_KLIBC_USING_LIBC_STRSTR) || \
|
||||
defined(RT_KLIBC_USING_LIBC_STRNCPY) || \
|
||||
defined(RT_KLIBC_USING_LIBC_STRCPY) || \
|
||||
defined(RT_KLIBC_USING_LIBC_STRNCMP) || \
|
||||
defined(RT_KLIBC_USING_LIBC_STRCMP) || \
|
||||
defined(RT_KLIBC_USING_LIBC_STRLEN)
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief This function will set the content of memory to specified value.
|
||||
*
|
||||
* @param s is the address of source memory, point to the memory block to be filled.
|
||||
*
|
||||
* @param c is the value to be set. The value is passed in int form, but the function
|
||||
* uses the unsigned character form of the value when filling the memory block.
|
||||
*
|
||||
* @param count number of bytes to be set.
|
||||
*
|
||||
* @return The address of source memory.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_MEMSET
|
||||
void *rt_memset(void *s, int c, rt_ubase_t count)
|
||||
{
|
||||
#if defined(RT_KLIBC_USING_LIBC_MEMSET)
|
||||
return memset(s, c, count);
|
||||
#elif defined(RT_KLIBC_USING_TINY_MEMSET)
|
||||
char *xs = (char *)s;
|
||||
|
||||
while (count--)
|
||||
*xs++ = c;
|
||||
|
||||
return s;
|
||||
#else
|
||||
|
||||
#define LBLOCKSIZE (sizeof(rt_ubase_t))
|
||||
#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1))
|
||||
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
||||
|
||||
unsigned int i = 0;
|
||||
char *m = (char *)s;
|
||||
unsigned long buffer = 0;
|
||||
unsigned long *aligned_addr = RT_NULL;
|
||||
unsigned char d = (unsigned int)c & (unsigned char)(-1); /* To avoid sign extension, copy C to an
|
||||
unsigned variable. (unsigned)((char)(-1))=0xFF for 8bit and =0xFFFF for 16bit: word independent */
|
||||
|
||||
RT_ASSERT(LBLOCKSIZE == 2 || LBLOCKSIZE == 4 || LBLOCKSIZE == 8);
|
||||
|
||||
if (!TOO_SMALL(count) && !UNALIGNED(s))
|
||||
{
|
||||
/* If we get this far, we know that count is large and s is word-aligned. */
|
||||
aligned_addr = (unsigned long *)s;
|
||||
|
||||
/* Store d into each char sized location in buffer so that
|
||||
* we can set large blocks quickly.
|
||||
*/
|
||||
for (i = 0; i < LBLOCKSIZE; i++)
|
||||
{
|
||||
*(((unsigned char *)&buffer)+i) = d;
|
||||
}
|
||||
|
||||
while (count >= LBLOCKSIZE * 4)
|
||||
{
|
||||
*aligned_addr++ = buffer;
|
||||
*aligned_addr++ = buffer;
|
||||
*aligned_addr++ = buffer;
|
||||
*aligned_addr++ = buffer;
|
||||
count -= 4 * LBLOCKSIZE;
|
||||
}
|
||||
|
||||
while (count >= LBLOCKSIZE)
|
||||
{
|
||||
*aligned_addr++ = buffer;
|
||||
count -= LBLOCKSIZE;
|
||||
}
|
||||
|
||||
/* Pick up the remainder with a bytewise loop. */
|
||||
m = (char *)aligned_addr;
|
||||
}
|
||||
|
||||
while (count--)
|
||||
{
|
||||
*m++ = (char)d;
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
#undef LBLOCKSIZE
|
||||
#undef UNALIGNED
|
||||
#undef TOO_SMALL
|
||||
#endif /* RT_KLIBC_USING_LIBC_MEMSET */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_MEMSET */
|
||||
RTM_EXPORT(rt_memset);
|
||||
|
||||
/**
|
||||
* @brief This function will copy memory content from source address to destination address.
|
||||
*
|
||||
* @param dst is the address of destination memory, points to the copied content.
|
||||
*
|
||||
* @param src is the address of source memory, pointing to the data source to be copied.
|
||||
*
|
||||
* @param count is the copied length.
|
||||
*
|
||||
* @return The address of destination memory
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_MEMCPY
|
||||
void *rt_memcpy(void *dst, const void *src, rt_ubase_t count)
|
||||
{
|
||||
#if defined(RT_KLIBC_USING_LIBC_MEMCPY)
|
||||
return memcpy(dst, src, count);
|
||||
#elif defined(RT_KLIBC_USING_TINY_MEMCPY)
|
||||
char *tmp = (char *)dst, *s = (char *)src;
|
||||
rt_ubase_t len = 0;
|
||||
|
||||
if (tmp <= s || tmp > (s + count))
|
||||
{
|
||||
while (count--)
|
||||
*tmp ++ = *s ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (len = count; len > 0; len --)
|
||||
tmp[len - 1] = s[len - 1];
|
||||
}
|
||||
|
||||
return dst;
|
||||
#else
|
||||
|
||||
#define UNALIGNED(X, Y) \
|
||||
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
|
||||
#define BIGBLOCKSIZE (sizeof (long) << 2)
|
||||
#define LITTLEBLOCKSIZE (sizeof (long))
|
||||
#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE)
|
||||
|
||||
char *dst_ptr = (char *)dst;
|
||||
char *src_ptr = (char *)src;
|
||||
long *aligned_dst = RT_NULL;
|
||||
long *aligned_src = RT_NULL;
|
||||
rt_ubase_t len = count;
|
||||
|
||||
/* If the size is small, or either SRC or DST is unaligned,
|
||||
then punt into the byte copy loop. This should be rare. */
|
||||
if (!TOO_SMALL(len) && !UNALIGNED(src_ptr, dst_ptr))
|
||||
{
|
||||
aligned_dst = (long *)dst_ptr;
|
||||
aligned_src = (long *)src_ptr;
|
||||
|
||||
/* Copy 4X long words at a time if possible. */
|
||||
while (len >= BIGBLOCKSIZE)
|
||||
{
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
len -= BIGBLOCKSIZE;
|
||||
}
|
||||
|
||||
/* Copy one long word at a time if possible. */
|
||||
while (len >= LITTLEBLOCKSIZE)
|
||||
{
|
||||
*aligned_dst++ = *aligned_src++;
|
||||
len -= LITTLEBLOCKSIZE;
|
||||
}
|
||||
|
||||
/* Pick up any residual with a byte copier. */
|
||||
dst_ptr = (char *)aligned_dst;
|
||||
src_ptr = (char *)aligned_src;
|
||||
}
|
||||
|
||||
while (len--)
|
||||
*dst_ptr++ = *src_ptr++;
|
||||
|
||||
return dst;
|
||||
#undef UNALIGNED
|
||||
#undef BIGBLOCKSIZE
|
||||
#undef LITTLEBLOCKSIZE
|
||||
#undef TOO_SMALL
|
||||
#endif /* RT_KLIBC_USING_LIBC_MEMCPY */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_MEMCPY */
|
||||
RTM_EXPORT(rt_memcpy);
|
||||
|
||||
/**
|
||||
* @brief This function will move memory content from source address to destination
|
||||
* address. If the destination memory does not overlap with the source memory,
|
||||
* the function is the same as memcpy().
|
||||
*
|
||||
* @param dest is the address of destination memory, points to the copied content.
|
||||
*
|
||||
* @param src is the address of source memory, point to the data source to be copied.
|
||||
*
|
||||
* @param n is the copied length.
|
||||
*
|
||||
* @return The address of destination memory.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_MEMMOVE
|
||||
void *rt_memmove(void *dest, const void *src, rt_size_t n)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_LIBC_MEMMOVE
|
||||
return memmove(dest, src, n);
|
||||
#else
|
||||
char *tmp = (char *)dest, *s = (char *)src;
|
||||
|
||||
if (s < tmp && tmp < s + n)
|
||||
{
|
||||
tmp += n;
|
||||
s += n;
|
||||
|
||||
while (n--)
|
||||
*(--tmp) = *(--s);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (n--)
|
||||
*tmp++ = *s++;
|
||||
}
|
||||
|
||||
return dest;
|
||||
#endif /* RT_KLIBC_USING_LIBC_MEMMOVE */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_MEMMOVE */
|
||||
RTM_EXPORT(rt_memmove);
|
||||
|
||||
/**
|
||||
* @brief This function will compare two areas of memory.
|
||||
*
|
||||
* @param cs is a block of memory.
|
||||
*
|
||||
* @param ct is another block of memory.
|
||||
*
|
||||
* @param count is the size of the area.
|
||||
*
|
||||
* @return Compare the results:
|
||||
* If the result < 0, cs is smaller than ct.
|
||||
* If the result > 0, cs is greater than ct.
|
||||
* If the result = 0, cs is equal to ct.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_MEMCMP
|
||||
rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_size_t count)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_LIBC_MEMCMP
|
||||
return memcmp(cs, ct, count);
|
||||
#else
|
||||
const unsigned char *su1 = RT_NULL, *su2 = RT_NULL;
|
||||
int res = 0;
|
||||
|
||||
for (su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--)
|
||||
if ((res = *su1 - *su2) != 0)
|
||||
break;
|
||||
|
||||
return res;
|
||||
#endif /* RT_KLIBC_USING_LIBC_MEMCMP */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_MEMCMP */
|
||||
RTM_EXPORT(rt_memcmp);
|
||||
|
||||
/**
|
||||
* @brief This function will return the first occurrence of a string, without the
|
||||
* terminator '\0'.
|
||||
*
|
||||
* @param s1 is the source string.
|
||||
*
|
||||
* @param s2 is the find string.
|
||||
*
|
||||
* @return The first occurrence of a s2 in s1, or RT_NULL if no found.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_STRSTR
|
||||
char *rt_strstr(const char *s1, const char *s2)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_LIBC_STRSTR
|
||||
return strstr(s1, s2);
|
||||
#else
|
||||
int l1 = 0, l2 = 0;
|
||||
|
||||
l2 = rt_strlen(s2);
|
||||
if (!l2)
|
||||
{
|
||||
return (char *)s1;
|
||||
}
|
||||
|
||||
l1 = rt_strlen(s1);
|
||||
while (l1 >= l2)
|
||||
{
|
||||
l1 --;
|
||||
if (!rt_memcmp(s1, s2, l2))
|
||||
{
|
||||
return (char *)s1;
|
||||
}
|
||||
|
||||
s1 ++;
|
||||
}
|
||||
|
||||
return RT_NULL;
|
||||
#endif /* RT_KLIBC_USING_LIBC_STRSTR */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_STRSTR */
|
||||
RTM_EXPORT(rt_strstr);
|
||||
|
||||
/**
|
||||
* @brief This function will compare two strings while ignoring differences in case
|
||||
*
|
||||
* @param a is the string to be compared.
|
||||
*
|
||||
* @param b is the string to be compared.
|
||||
*
|
||||
* @return Compare the results:
|
||||
* If the result < 0, a is smaller than a.
|
||||
* If the result > 0, a is greater than a.
|
||||
* If the result = 0, a is equal to a.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_STRCASECMP
|
||||
rt_int32_t rt_strcasecmp(const char *a, const char *b)
|
||||
{
|
||||
int ca = 0, cb = 0;
|
||||
|
||||
do
|
||||
{
|
||||
ca = *a++ & 0xff;
|
||||
cb = *b++ & 0xff;
|
||||
if (ca >= 'A' && ca <= 'Z')
|
||||
ca += 'a' - 'A';
|
||||
if (cb >= 'A' && cb <= 'Z')
|
||||
cb += 'a' - 'A';
|
||||
}
|
||||
while (ca == cb && ca != '\0');
|
||||
|
||||
return ca - cb;
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_STRCASECMP */
|
||||
RTM_EXPORT(rt_strcasecmp);
|
||||
|
||||
/**
|
||||
* @brief This function will copy string no more than n bytes.
|
||||
*
|
||||
* @param dst points to the address used to store the copied content.
|
||||
*
|
||||
* @param src is the string to be copied.
|
||||
*
|
||||
* @param n is the maximum copied length.
|
||||
*
|
||||
* @return The address where the copied content is stored.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_STRNCPY
|
||||
char *rt_strncpy(char *dst, const char *src, rt_size_t n)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_LIBC_STRNCPY
|
||||
return strncpy(dst, src, n);
|
||||
#else
|
||||
if (n != 0)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
|
||||
do
|
||||
{
|
||||
if ((*d++ = *s++) == 0)
|
||||
{
|
||||
/* NUL pad the remaining n-1 bytes */
|
||||
while (--n != 0)
|
||||
{
|
||||
*d++ = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} while (--n != 0);
|
||||
}
|
||||
|
||||
return (dst);
|
||||
#endif /* RT_KLIBC_USING_LIBC_STRNCPY */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_STRNCPY */
|
||||
RTM_EXPORT(rt_strncpy);
|
||||
|
||||
/**
|
||||
* @brief This function will copy string.
|
||||
*
|
||||
* @param dst points to the address used to store the copied content.
|
||||
*
|
||||
* @param src is the string to be copied.
|
||||
*
|
||||
* @return The address where the copied content is stored.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_STRCPY
|
||||
char *rt_strcpy(char *dst, const char *src)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_LIBC_STRCPY
|
||||
return strcpy(dst, src);
|
||||
#else
|
||||
char *dest = dst;
|
||||
|
||||
while (*src != '\0')
|
||||
{
|
||||
*dst = *src;
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
return dest;
|
||||
#endif /* RT_KLIBC_USING_LIBC_STRCPY */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_STRCPY */
|
||||
RTM_EXPORT(rt_strcpy);
|
||||
|
||||
/**
|
||||
* @brief This function will compare two strings with specified maximum length.
|
||||
*
|
||||
* @param cs is the string to be compared.
|
||||
*
|
||||
* @param ct is the string to be compared.
|
||||
*
|
||||
* @param count is the maximum compare length.
|
||||
*
|
||||
* @return Compare the results:
|
||||
* If the result < 0, cs is smaller than ct.
|
||||
* If the result > 0, cs is greater than ct.
|
||||
* If the result = 0, cs is equal to ct.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_STRNCMP
|
||||
rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_size_t count)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_LIBC_STRNCMP
|
||||
return strncmp(cs, ct, count);
|
||||
#else
|
||||
signed char res = 0;
|
||||
|
||||
while (count)
|
||||
{
|
||||
if ((res = *cs - *ct++) != 0 || !*cs++)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
count --;
|
||||
}
|
||||
|
||||
return res;
|
||||
#endif /* RT_KLIBC_USING_LIBC_STRNCMP */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_STRNCMP */
|
||||
RTM_EXPORT(rt_strncmp);
|
||||
|
||||
/**
|
||||
* @brief This function will compare two strings without specified length.
|
||||
*
|
||||
* @param cs is the string to be compared.
|
||||
*
|
||||
* @param ct is the string to be compared.
|
||||
*
|
||||
* @return Compare the results:
|
||||
* If the result < 0, cs is smaller than ct.
|
||||
* If the result > 0, cs is greater than ct.
|
||||
* If the result = 0, cs is equal to ct.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_STRCMP
|
||||
rt_int32_t rt_strcmp(const char *cs, const char *ct)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_LIBC_STRCMP
|
||||
return strcmp(cs, ct);
|
||||
#else
|
||||
while (*cs && *cs == *ct)
|
||||
{
|
||||
cs++;
|
||||
ct++;
|
||||
}
|
||||
|
||||
return (*cs - *ct);
|
||||
#endif /* RT_KLIBC_USING_LIBC_STRCMP */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_STRCMP */
|
||||
RTM_EXPORT(rt_strcmp);
|
||||
|
||||
/**
|
||||
* @brief This function will return the length of a string, which terminate will
|
||||
* null character.
|
||||
*
|
||||
* @param s is the string
|
||||
*
|
||||
* @return The length of string.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_STRLEN
|
||||
rt_size_t rt_strlen(const char *s)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_LIBC_STRLEN
|
||||
return strlen(s);
|
||||
#else
|
||||
const char *sc = RT_NULL;
|
||||
for (sc = s; *sc != '\0'; ++sc);
|
||||
return sc - s;
|
||||
#endif /* RT_KLIBC_USING_LIBC_STRLEN */
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_STRLEN */
|
||||
RTM_EXPORT(rt_strlen);
|
||||
|
||||
/**
|
||||
* @brief The strnlen() function returns the number of characters in the
|
||||
* string pointed to by s, excluding the terminating null byte ('\0'),
|
||||
* but at most maxlen. In doing this, strnlen() looks only at the
|
||||
* first maxlen characters in the string pointed to by s and never
|
||||
* beyond s+maxlen.
|
||||
*
|
||||
* @param s is the string.
|
||||
*
|
||||
* @param maxlen is the max size.
|
||||
*
|
||||
* @return The length of string.
|
||||
*/
|
||||
#ifndef RT_KLIBC_USING_USER_STRNLEN
|
||||
rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen)
|
||||
{
|
||||
const char *sc;
|
||||
for (sc = s; *sc != '\0' && (rt_ubase_t)(sc - s) < maxlen; ++sc);
|
||||
return sc - s;
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_USER_STRNLEN */
|
||||
RTM_EXPORT(rt_strnlen);
|
||||
|
||||
#ifdef RT_USING_HEAP
|
||||
/**
|
||||
* @brief This function will duplicate a string.
|
||||
*
|
||||
* @param s is the string to be duplicated.
|
||||
*
|
||||
* @return The string address of the copy.
|
||||
*/
|
||||
char *rt_strdup(const char *s)
|
||||
{
|
||||
rt_size_t len = rt_strlen(s) + 1;
|
||||
char *tmp = (char *)rt_malloc(len);
|
||||
|
||||
if (!tmp)
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
rt_memcpy(tmp, s, len);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
RTM_EXPORT(rt_strdup);
|
||||
#endif /* RT_USING_HEAP */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,611 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-11-19 Meco Man the first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
#define _ISDIGIT(c) ((unsigned)((c) - '0') < 10)
|
||||
|
||||
/**
|
||||
* @brief This function will duplicate a string.
|
||||
*
|
||||
* @param n is the string to be duplicated.
|
||||
*
|
||||
* @param base is support divide instructions value.
|
||||
*
|
||||
* @return the duplicated string pointer.
|
||||
*/
|
||||
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
rt_inline int divide(unsigned long long *n, int base)
|
||||
#else
|
||||
rt_inline int divide(unsigned long *n, int base)
|
||||
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
|
||||
{
|
||||
int res;
|
||||
|
||||
/* optimized for processor which does not support divide instructions. */
|
||||
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
res = (int)((*n) % base);
|
||||
*n = (long long)((*n) / base);
|
||||
#else
|
||||
res = (int)((*n) % base);
|
||||
*n = (long)((*n) / base);
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
rt_inline int skip_atoi(const char **s)
|
||||
{
|
||||
int i = 0;
|
||||
while (_ISDIGIT(**s))
|
||||
i = i * 10 + *((*s)++) - '0';
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#define ZEROPAD (1 << 0) /* pad with zero */
|
||||
#define SIGN (1 << 1) /* unsigned/signed long */
|
||||
#define PLUS (1 << 2) /* show plus */
|
||||
#define SPACE (1 << 3) /* space if plus */
|
||||
#define LEFT (1 << 4) /* left justified */
|
||||
#define SPECIAL (1 << 5) /* 0x */
|
||||
#define LARGE (1 << 6) /* use 'ABCDEF' instead of 'abcdef' */
|
||||
|
||||
static char *print_number(char *buf,
|
||||
char *end,
|
||||
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
unsigned long long num,
|
||||
#else
|
||||
unsigned long num,
|
||||
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
|
||||
int base,
|
||||
int qualifier,
|
||||
int s,
|
||||
int precision,
|
||||
int type)
|
||||
{
|
||||
char c = 0, sign = 0;
|
||||
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
char tmp[64] = {0};
|
||||
#else
|
||||
char tmp[32] = {0};
|
||||
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
|
||||
int precision_bak = precision;
|
||||
const char *digits = RT_NULL;
|
||||
static const char small_digits[] = "0123456789abcdef";
|
||||
static const char large_digits[] = "0123456789ABCDEF";
|
||||
int i = 0;
|
||||
int size = 0;
|
||||
|
||||
size = s;
|
||||
|
||||
digits = (type & LARGE) ? large_digits : small_digits;
|
||||
if (type & LEFT)
|
||||
{
|
||||
type &= ~ZEROPAD;
|
||||
}
|
||||
|
||||
c = (type & ZEROPAD) ? '0' : ' ';
|
||||
|
||||
/* get sign */
|
||||
sign = 0;
|
||||
if (type & SIGN)
|
||||
{
|
||||
switch (qualifier)
|
||||
{
|
||||
case 'h':
|
||||
if ((rt_int16_t)num < 0)
|
||||
{
|
||||
sign = '-';
|
||||
num = (rt_uint16_t)-num;
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
case 'l':
|
||||
if ((long)num < 0)
|
||||
{
|
||||
sign = '-';
|
||||
num = (unsigned long)-num;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
if ((rt_int32_t)num < 0)
|
||||
{
|
||||
sign = '-';
|
||||
num = (rt_uint32_t)-num;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (sign != '-')
|
||||
{
|
||||
if (type & PLUS)
|
||||
{
|
||||
sign = '+';
|
||||
}
|
||||
else if (type & SPACE)
|
||||
{
|
||||
sign = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type & SPECIAL)
|
||||
{
|
||||
if (base == 2 || base == 16)
|
||||
{
|
||||
size -= 2;
|
||||
}
|
||||
else if (base == 8)
|
||||
{
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
if (num == 0)
|
||||
{
|
||||
tmp[i++] = '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
while (num != 0)
|
||||
tmp[i++] = digits[divide(&num, base)];
|
||||
}
|
||||
|
||||
if (i > precision)
|
||||
{
|
||||
precision = i;
|
||||
}
|
||||
size -= precision;
|
||||
|
||||
if (!(type & (ZEROPAD | LEFT)))
|
||||
{
|
||||
if ((sign) && (size > 0))
|
||||
{
|
||||
size--;
|
||||
}
|
||||
|
||||
while (size-- > 0)
|
||||
{
|
||||
if (buf < end)
|
||||
{
|
||||
*buf = ' ';
|
||||
}
|
||||
|
||||
++ buf;
|
||||
}
|
||||
}
|
||||
|
||||
if (sign)
|
||||
{
|
||||
if (buf < end)
|
||||
{
|
||||
*buf = sign;
|
||||
}
|
||||
-- size;
|
||||
++ buf;
|
||||
}
|
||||
|
||||
if (type & SPECIAL)
|
||||
{
|
||||
if (base == 2)
|
||||
{
|
||||
if (buf < end)
|
||||
*buf = '0';
|
||||
++ buf;
|
||||
if (buf < end)
|
||||
*buf = 'b';
|
||||
++ buf;
|
||||
}
|
||||
else if (base == 8)
|
||||
{
|
||||
if (buf < end)
|
||||
*buf = '0';
|
||||
++ buf;
|
||||
}
|
||||
else if (base == 16)
|
||||
{
|
||||
if (buf < end)
|
||||
{
|
||||
*buf = '0';
|
||||
}
|
||||
|
||||
++ buf;
|
||||
if (buf < end)
|
||||
{
|
||||
*buf = type & LARGE ? 'X' : 'x';
|
||||
}
|
||||
++ buf;
|
||||
}
|
||||
}
|
||||
|
||||
/* no align to the left */
|
||||
if (!(type & LEFT))
|
||||
{
|
||||
while (size-- > 0)
|
||||
{
|
||||
if (buf < end)
|
||||
{
|
||||
*buf = c;
|
||||
}
|
||||
|
||||
++ buf;
|
||||
}
|
||||
}
|
||||
|
||||
while (i < precision--)
|
||||
{
|
||||
if (buf < end)
|
||||
{
|
||||
*buf = '0';
|
||||
}
|
||||
|
||||
++ buf;
|
||||
}
|
||||
|
||||
/* put number in the temporary buffer */
|
||||
while (i-- > 0 && (precision_bak != 0))
|
||||
{
|
||||
if (buf < end)
|
||||
{
|
||||
*buf = tmp[i];
|
||||
}
|
||||
|
||||
++ buf;
|
||||
}
|
||||
|
||||
while (size-- > 0)
|
||||
{
|
||||
if (buf < end)
|
||||
{
|
||||
*buf = ' ';
|
||||
}
|
||||
|
||||
++ buf;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#if (defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */) && (__GNUC__ >= 7)
|
||||
/* Disable "-Wimplicit-fallthrough" below GNUC V7 */
|
||||
#pragma GCC diagnostic push
|
||||
/* ignore warning: this statement may fall through */
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#endif /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) && (__GNUC__ >= 7 */
|
||||
/**
|
||||
* @brief This function will fill a formatted string to buffer.
|
||||
*
|
||||
* @param buf is the buffer to save formatted string.
|
||||
*
|
||||
* @param size is the size of buffer.
|
||||
*
|
||||
* @param fmt is the format parameters.
|
||||
*
|
||||
* @param args is a list of variable parameters.
|
||||
*
|
||||
* @return The number of characters actually written to buffer.
|
||||
*/
|
||||
int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args)
|
||||
{
|
||||
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
unsigned long long num = 0;
|
||||
#else
|
||||
unsigned long num = 0;
|
||||
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
|
||||
int i = 0, len = 0;
|
||||
char *str = RT_NULL, *end = RT_NULL, c = 0;
|
||||
const char *s = RT_NULL;
|
||||
|
||||
rt_uint8_t base = 0; /* the base of number */
|
||||
rt_uint8_t flags = 0; /* flags to print number */
|
||||
rt_uint8_t qualifier = 0; /* 'h', 'l', or 'L' for integer fields */
|
||||
rt_int32_t field_width = 0; /* width of output field */
|
||||
int precision = 0; /* min. # of digits for integers and max for a string */
|
||||
|
||||
str = buf;
|
||||
end = buf + size;
|
||||
|
||||
/* Make sure end is always >= buf */
|
||||
if (end < buf)
|
||||
{
|
||||
end = ((char *) - 1);
|
||||
size = end - buf;
|
||||
}
|
||||
|
||||
for (; *fmt ; ++fmt)
|
||||
{
|
||||
if (*fmt != '%')
|
||||
{
|
||||
if (str < end)
|
||||
{
|
||||
*str = *fmt;
|
||||
}
|
||||
|
||||
++ str;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* process flags */
|
||||
flags = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* skips the first '%' also */
|
||||
++fmt;
|
||||
if (*fmt == '-') flags |= LEFT;
|
||||
else if (*fmt == '+') flags |= PLUS;
|
||||
else if (*fmt == ' ') flags |= SPACE;
|
||||
else if (*fmt == '#') flags |= SPECIAL;
|
||||
else if (*fmt == '0') flags |= ZEROPAD;
|
||||
else break;
|
||||
}
|
||||
|
||||
/* get field width */
|
||||
field_width = -1;
|
||||
if (_ISDIGIT(*fmt))
|
||||
{
|
||||
field_width = skip_atoi(&fmt);
|
||||
}
|
||||
else if (*fmt == '*')
|
||||
{
|
||||
++fmt;
|
||||
/* it's the next argument */
|
||||
field_width = va_arg(args, int);
|
||||
if (field_width < 0)
|
||||
{
|
||||
field_width = -field_width;
|
||||
flags |= LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
/* get the precision */
|
||||
precision = -1;
|
||||
if (*fmt == '.')
|
||||
{
|
||||
++fmt;
|
||||
if (_ISDIGIT(*fmt))
|
||||
{
|
||||
precision = skip_atoi(&fmt);
|
||||
}
|
||||
else if (*fmt == '*')
|
||||
{
|
||||
++fmt;
|
||||
/* it's the next argument */
|
||||
precision = va_arg(args, int);
|
||||
}
|
||||
if (precision < 0)
|
||||
{
|
||||
precision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
qualifier = 0; /* get the conversion qualifier */
|
||||
|
||||
if (*fmt == 'h' || *fmt == 'l' ||
|
||||
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
*fmt == 'L' ||
|
||||
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
|
||||
*fmt == 'z')
|
||||
{
|
||||
qualifier = *fmt;
|
||||
++fmt;
|
||||
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
|
||||
if (qualifier == 'l' && *fmt == 'l')
|
||||
{
|
||||
qualifier = 'L';
|
||||
++fmt;
|
||||
}
|
||||
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
|
||||
if (qualifier == 'h' && *fmt == 'h')
|
||||
{
|
||||
qualifier = 'H';
|
||||
++fmt;
|
||||
}
|
||||
}
|
||||
|
||||
/* the default base */
|
||||
base = 10;
|
||||
|
||||
switch (*fmt)
|
||||
{
|
||||
case 'c':
|
||||
if (!(flags & LEFT))
|
||||
{
|
||||
while (--field_width > 0)
|
||||
{
|
||||
if (str < end) *str = ' ';
|
||||
++ str;
|
||||
}
|
||||
}
|
||||
|
||||
/* get character */
|
||||
c = (rt_uint8_t)va_arg(args, int);
|
||||
if (str < end)
|
||||
{
|
||||
*str = c;
|
||||
}
|
||||
++ str;
|
||||
|
||||
/* put width */
|
||||
while (--field_width > 0)
|
||||
{
|
||||
if (str < end) *str = ' ';
|
||||
++ str;
|
||||
}
|
||||
continue;
|
||||
|
||||
case 's':
|
||||
s = va_arg(args, char *);
|
||||
if (!s)
|
||||
{
|
||||
s = "(null)";
|
||||
}
|
||||
|
||||
for (len = 0; (len != field_width) && (s[len] != '\0'); len++);
|
||||
|
||||
if (precision > 0 && len > precision)
|
||||
{
|
||||
len = precision;
|
||||
}
|
||||
|
||||
if (!(flags & LEFT))
|
||||
{
|
||||
while (len < field_width--)
|
||||
{
|
||||
if (str < end) *str = ' ';
|
||||
++ str;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
if (str < end) *str = *s;
|
||||
++ str;
|
||||
++ s;
|
||||
}
|
||||
|
||||
while (len < field_width--)
|
||||
{
|
||||
if (str < end) *str = ' ';
|
||||
++ str;
|
||||
}
|
||||
continue;
|
||||
|
||||
case 'p':
|
||||
if (field_width == -1)
|
||||
{
|
||||
field_width = sizeof(void *) << 1;
|
||||
field_width += 2; /* `0x` prefix */
|
||||
flags |= SPECIAL;
|
||||
flags |= ZEROPAD;
|
||||
}
|
||||
str = print_number(str, end, (unsigned long)va_arg(args, void *),
|
||||
16, qualifier, field_width, precision, flags);
|
||||
continue;
|
||||
|
||||
case '%':
|
||||
if (str < end)
|
||||
{
|
||||
*str = '%';
|
||||
}
|
||||
++ str;
|
||||
continue;
|
||||
|
||||
/* integer number formats - set up the flags and "break" */
|
||||
case 'b':
|
||||
base = 2;
|
||||
break;
|
||||
case 'o':
|
||||
base = 8;
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
flags |= LARGE;
|
||||
case 'x':
|
||||
base = 16;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'i':
|
||||
flags |= SIGN;
|
||||
case 'u':
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'G':
|
||||
case 'g':
|
||||
case 'f':
|
||||
case 'F':
|
||||
va_arg(args, double);
|
||||
default:
|
||||
if (str < end)
|
||||
{
|
||||
*str = '%';
|
||||
}
|
||||
++ str;
|
||||
|
||||
if (*fmt)
|
||||
{
|
||||
if (str < end)
|
||||
{
|
||||
*str = *fmt;
|
||||
}
|
||||
++ str;
|
||||
}
|
||||
else
|
||||
{
|
||||
-- fmt;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (qualifier == 'L')
|
||||
{
|
||||
num = va_arg(args, unsigned long long);
|
||||
}
|
||||
else if (qualifier == 'l')
|
||||
{
|
||||
num = va_arg(args, unsigned long);
|
||||
}
|
||||
else if (qualifier == 'H')
|
||||
{
|
||||
num = (rt_int8_t)va_arg(args, rt_int32_t);
|
||||
if (flags & SIGN)
|
||||
{
|
||||
num = (rt_int8_t)num;
|
||||
}
|
||||
}
|
||||
else if (qualifier == 'h')
|
||||
{
|
||||
num = (rt_uint16_t)va_arg(args, rt_int32_t);
|
||||
if (flags & SIGN)
|
||||
{
|
||||
num = (rt_int16_t)num;
|
||||
}
|
||||
}
|
||||
else if (qualifier == 'z')
|
||||
{
|
||||
num = va_arg(args, rt_size_t);
|
||||
if (flags & SIGN)
|
||||
{
|
||||
num = (rt_ssize_t)num;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
num = (rt_uint32_t)va_arg(args, unsigned long);
|
||||
}
|
||||
str = print_number(str, end, num, base, qualifier, field_width, precision, flags);
|
||||
}
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
if (str < end)
|
||||
{
|
||||
*str = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
end[-1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* the trailing null byte doesn't count towards the total
|
||||
* ++str;
|
||||
*/
|
||||
return str - buf;
|
||||
}
|
||||
#if (defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */) && (__GNUC__ >= 7)
|
||||
#pragma GCC diagnostic pop /* ignored "-Wimplicit-fallthrough" */
|
||||
#endif /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) && (__GNUC__ >= 7 */
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2025, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-11-24 Meco Man port to klibc
|
||||
* 2025-01-04 Meco Man using Phoenix version
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2017, 2022-2023 Phoenix Systems
|
||||
* Author: Adrian Kepka, Gerard Swiderski
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <stdlib.h> /* for strtod */
|
||||
#include <ctype.h> /* for isspace */
|
||||
#include <stdarg.h> /* for va_list */
|
||||
|
||||
#define FORMAT_NIL_STR "(nil)"
|
||||
#define FORMAT_NIL_STR_LEN (sizeof(FORMAT_NIL_STR) - 1)
|
||||
|
||||
#define LONG 0x01 /* l: long or double */
|
||||
#define LONGDOUBLE 0x02 /* L: long double */
|
||||
#define SHORT 0x04 /* h: short */
|
||||
#define SUPPRESS 0x08 /* *: suppress assignment */
|
||||
#define POINTER 0x10 /* p: void * (as hex) */
|
||||
#define NOSKIP 0x20 /* [ or c: do not skip blanks */
|
||||
#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
|
||||
#define PTRDIFF 0x800 /* t: ptrdiff_t */
|
||||
#define SHORTSHORT 0x4000 /* hh: char */
|
||||
#define UNSIGNED 0x8000 /* %[oupxX] conversions */
|
||||
|
||||
#define SIGNOK 0x40 /* +/- is (still) legal */
|
||||
#define NDIGITS 0x80 /* no digits detected */
|
||||
#define PFXOK 0x100 /* 0x prefix is (still) legal */
|
||||
#define NZDIGITS 0x200 /* no zero digits detected */
|
||||
|
||||
#define CT_CHAR 0 /* %c conversion */
|
||||
#define CT_CCL 1 /* %[...] conversion */
|
||||
#define CT_STRING 2 /* %s conversion */
|
||||
#define CT_INT 3 /* %[dioupxX] conversion */
|
||||
#define CT_FLOAT 4 /* %[aefgAEFG] conversion */
|
||||
#define CT_NONE 5 /* No conversion (ex. %n) */
|
||||
|
||||
static const unsigned char *__sccl(char *tab, const unsigned char *fmt)
|
||||
{
|
||||
int c, n, v;
|
||||
|
||||
c = *fmt++;
|
||||
if (c == '^') {
|
||||
v = 1;
|
||||
c = *fmt++;
|
||||
}
|
||||
else {
|
||||
v = 0;
|
||||
}
|
||||
|
||||
rt_memset(tab, (uint8_t)v, 256);
|
||||
|
||||
if (c == 0) {
|
||||
return (fmt - 1);
|
||||
}
|
||||
|
||||
v = 1 - v;
|
||||
tab[c] = v;
|
||||
for (;;) {
|
||||
n = *fmt++;
|
||||
switch (n) {
|
||||
|
||||
case 0:
|
||||
return (fmt - 1);
|
||||
|
||||
case '-':
|
||||
n = *fmt;
|
||||
if ((n == ']') || (n < c)) {
|
||||
c = '-';
|
||||
tab[c] = v;
|
||||
break;
|
||||
}
|
||||
fmt++;
|
||||
|
||||
do {
|
||||
tab[++c] = v;
|
||||
} while (c < n);
|
||||
c = n;
|
||||
break;
|
||||
|
||||
case ']':
|
||||
return (fmt);
|
||||
|
||||
default:
|
||||
c = n;
|
||||
tab[c] = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int scanf_parse(char *ccltab, const char *inp, int *inr, char const *fmt0, va_list ap)
|
||||
{
|
||||
const unsigned char *fmt = (const unsigned char *)fmt0;
|
||||
int c, n, flags, nassigned, nconversions, nread, base;
|
||||
rt_size_t width;
|
||||
char *p, *p0;
|
||||
char buf[32];
|
||||
|
||||
static const short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
|
||||
|
||||
*inr = rt_strlen(inp);
|
||||
|
||||
nassigned = 0;
|
||||
nconversions = 0;
|
||||
nread = 0;
|
||||
base = 0;
|
||||
for (;;) {
|
||||
int convType = CT_NONE;
|
||||
c = *fmt++;
|
||||
if (c == '\0') {
|
||||
return (nassigned);
|
||||
}
|
||||
|
||||
if (isspace(c) != 0) {
|
||||
while ((*inr > 0) && (isspace((int)*inp) != 0)) {
|
||||
nread++;
|
||||
(*inr)--;
|
||||
inp++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c != '%') {
|
||||
if (*inr <= 0) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
|
||||
if (*inp != c) {
|
||||
return nassigned;
|
||||
}
|
||||
|
||||
nread++;
|
||||
(*inr)--;
|
||||
inp++;
|
||||
continue;
|
||||
}
|
||||
|
||||
width = 0;
|
||||
flags = 0;
|
||||
for (;;) {
|
||||
c = *fmt++;
|
||||
if (c == '\0') {
|
||||
return nassigned;
|
||||
}
|
||||
|
||||
if (c == '%') {
|
||||
if (*inr <= 0) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
|
||||
if (*inp != c) {
|
||||
return nassigned;
|
||||
}
|
||||
|
||||
nread++;
|
||||
(*inr)--;
|
||||
inp++;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '*':
|
||||
flags |= SUPPRESS;
|
||||
continue;
|
||||
|
||||
case 'l':
|
||||
if ((flags & LONG) != 0) {
|
||||
flags &= ~LONG;
|
||||
flags |= LONGLONG;
|
||||
}
|
||||
else {
|
||||
flags |= LONG;
|
||||
}
|
||||
continue;
|
||||
|
||||
case 'L':
|
||||
flags |= LONGDOUBLE;
|
||||
continue;
|
||||
|
||||
case 'q':
|
||||
case 'j':
|
||||
flags |= LONGLONG;
|
||||
continue;
|
||||
|
||||
case 't':
|
||||
flags |= PTRDIFF;
|
||||
continue;
|
||||
|
||||
case 'z':
|
||||
if (sizeof(rt_size_t) == sizeof(uint64_t)) {
|
||||
flags |= LONGLONG;
|
||||
}
|
||||
continue;
|
||||
|
||||
case 'h':
|
||||
if ((flags & SHORT) != 0) {
|
||||
flags &= ~SHORT;
|
||||
flags |= SHORTSHORT;
|
||||
}
|
||||
else {
|
||||
flags |= SHORT;
|
||||
}
|
||||
continue;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
width = width * 10 + c - '0';
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* conversions */
|
||||
switch (c) {
|
||||
case 'd':
|
||||
convType = CT_INT;
|
||||
base = 10;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
convType = CT_INT;
|
||||
base = 0;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
convType = CT_INT;
|
||||
flags |= UNSIGNED;
|
||||
base = 8;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
convType = CT_INT;
|
||||
flags |= UNSIGNED;
|
||||
base = 10;
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
case 'x':
|
||||
flags |= PFXOK; /* enable 0x prefixing */
|
||||
convType = CT_INT;
|
||||
flags |= UNSIGNED;
|
||||
base = 16;
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'E':
|
||||
case 'F':
|
||||
case 'G':
|
||||
case 'a':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'g':
|
||||
convType = CT_FLOAT;
|
||||
break;
|
||||
|
||||
|
||||
case 's':
|
||||
convType = CT_STRING;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
fmt = __sccl(ccltab, fmt);
|
||||
flags |= NOSKIP;
|
||||
convType = CT_CCL;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
flags |= NOSKIP;
|
||||
convType = CT_CHAR;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
flags |= POINTER | PFXOK | UNSIGNED;
|
||||
convType = CT_INT;
|
||||
base = 16;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
nconversions++;
|
||||
if ((flags & SUPPRESS) != 0) {
|
||||
break;
|
||||
}
|
||||
if ((flags & SHORTSHORT) != 0) {
|
||||
*va_arg(ap, char *) = nread;
|
||||
}
|
||||
else if ((flags & SHORT) != 0) {
|
||||
*va_arg(ap, short *) = nread;
|
||||
}
|
||||
else if ((flags & LONG) != 0) {
|
||||
*va_arg(ap, long *) = nread;
|
||||
}
|
||||
else if ((flags & LONGLONG) != 0) {
|
||||
*va_arg(ap, long long *) = nread;
|
||||
}
|
||||
else if ((flags & PTRDIFF) != 0) {
|
||||
*va_arg(ap, ptrdiff_t *) = nread;
|
||||
}
|
||||
else {
|
||||
*va_arg(ap, int *) = nread;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Character not a conversion specifier; end parsing */
|
||||
return nassigned;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (convType == CT_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*inr <= 0) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
|
||||
if ((flags & NOSKIP) == 0) {
|
||||
while (isspace((int)*inp) != 0) {
|
||||
nread++;
|
||||
if (--(*inr) > 0) {
|
||||
inp++;
|
||||
}
|
||||
else {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do the conversion */
|
||||
switch (convType) {
|
||||
case CT_CHAR:
|
||||
if (width == 0) {
|
||||
width = 1;
|
||||
}
|
||||
|
||||
if (*inr <= 0) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
|
||||
if (width > *inr) {
|
||||
width = *inr;
|
||||
}
|
||||
|
||||
if ((flags & SUPPRESS) == 0) {
|
||||
rt_memcpy(va_arg(ap, char *), inp, width);
|
||||
nassigned++;
|
||||
}
|
||||
|
||||
*inr -= width;
|
||||
inp += width;
|
||||
nread += width;
|
||||
nconversions++;
|
||||
break;
|
||||
|
||||
case CT_CCL:
|
||||
if (width == 0) {
|
||||
width = (rt_size_t)~0;
|
||||
}
|
||||
if ((flags & SUPPRESS) != 0) {
|
||||
n = 0;
|
||||
while (ccltab[(unsigned char)*inp] != 0) {
|
||||
n++;
|
||||
(*inr)--;
|
||||
inp++;
|
||||
if (--width == 0) {
|
||||
break;
|
||||
}
|
||||
if (*inr <= 0) {
|
||||
if (n == 0) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n == 0) {
|
||||
return nassigned;
|
||||
}
|
||||
}
|
||||
else {
|
||||
p0 = p = va_arg(ap, char *);
|
||||
while (ccltab[(unsigned char)*inp] != 0) {
|
||||
(*inr)--;
|
||||
*p++ = *inp++;
|
||||
if (--width == 0) {
|
||||
break;
|
||||
}
|
||||
if (*inr <= 0) {
|
||||
if (p == p0) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
n = p - p0;
|
||||
if (n == 0) {
|
||||
return nassigned;
|
||||
}
|
||||
*p = 0;
|
||||
nassigned++;
|
||||
}
|
||||
nread += n;
|
||||
nconversions++;
|
||||
break;
|
||||
|
||||
case CT_STRING:
|
||||
if (width == 0) {
|
||||
width = (rt_size_t)~0;
|
||||
}
|
||||
if ((flags & SUPPRESS) != 0) {
|
||||
while (isspace((int)*inp) == 0) {
|
||||
nread++;
|
||||
(*inr)--;
|
||||
inp++;
|
||||
if (--width == 0) {
|
||||
break;
|
||||
}
|
||||
if (*inr <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
p0 = p = va_arg(ap, char *);
|
||||
while (isspace((int)*inp) == 0) {
|
||||
(*inr)--;
|
||||
*p++ = *inp++;
|
||||
if (--width == 0) {
|
||||
break;
|
||||
}
|
||||
if (*inr <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
nread += p - p0;
|
||||
nassigned++;
|
||||
}
|
||||
nconversions++;
|
||||
continue;
|
||||
|
||||
case CT_INT:
|
||||
if (((flags & POINTER) != 0) && ((*inr) >= FORMAT_NIL_STR_LEN) && (rt_strncmp(FORMAT_NIL_STR, inp, FORMAT_NIL_STR_LEN) == 0)) {
|
||||
*va_arg(ap, void **) = RT_NULL;
|
||||
nassigned++;
|
||||
nconversions++;
|
||||
nread += FORMAT_NIL_STR_LEN;
|
||||
inp += FORMAT_NIL_STR_LEN;
|
||||
(*inr) -= FORMAT_NIL_STR_LEN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (--width > (sizeof(buf) - 2)) {
|
||||
width = sizeof(buf) - 2;
|
||||
}
|
||||
width++;
|
||||
|
||||
if ((flags & SUPPRESS) != 0) {
|
||||
width = ~0;
|
||||
}
|
||||
|
||||
flags |= SIGNOK | NDIGITS | NZDIGITS;
|
||||
for (p = buf; width; width--) {
|
||||
int ok = 0;
|
||||
c = *inp;
|
||||
switch (c) {
|
||||
case '0':
|
||||
if (base == 0) {
|
||||
base = 8;
|
||||
flags |= PFXOK;
|
||||
}
|
||||
if ((flags & NZDIGITS) != 0) {
|
||||
flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
|
||||
}
|
||||
else {
|
||||
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
||||
}
|
||||
ok = 1;
|
||||
break;
|
||||
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
base = basefix[base];
|
||||
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
||||
ok = 1;
|
||||
break;
|
||||
|
||||
case '8':
|
||||
case '9':
|
||||
base = basefix[base];
|
||||
if (base <= 8) {
|
||||
break; /* not legal here */
|
||||
}
|
||||
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
||||
ok = 1;
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
if (base <= 10) {
|
||||
break;
|
||||
}
|
||||
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
||||
ok = 1;
|
||||
break;
|
||||
|
||||
case '+':
|
||||
case '-':
|
||||
if ((flags & SIGNOK) != 0) {
|
||||
flags &= ~SIGNOK;
|
||||
ok = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
case 'X':
|
||||
if (((flags & PFXOK) != 0) && (p == buf + 1)) {
|
||||
base = 16; /* if %i */
|
||||
flags &= ~PFXOK;
|
||||
ok = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!ok)
|
||||
break;
|
||||
|
||||
if ((flags & SUPPRESS) == 0) {
|
||||
*p++ = c;
|
||||
}
|
||||
if (--(*inr) > 0) {
|
||||
inp++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((flags & NDIGITS) != 0) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
|
||||
c = ((unsigned char *)p)[-1];
|
||||
if ((c == 'x') || (c == 'X')) {
|
||||
--p;
|
||||
inp--;
|
||||
(*inr)++;
|
||||
}
|
||||
|
||||
if ((flags & SUPPRESS) == 0) {
|
||||
uint64_t res;
|
||||
|
||||
*p = 0;
|
||||
if ((flags & UNSIGNED) == 0) {
|
||||
res = strtoll(buf, (char **)RT_NULL, base);
|
||||
}
|
||||
else {
|
||||
res = strtoull(buf, (char **)RT_NULL, base);
|
||||
}
|
||||
if ((flags & POINTER) != 0) {
|
||||
*va_arg(ap, void **) = (void *)(unsigned long)res;
|
||||
}
|
||||
else if ((flags & SHORTSHORT) != 0) {
|
||||
*va_arg(ap, char *) = res;
|
||||
}
|
||||
else if ((flags & SHORT) != 0) {
|
||||
*va_arg(ap, short *) = res;
|
||||
}
|
||||
else if ((flags & LONG) != 0) {
|
||||
*va_arg(ap, long *) = res;
|
||||
}
|
||||
else if ((flags & LONGLONG) != 0) {
|
||||
*va_arg(ap, long long *) = res;
|
||||
}
|
||||
else if ((flags & PTRDIFF) != 0) {
|
||||
*va_arg(ap, ptrdiff_t *) = res;
|
||||
}
|
||||
else {
|
||||
*va_arg(ap, int *) = res;
|
||||
}
|
||||
nassigned++;
|
||||
}
|
||||
|
||||
nread += p - buf;
|
||||
nconversions++;
|
||||
break;
|
||||
|
||||
case CT_FLOAT: {
|
||||
union {
|
||||
float f;
|
||||
double d;
|
||||
long double ld;
|
||||
} res;
|
||||
|
||||
const char *srcbuf = inp;
|
||||
if ((width != 0) && (width < *inr)) {
|
||||
/* TODO: handle larger widths */
|
||||
if (width > (sizeof(buf) - 1)) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
|
||||
rt_memcpy(buf, inp, width);
|
||||
buf[width] = '\0';
|
||||
srcbuf = buf;
|
||||
}
|
||||
|
||||
int is_zero;
|
||||
if ((flags & LONGDOUBLE) != 0) {
|
||||
res.ld = strtold(srcbuf, &p);
|
||||
is_zero = res.ld == 0;
|
||||
}
|
||||
else if ((flags & LONG) != 0) {
|
||||
res.d = strtod(srcbuf, &p);
|
||||
is_zero = res.d == 0;
|
||||
}
|
||||
else {
|
||||
res.f = strtof(srcbuf, &p);
|
||||
is_zero = res.f == 0;
|
||||
}
|
||||
|
||||
if (is_zero && (srcbuf == p)) {
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
}
|
||||
|
||||
int consumed = p - srcbuf;
|
||||
*inr -= consumed;
|
||||
inp += consumed;
|
||||
nread += consumed;
|
||||
nconversions++;
|
||||
if ((flags & SUPPRESS) == 0) {
|
||||
if ((flags & LONGDOUBLE) != 0) {
|
||||
*va_arg(ap, long double *) = res.ld;
|
||||
}
|
||||
else if ((flags & LONG) != 0) {
|
||||
*va_arg(ap, double *) = res.d;
|
||||
}
|
||||
else {
|
||||
*va_arg(ap, float *) = res.f;
|
||||
}
|
||||
|
||||
nassigned++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* never reached */
|
||||
}
|
||||
|
||||
int rt_vsscanf(const char *str, const char *format, va_list ap)
|
||||
{
|
||||
int ret, nremain;
|
||||
char *ccltab = rt_malloc(256);
|
||||
|
||||
if (ccltab == RT_NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = scanf_parse(ccltab, str, &nremain, format, ap);
|
||||
rt_free(ccltab);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
from building import *
|
||||
|
||||
src = []
|
||||
|
||||
if GetDepend('RT_UTEST_USING_ALL_CASES') or GetDepend('RT_UTEST_TC_USING_KLIBC'):
|
||||
src += Glob('TC_*.c')
|
||||
|
||||
group = DefineGroup('utestcases', src, depend = [''])
|
||||
|
||||
Return('group')
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2020-05-06 Phillip Johnston the first version
|
||||
* 2024-12-24 Meco Man port to utest
|
||||
*/
|
||||
|
||||
#include <rtklibc.h>
|
||||
#include <utest.h>
|
||||
|
||||
static void TC_rt_memcmp_str(void)
|
||||
{
|
||||
const char* s = "abc 123";
|
||||
|
||||
uassert_int_equal(rt_memcmp("abc", "abc", 4), 0);
|
||||
uassert_int_equal(rt_memcmp(s, "abc", 3), 0);
|
||||
uassert_int_equal(rt_memcmp("abc", s, 3), 0);
|
||||
|
||||
/* The following tests intentionally use a length > 3 */
|
||||
/* To test what rt_memcmp does in such a situation */
|
||||
uassert_value_greater(rt_memcmp(s, "abc", 6), 0);
|
||||
uassert_value_less(rt_memcmp("abc", s, 6), 0);
|
||||
}
|
||||
|
||||
static void TC_rt_memcmp_int_array(void)
|
||||
{
|
||||
int arr1[] = {1, 2, 3, 4, 5};
|
||||
int arr2[] = {1, 2, 3, 4, 5};
|
||||
int arr3[] = {1, 2, 3, 4, 6};
|
||||
|
||||
uassert_int_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
|
||||
uassert_value_less(rt_memcmp(arr1, arr3, sizeof(arr1)), 0);
|
||||
uassert_value_greater(rt_memcmp(arr3, arr1, sizeof(arr1)), 0);
|
||||
}
|
||||
|
||||
static void TC_rt_memcmp_float_array(void)
|
||||
{
|
||||
float arr1[] = {1.0f, 2.0f, 3.0f};
|
||||
float arr2[] = {1.0f, 2.0f, 3.0f};
|
||||
float arr3[] = {1.0f, 2.0f, 3.1f};
|
||||
|
||||
uassert_int_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
|
||||
uassert_value_less(rt_memcmp(arr1, arr3, sizeof(arr1)), 0);
|
||||
uassert_value_greater(rt_memcmp(arr3, arr1, sizeof(arr1)), 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
float value;
|
||||
} Item;
|
||||
|
||||
static void TC_rt_memcmp_struct_array(void)
|
||||
{
|
||||
Item arr1[] = {{1, 1.0f}, {2, 2.0f}};
|
||||
Item arr2[] = {{1, 1.0f}, {2, 2.0f}};
|
||||
Item arr3[] = {{1, 1.0f}, {2, 2.1f}};
|
||||
|
||||
uassert_int_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
|
||||
uassert_value_less(rt_memcmp(arr1, arr3, sizeof(arr1)), 0);
|
||||
uassert_value_greater(rt_memcmp(arr3, arr1, sizeof(arr1)), 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
float value;
|
||||
char name[10];
|
||||
} MixedItem;
|
||||
|
||||
static void TC_rt_memcmp_mixed_array(void)
|
||||
{
|
||||
MixedItem arr1[] = {{1, 1.0f, "item1"}, {2, 2.0f, "item2"}};
|
||||
MixedItem arr2[] = {{1, 1.0f, "item1"}, {2, 2.0f, "item2"}};
|
||||
MixedItem arr3[] = {{1, 1.0f, "item1"}, {2, 2.1f, "item2"}};
|
||||
|
||||
uassert_int_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
|
||||
uassert_value_less(rt_memcmp(arr1, arr3, sizeof(arr1)), 0);
|
||||
uassert_value_greater(rt_memcmp(arr3, arr1, sizeof(arr1)), 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
float score;
|
||||
} Student;
|
||||
|
||||
typedef struct {
|
||||
Student students[3];
|
||||
char className[10];
|
||||
} Class;
|
||||
|
||||
static void TC_rt_memcmp_nested_struct_array(void)
|
||||
{
|
||||
Class class1 = {
|
||||
.students = {{1, 90.5}, {2, 85.0}, {3, 92.0}},
|
||||
.className = "ClassA"
|
||||
};
|
||||
|
||||
Class class2 = {
|
||||
.students = {{1, 90.5}, {2, 85.0}, {3, 92.0}},
|
||||
.className = "ClassA"
|
||||
};
|
||||
|
||||
Class class3 = {
|
||||
.students = {{1, 90.5}, {2, 85.1}, {3, 92.0}},
|
||||
.className = "ClassA"
|
||||
};
|
||||
|
||||
uassert_int_equal(rt_memcmp(&class1, &class2, sizeof(Class)), 0);
|
||||
uassert_int_not_equal(rt_memcmp(&class1, &class3, sizeof(Class)), 0);
|
||||
}
|
||||
|
||||
static void TC_rt_memcmp_partial_match(void)
|
||||
{
|
||||
char arr1[] = "abcdefghijklmnopqrstuvwxyz";
|
||||
char arr2[] = "abcdefghijklmxyznopqrstuvw";
|
||||
|
||||
uassert_int_equal(rt_memcmp(arr1, arr2, 13), 0);
|
||||
uassert_int_not_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
|
||||
}
|
||||
|
||||
#define LARGE_ARRAY_SIZE 500
|
||||
|
||||
static void TC_rt_memcmp_large_array(void)
|
||||
{
|
||||
int *arr1 = rt_calloc(LARGE_ARRAY_SIZE, sizeof(int));
|
||||
int *arr2 = rt_calloc(LARGE_ARRAY_SIZE, sizeof(int));
|
||||
|
||||
uassert_not_null(arr1);
|
||||
uassert_not_null(arr2);
|
||||
|
||||
for (int i = 0; i < LARGE_ARRAY_SIZE; i++) {
|
||||
arr1[i] = i;
|
||||
arr2[i] = i;
|
||||
}
|
||||
|
||||
uassert_int_equal(rt_memcmp(arr1, arr2, LARGE_ARRAY_SIZE * sizeof(int)), 0);
|
||||
arr2[LARGE_ARRAY_SIZE - 1] = LARGE_ARRAY_SIZE;
|
||||
|
||||
uassert_value_less(rt_memcmp(arr1, arr2, LARGE_ARRAY_SIZE * sizeof(int)), 0);
|
||||
uassert_value_greater(rt_memcmp(arr2, arr1, LARGE_ARRAY_SIZE * sizeof(int)), 0);
|
||||
|
||||
rt_free(arr1);
|
||||
rt_free(arr2);
|
||||
}
|
||||
|
||||
static void utest_do_tc(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(TC_rt_memcmp_str);
|
||||
UTEST_UNIT_RUN(TC_rt_memcmp_int_array);
|
||||
UTEST_UNIT_RUN(TC_rt_memcmp_float_array);
|
||||
UTEST_UNIT_RUN(TC_rt_memcmp_struct_array);
|
||||
UTEST_UNIT_RUN(TC_rt_memcmp_mixed_array);
|
||||
UTEST_UNIT_RUN(TC_rt_memcmp_nested_struct_array);
|
||||
UTEST_UNIT_RUN(TC_rt_memcmp_partial_match);
|
||||
UTEST_UNIT_RUN(TC_rt_memcmp_large_array);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memcmp", RT_NULL, RT_NULL, 1000);
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2020-05-06 Phillip Johnston the first version
|
||||
* 2024-12-24 Meco Man port to utest
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <utest.h>
|
||||
|
||||
#define N 80 /**< Define the constant N for buffer size as 80 */
|
||||
#define TEST_BUF_SIZE 512 /**< Define the constant TEST_BUF_SIZE as 512 */
|
||||
static char *buf; /**< Define a static buffer of 512 bytes, initialized to 0 */
|
||||
|
||||
static rt_err_t utest_tc_init(void)
|
||||
{
|
||||
buf = rt_malloc(TEST_BUF_SIZE * sizeof(char)); /**< Allocate memory for the buffer */
|
||||
uassert_not_null(buf);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t utest_tc_cleanup(void)
|
||||
{
|
||||
rt_free(buf);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test memory copy with alignment.
|
||||
* @param dalign The alignment offset for the destination buffer.
|
||||
* @param salign The alignment offset for the source buffer.
|
||||
* @param len The length of data to copy.
|
||||
*/
|
||||
static void test_align(unsigned dalign, unsigned salign, size_t len)
|
||||
{
|
||||
char *src = (char *)RT_ALIGN((rt_ubase_t)buf, 64); /**< Source buffer starting address, 64-byte aligned */
|
||||
char *dst = (char *)RT_ALIGN(((rt_ubase_t)buf + 128), 64); /**< Destination buffer starting address, 64-byte aligned from buf+128 */
|
||||
char *want = (char *)RT_ALIGN(((rt_ubase_t)buf + 256), 64); /**< Expected result buffer starting address, 64-byte aligned from buf+256 */
|
||||
char *p; /**< Pointer to receive the return value of rt_memcpy */
|
||||
unsigned i;
|
||||
|
||||
/** Assert that the source alignment offset plus length does not exceed N */
|
||||
uassert_false(salign + len > N);
|
||||
/** Assert that the destination alignment offset plus length does not exceed N */
|
||||
uassert_false(dalign + len > N);
|
||||
|
||||
/** Initialize all buffers with '#' or ' ' */
|
||||
for(i = 0; i < N; i++)
|
||||
{
|
||||
src[i] = '#';
|
||||
dst[i] = want[i] = ' ';
|
||||
}
|
||||
|
||||
/** Set data in the specified alignment offsets of the source and expected result buffers */
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
src[salign + i] = want[dalign + i] = (char)('0' + i);
|
||||
}
|
||||
|
||||
/** Call rt_memcpy to copy data */
|
||||
p = rt_memcpy(dst + dalign, src + salign, len);
|
||||
|
||||
/** Assert that the return value of rt_memcpy is the pointer to the start of the copied data in the destination buffer */
|
||||
uassert_ptr_equal(p, dst + dalign);
|
||||
|
||||
/** Assert that the content of the destination buffer matches the expected result buffer */
|
||||
for(i = 0; i < N; i++)
|
||||
{
|
||||
uassert_int_equal(dst[i], want[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case to iterate over all possible alignment offsets and length combinations.
|
||||
*/
|
||||
static void TC_rt_memcpy_align(void)
|
||||
{
|
||||
for(unsigned i = 0; i < 16; i++) /**< Iterate over source alignment offsets from 0 to 15 */
|
||||
{
|
||||
for(unsigned j = 0; j < 16; j++) /**< Iterate over destination alignment offsets from 0 to 15 */
|
||||
{
|
||||
for(size_t k = 0; k < 64; k++) /**< Iterate over data lengths from 0 to 63 */
|
||||
{
|
||||
test_align(i, j, k); /**< Call the test_align function */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void TC_rt_memcpy_str(void)
|
||||
{
|
||||
const char src[] = "Hello, memcpy!";
|
||||
char dest[20] = {0};
|
||||
rt_memcpy(dest, src, sizeof(src));
|
||||
uassert_true(rt_strcmp(src, dest) == 0);
|
||||
}
|
||||
|
||||
static void utest_do_tc(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(TC_rt_memcpy_str);
|
||||
UTEST_UNIT_RUN(TC_rt_memcpy_align);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memcpy", utest_tc_init, utest_tc_cleanup, 1000);
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-12-25 Meco Man first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <utest.h>
|
||||
|
||||
/* Basic move with no overlap */
|
||||
static void TC_rt_memmove_basic(void)
|
||||
{
|
||||
char src[] = "Hello";
|
||||
char dest[10] = {0};
|
||||
rt_memmove(dest, src, rt_strlen(src) + 1);
|
||||
uassert_str_equal(dest, "Hello");
|
||||
}
|
||||
|
||||
/* Move with overlap (src before dest) */
|
||||
static void TC_rt_memmove_overlap_src_before(void)
|
||||
{
|
||||
char buffer[] = "1234567890";
|
||||
rt_memmove(&buffer[3], buffer, 5);
|
||||
uassert_str_equal(buffer, "1231234590");
|
||||
}
|
||||
|
||||
/* Move with overlap (src after dest) */
|
||||
static void TC_rt_memmove_overlap_src_after(void)
|
||||
{
|
||||
char buffer[] = "1234567890";
|
||||
rt_memmove(&buffer[2], &buffer[5], 5);
|
||||
uassert_str_equal(buffer, "1267890890");
|
||||
}
|
||||
|
||||
/* Move with zero length */
|
||||
static void TC_rt_memmove_zero_length(void)
|
||||
{
|
||||
char src[] = "Hello";
|
||||
char dest[10] = "World";
|
||||
rt_memmove(dest, src, 0);
|
||||
uassert_str_equal(dest, "World");
|
||||
}
|
||||
|
||||
/* Move to the same location */
|
||||
static void TC_rt_memmove_same_location(void)
|
||||
{
|
||||
char buffer[] = "Hello";
|
||||
rt_memmove(buffer, buffer, rt_strlen(buffer) + 1);
|
||||
uassert_str_equal(buffer, "Hello");
|
||||
}
|
||||
|
||||
/* Move from NULL */
|
||||
static void TC_rt_memmove_null_src(void)
|
||||
{
|
||||
char dest[10];
|
||||
rt_memset(dest, 'A', sizeof(dest));
|
||||
rt_memmove(dest, RT_NULL, 0); /* Should not crash and do nothing */
|
||||
uassert_buf_equal(dest, "AAAAAAAAAA", 10);
|
||||
}
|
||||
|
||||
/* Move to NULL */
|
||||
static void TC_rt_memmove_null_dest(void)
|
||||
{
|
||||
char src[] = "Hello";
|
||||
rt_memmove(RT_NULL, src, 0); /* Should not crash and do nothing */
|
||||
}
|
||||
|
||||
/* Move more than source size */
|
||||
static void TC_rt_memmove_too_long(void)
|
||||
{
|
||||
char src[] = "Short";
|
||||
char dest[10] = {0};
|
||||
rt_memmove(dest, src, sizeof(src) + 5); /* Should only copy up to src length */
|
||||
uassert_str_equal(dest, "Short");
|
||||
uassert_int_equal(dest[5], 0); /* Ensure no buffer overflow */
|
||||
}
|
||||
|
||||
/* Move empty string */
|
||||
static void TC_rt_memmove_empty_string(void)
|
||||
{
|
||||
char src[] = "";
|
||||
char dest[10] = "Unchanged";
|
||||
rt_memmove(dest, src, rt_strlen(src) + 1);
|
||||
|
||||
/* Expect dest to only contain '\0' at the start */
|
||||
uassert_str_equal(dest, ""); /* Destination should now be an empty string */
|
||||
uassert_int_equal(dest[0], '\0'); /* First character should be '\0' */
|
||||
}
|
||||
|
||||
/* Utest function to run all test cases */
|
||||
static void utest_do_tc(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_basic);
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_overlap_src_before);
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_overlap_src_after);
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_zero_length);
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_same_location);
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_null_src);
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_null_dest);
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_too_long);
|
||||
UTEST_UNIT_RUN(TC_rt_memmove_empty_string);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memmove", RT_NULL, RT_NULL, 1000);
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2020-05-06 Phillip Johnston the first version
|
||||
* 2024-12-24 Meco Man port to utest
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <utest.h>
|
||||
|
||||
#define TEST_BUF_SIZE 400
|
||||
|
||||
static char *buf;
|
||||
static char *buf2;
|
||||
|
||||
static rt_err_t utest_tc_init(void)
|
||||
{
|
||||
buf = rt_malloc(TEST_BUF_SIZE * sizeof(char));
|
||||
uassert_not_null(buf);
|
||||
buf2 = rt_malloc(TEST_BUF_SIZE * sizeof(char));
|
||||
uassert_not_null(buf2);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t utest_tc_cleanup(void)
|
||||
{
|
||||
rt_free(buf);
|
||||
rt_free(buf2);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static void test_align(int align, size_t len)
|
||||
{
|
||||
char *s = (char *)RT_ALIGN(((rt_ubase_t)buf + 64), 64) + align;
|
||||
char *want = (char *)RT_ALIGN(((rt_ubase_t)buf2 + 64), 64) + align;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
uassert_false(len + 64 > (size_t)(buf + TEST_BUF_SIZE - s));
|
||||
uassert_false(len + 64 > (size_t)(buf2 + TEST_BUF_SIZE - want));
|
||||
|
||||
for(i = 0; i < TEST_BUF_SIZE; i++)
|
||||
{
|
||||
buf[i] = buf2[i] = ' ';
|
||||
}
|
||||
|
||||
for(i = 0; i < (int)len; i++)
|
||||
{
|
||||
want[i] = '#';
|
||||
}
|
||||
|
||||
p = rt_memset(s, '#', len);
|
||||
|
||||
uassert_ptr_equal(p, s);
|
||||
|
||||
for(i = -64; i < (int)len + 64; i++)
|
||||
{
|
||||
uassert_int_equal(s[i], want[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void TC_rt_memcpy_align(void)
|
||||
{
|
||||
for(int i = 0; i < 16; i++)
|
||||
{
|
||||
for(size_t j = 0; j < 200; j++)
|
||||
{
|
||||
test_align(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_input(char c)
|
||||
{
|
||||
rt_memset(buf, c, 10);
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
uassert_int_equal(buf[i], c);
|
||||
}
|
||||
}
|
||||
|
||||
static void TC_rt_memcpy_input(void)
|
||||
{
|
||||
test_input('c');
|
||||
test_input(0);
|
||||
test_input(-1);
|
||||
test_input(0xab);
|
||||
test_input((char)RT_UINT32_MAX);
|
||||
test_input((char)-RT_UINT32_MAX);
|
||||
}
|
||||
|
||||
static void utest_do_tc(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(TC_rt_memcpy_align);
|
||||
UTEST_UNIT_RUN(TC_rt_memcpy_input);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memset", utest_tc_init, utest_tc_cleanup, 1000);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2025, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2025-01-04 Meco Man the first version
|
||||
*/
|
||||
|
||||
#include <rtklibc.h>
|
||||
#include "utest.h"
|
||||
|
||||
static void TC_rt_sscanf_char(void)
|
||||
{
|
||||
const char str[] = "A B";
|
||||
char a, b;
|
||||
rt_sscanf(str, "%c %c", &a, &b);
|
||||
uassert_true(a == 'A' && b == 'B');
|
||||
/* Move to the next character after space for the second %c */
|
||||
rt_sscanf(str + 2, "%c", &b);
|
||||
uassert_true(b == 'B');
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_basic_int(void)
|
||||
{
|
||||
const char str[] = "12345";
|
||||
int value;
|
||||
int result = rt_sscanf(str, "%d", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 12345);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_basic_float(void)
|
||||
{
|
||||
const char str[] = "123.45";
|
||||
float value;
|
||||
int result = rt_sscanf(str, "%f", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_float_equal(value, 123.45);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_basic_string(void)
|
||||
{
|
||||
const char str[] = "Hello, World!";
|
||||
char buffer[20];
|
||||
int result = rt_sscanf(str, "%s", buffer);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_str_equal(buffer, "Hello,");
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_string_with_space(void)
|
||||
{
|
||||
const char str[] = "Hello World";
|
||||
char a[20];
|
||||
rt_sscanf(str, "%*s %s", a);
|
||||
uassert_str_equal(a, "World");
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_basic_char(void)
|
||||
{
|
||||
const char str[] = "A";
|
||||
char value;
|
||||
int result = rt_sscanf(str, "%c", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 'A');
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_hex_1(void)
|
||||
{
|
||||
const char str[] = "0x1A3F";
|
||||
int value;
|
||||
int result = rt_sscanf(str, "%x", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 0x1A3F);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_hex_2(void)
|
||||
{
|
||||
const char str[] = "0x1A 0XFF";
|
||||
int a, b;
|
||||
rt_sscanf(str, "%x %x", &a, &b);
|
||||
uassert_true(a == 0x1A && b == 0XFF);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_oct_1(void)
|
||||
{
|
||||
const char str[] = "0755";
|
||||
int value;
|
||||
int result = rt_sscanf(str, "%o", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 0755);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_oct_2(void)
|
||||
{
|
||||
const char str[] = "012 077";
|
||||
int a, b;
|
||||
rt_sscanf(str, "%o %o", &a, &b);
|
||||
uassert_true(a == 012 && b == 077);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_multiple_args(void)
|
||||
{
|
||||
const char str[] = "123 Hello";
|
||||
int int_value;
|
||||
char str_value[20];
|
||||
int result = rt_sscanf(str, "%d %s", &int_value, str_value);
|
||||
uassert_int_equal(result, 2);
|
||||
uassert_int_equal(int_value, 123);
|
||||
uassert_str_equal(str_value, "Hello");
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_pointer(void)
|
||||
{
|
||||
const char str[] = "0x12345678";
|
||||
void *ptr;
|
||||
int result = rt_sscanf(str, "%p", &ptr);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_ptr_equal(ptr, (void *)0x12345678);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_width_specifier(void)
|
||||
{
|
||||
const char str[] = "123456789";
|
||||
int value;
|
||||
int result = rt_sscanf(str, "%4d", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 1234);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_suppression(void)
|
||||
{
|
||||
const char str[] = "123 456";
|
||||
int second_value;
|
||||
int result = rt_sscanf(str, "%*d %d", &second_value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(second_value, 456);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_match_set(void)
|
||||
{
|
||||
const char str[] = "abc123";
|
||||
char buffer[10] = {0};
|
||||
int result = rt_sscanf(str, "%[a-z]", buffer);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_str_equal(buffer, "abc");
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_match_set_negated(void)
|
||||
{
|
||||
const char str[] = "abc123";
|
||||
char buffer[10];
|
||||
int result = rt_sscanf(str, "%[^0-9]", buffer);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_str_equal(buffer, "abc");
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_match_set_range(void)
|
||||
{
|
||||
const char str[] = "a-zA-Z";
|
||||
char buffer[10];
|
||||
int result = rt_sscanf(str, "%[a-z-A-Z]", buffer);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_str_equal(buffer, "a-zA-Z");
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_whitespace_skip(void)
|
||||
{
|
||||
const char str[] = " 12345";
|
||||
int value;
|
||||
int result = rt_sscanf(str, "%d", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 12345);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_unsigned_int(void)
|
||||
{
|
||||
const char str[] = "4294967295";
|
||||
unsigned int value;
|
||||
int result = rt_sscanf(str, "%u", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 4294967295U);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_long_long_int(void)
|
||||
{
|
||||
const char str[] = "9223372036854775807";
|
||||
long long value;
|
||||
int result = rt_sscanf(str, "%lld", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 9223372036854775807LL);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_short_int(void)
|
||||
{
|
||||
const char str[] = "32767";
|
||||
short value;
|
||||
int result = rt_sscanf(str, "%hd", &value);
|
||||
uassert_int_equal(result, 1);
|
||||
uassert_int_equal(value, 32767);
|
||||
}
|
||||
|
||||
static void TC_rt_sscanf_null_string(void)
|
||||
{
|
||||
const char str[] = "";
|
||||
int value;
|
||||
int result = rt_sscanf(str, "%d", &value);
|
||||
uassert_int_equal(result, -1);
|
||||
}
|
||||
|
||||
/* https://github.com/RT-Thread/rt-thread/issues/9853 */
|
||||
static void TC_rt_sscanf_issue_9853(void)
|
||||
{
|
||||
int device_socket = 255;
|
||||
int bfsz = 255;
|
||||
const char str[] = "+MIPURC: \"rtcp\",0,240,HTTP/1.1 200 OK";
|
||||
rt_sscanf(str, "+MIPURC:%*[^,],%d,%d", &device_socket, (int *)&bfsz);
|
||||
uassert_int_equal(device_socket, 0);
|
||||
uassert_int_equal(bfsz, 240);
|
||||
}
|
||||
|
||||
static void utest_do_tc(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_char);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_basic_int);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_basic_float);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_basic_string);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_string_with_space);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_basic_char);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_hex_1);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_hex_2);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_oct_1);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_oct_2);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_multiple_args);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_pointer);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_width_specifier);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_suppression);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_match_set);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_match_set_negated);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_match_set_range);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_whitespace_skip);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_unsigned_int);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_long_long_int);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_short_int);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_null_string);
|
||||
UTEST_UNIT_RUN(TC_rt_sscanf_issue_9853);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_sscanf", RT_NULL, RT_NULL, 1000);
|
||||
Reference in New Issue
Block a user