2015-07-10 06:01:56 -07:00
/**
* @ file efilib . cpp
*
* We cannot use stdlib because we do not have malloc - so , we have to implement these functions
*
* @ date Feb 21 , 2014
2020-01-13 18:57:43 -08:00
* @ author Andrey Belomutskiy , ( c ) 2012 - 2020
2015-07-10 06:01:56 -07:00
*/
2021-07-25 22:05:17 -07:00
# include "pch.h"
2015-07-10 06:01:56 -07:00
# include <string.h>
# include <math.h>
# include "datalogging.h"
# include "histogram.h"
const char * boolToString ( bool value ) {
return value ? " Yes " : " No " ;
}
int minI ( int i1 , int i2 ) {
return i1 < i2 ? i1 : i2 ;
}
2017-12-10 07:19:05 -08:00
/*
2017-12-10 12:05:58 -08:00
float efiFloor ( float value , float precision ) {
2015-07-10 06:01:56 -07:00
int a = ( int ) ( value / precision ) ;
return a * precision ;
}
2017-12-10 07:19:05 -08:00
*/
2018-01-07 09:11:49 -08:00
/**
*
* @ param precision for example ' 0.1 ' for one digit fractional part
*/
2017-12-10 07:19:05 -08:00
float efiRound ( float value , float precision ) {
2018-07-25 20:30:00 -07:00
efiAssert ( CUSTOM_ERR_ASSERT , precision ! = 0 , " zero precision " , NAN ) ;
2020-12-05 21:11:57 -08:00
float a = round ( value / precision ) ;
2020-06-18 20:29:08 -07:00
return fixNegativeZero ( a * precision ) ;
2017-12-10 07:19:05 -08:00
}
2015-07-10 06:01:56 -07:00
float absF ( float value ) {
return value > 0 ? value : - value ;
}
int absI ( int32_t value ) {
return value > = 0 ? value : - value ;
}
int maxI ( int i1 , int i2 ) {
return i1 > i2 ? i1 : i2 ;
}
float maxF ( float i1 , float i2 ) {
return i1 > i2 ? i1 : i2 ;
}
float minF ( float i1 , float i2 ) {
return i1 < i2 ? i1 : i2 ;
}
2021-04-29 19:22:04 -07:00
int clampI ( int min , int clamp , int max ) {
return maxI ( min , minI ( clamp , max ) ) ;
}
2020-04-20 10:52:20 -07:00
float clampF ( float min , float clamp , float max ) {
return maxF ( min , minF ( clamp , max ) ) ;
}
2015-07-10 06:01:56 -07:00
uint32_t efiStrlen ( const char * param ) {
2020-07-29 02:40:10 -07:00
const char * s ;
2015-07-10 06:01:56 -07:00
for ( s = param ; * s ; + + s )
;
return ( s - param ) ;
}
char * efiTrim ( char * param ) {
while ( param [ 0 ] = = ' ' ) {
param + + ; // that would skip leading spaces
}
int len = efiStrlen ( param ) ;
while ( len > 0 & & param [ len - 1 ] = = ' ' ) {
param [ len - 1 ] = 0 ;
len - - ;
}
return param ;
}
bool startsWith ( const char * line , const char * prefix ) {
uint32_t len = efiStrlen ( prefix ) ;
if ( efiStrlen ( line ) < len ) {
return false ;
}
for ( uint32_t i = 0 ; i < len ; i + + ) {
if ( line [ i ] ! = prefix [ i ] ) {
return false ;
}
}
return true ;
}
int indexOf ( const char * string , char ch ) {
2020-08-06 19:05:26 -07:00
// a standard function for this is strnchr?
2015-07-10 06:01:56 -07:00
// todo: on the other hand MISRA wants us not to use standard headers
int len = efiStrlen ( string ) ;
for ( int i = 0 ; i < len ; i + + ) {
if ( string [ i ] = = ch ) {
return i ;
}
}
return - 1 ;
}
// string to integer
int atoi ( const char * string ) {
// todo: use stdlib '#include <stdlib.h> '
int len = strlen ( string ) ;
if ( len = = 0 ) {
return - ERROR_CODE ;
}
if ( string [ 0 ] = = ' - ' ) {
return - atoi ( string + 1 ) ;
}
int result = 0 ;
for ( int i = 0 ; i < len ; i + + ) {
char ch = string [ i ] ;
if ( ch < ' 0 ' | | ch > ' 9 ' ) {
return ERROR_CODE ;
}
int c = ch - ' 0 ' ;
result = result * 10 + c ;
}
return result ;
}
static char todofixthismesswithcopy [ 100 ] ;
2019-09-23 05:45:45 -07:00
static char * ltoa_internal ( char * p , uint32_t num , unsigned radix ) {
constexpr int bufferLength = 10 ;
char buffer [ bufferLength ] ;
size_t idx = bufferLength - 1 ;
// First, we write from right-to-left so that we don't have to compute
// log(num)/log(radix)
do
{
auto digit = num % radix ;
// Digits 0-9 -> '0'-'9'
// Digits 10-15 -> 'a'-'f'
char c = digit < 10
? digit + ' 0 '
: digit + ' a ' - 10 ;
// Write this digit in to the buffer
buffer [ idx ] = c ;
idx - - ;
2015-07-10 06:01:56 -07:00
} while ( ( num / = radix ) ! = 0 ) ;
2019-09-23 05:45:45 -07:00
idx + + ;
2015-07-10 06:01:56 -07:00
2019-09-23 05:45:45 -07:00
// Now, we copy characters in to place in the final buffer
while ( idx < bufferLength )
{
* p + + = buffer [ idx + + ] ;
}
2015-07-10 06:01:56 -07:00
return p ;
}
2017-03-05 11:17:48 -08:00
/**
* @ return pointer at the end zero symbol after the digits
*/
2015-07-10 06:01:56 -07:00
static char * itoa_signed ( char * p , int num , unsigned radix ) {
if ( num < 0 ) {
* p + + = ' - ' ;
char * end = ltoa_internal ( p , - num , radix ) ;
* end = 0 ;
return end ;
}
char * end = ltoa_internal ( p , num , radix ) ;
* end = 0 ;
return end ;
}
/**
* Integer to string
2017-03-05 11:17:48 -08:00
*
* @ return pointer at the end zero symbol after the digits
2015-07-10 06:01:56 -07:00
*/
char * itoa10 ( char * p , int num ) {
// todo: unit test
return itoa_signed ( p , num , 10 ) ;
}
# define EPS 0.0001
bool isSameF ( float v1 , float v2 ) {
return absF ( v1 - v2 ) < EPS ;
}
int efiPow10 ( int param ) {
switch ( param ) {
case 0 :
return 1 ;
case 1 :
return 10 ;
case 2 :
return 100 ;
case 3 :
return 1000 ;
case 4 :
return 10000 ;
case 5 :
return 100000 ;
case 6 :
return 1000000 ;
case 7 :
return 10000000 ;
case 8 :
return 100000000 ;
}
return 10 * efiPow10 ( 10 - 1 ) ;
}
/**
2018-11-25 19:20:57 -08:00
* string to float . NaN input is supported
2016-06-22 20:01:57 -07:00
*
2015-07-10 06:01:56 -07:00
* @ return NAN in case of invalid string
2016-06-22 20:01:57 -07:00
* todo : explicit value for error code ? probably not , NaN is only returned in case of an error
2015-07-10 06:01:56 -07:00
*/
float atoff ( const char * param ) {
uint32_t totallen = strlen ( param ) ;
if ( totallen > sizeof ( todofixthismesswithcopy ) - 1 )
return ( float ) NAN ;
strcpy ( todofixthismesswithcopy , param ) ;
char * string = todofixthismesswithcopy ;
2018-11-25 19:20:57 -08:00
if ( indexOf ( string , ' n ' ) ! = - 1 | | indexOf ( string , ' N ' ) ! = - 1 ) {
2019-03-30 15:04:22 -07:00
# if ! EFI_SIMULATOR
2021-04-21 09:53:13 -07:00
efiPrintf ( " NAN from [%s] " , string ) ;
2019-03-30 15:04:22 -07:00
# endif
2018-11-25 19:20:57 -08:00
return ( float ) NAN ;
}
2015-07-10 06:01:56 -07:00
// todo: is there a standard function?
2018-11-25 19:20:57 -08:00
// unit-tested by 'testMisc()'
2015-07-10 06:01:56 -07:00
int dotIndex = indexOf ( string , ' . ' ) ;
if ( dotIndex = = - 1 ) {
// just an integer
int result = atoi ( string ) ;
if ( absI ( result ) = = ERROR_CODE )
return ( float ) NAN ;
return ( float ) result ;
}
// todo: this needs to be fixed
string [ dotIndex ] = 0 ;
int integerPart = atoi ( string ) ;
if ( absI ( integerPart ) = = ERROR_CODE )
return ( float ) NAN ;
string + = ( dotIndex + 1 ) ;
int decimalLen = strlen ( string ) ;
int decimal = atoi ( string ) ;
if ( absI ( decimal ) = = ERROR_CODE )
return ( float ) NAN ;
float divider = 1.0 ;
// todo: reuse 'pow10' function which we have anyway
for ( int i = 0 ; i < decimalLen ; i + + ) {
divider = divider * 10.0 ;
}
return integerPart + decimal / divider ;
}
# define TO_LOWER(x) (((x)>='A' && (x)<='Z') ? (x) - 'A' + 'a' : (x))
bool strEqualCaseInsensitive ( const char * str1 , const char * str2 ) {
int len1 = strlen ( str1 ) ;
int len2 = strlen ( str2 ) ;
if ( len1 ! = len2 ) {
return false ;
}
for ( int i = 0 ; i < len1 ; i + + )
if ( TO_LOWER ( str1 [ i ] ) ! = TO_LOWER ( str2 [ i ] ) )
return false ;
return true ;
}
2017-04-04 20:00:21 -07:00
/*
* * return lower - case of c if upper - case , else c
*/
int mytolower ( const char c ) {
return TO_LOWER ( c ) ;
}
2015-07-10 06:01:56 -07:00
bool strEqual ( const char * str1 , const char * str2 ) {
// todo: there must be a standard function?!
int len1 = strlen ( str1 ) ;
int len2 = strlen ( str2 ) ;
if ( len1 ! = len2 ) {
return false ;
}
for ( int i = 0 ; i < len1 ; i + + )
if ( str1 [ i ] ! = str2 [ i ] )
return false ;
return true ;
}
/**
* @ brief This function knows how to print a histogram_s summary
*/
void printHistogram ( Logging * logging , histogram_s * histogram ) {
# if EFI_HISTOGRAMS && ! EFI_UNIT_TEST
int report [ 5 ] ;
int len = hsReport ( histogram , report ) ;
2020-09-13 01:49:25 -07:00
logging - > reset ( ) ;
2021-11-08 16:01:18 -08:00
logging . append ( PROTOCOL_MSG DELIMETER ) ;
2020-09-13 01:49:25 -07:00
logging . appendPrintf ( " histogram %s * " , histogram - > name ) ;
2015-07-10 06:01:56 -07:00
for ( int i = 0 ; i < len ; i + + )
2020-09-13 01:49:25 -07:00
logging . appendPrintf ( " %d " , report [ i ] ) ;
logging . appendPrintf ( " * " ) ;
2021-11-08 16:01:18 -08:00
logging . append ( DELIMETER ) ;
2015-07-10 06:01:56 -07:00
scheduleLogging ( logging ) ;
# else
UNUSED ( logging ) ;
UNUSED ( histogram ) ;
# endif /* EFI_HISTOGRAMS */
}
2018-09-29 09:16:36 -07:00
float limitRateOfChange ( float newValue , float oldValue , float incrLimitPerSec , float decrLimitPerSec , float secsPassed ) {
if ( newValue > = oldValue )
return ( incrLimitPerSec < = 0.0f ) ? newValue : oldValue + minF ( newValue - oldValue , incrLimitPerSec * secsPassed ) ;
return ( decrLimitPerSec < = 0.0f ) ? newValue : oldValue - minF ( oldValue - newValue , decrLimitPerSec * secsPassed ) ;
}
2019-01-06 17:45:51 -08:00
constexpr float constant_e = 2.71828f ;
2019-01-06 17:48:15 -08:00
// 'constexpr' is a keyword that tells the compiler
// "yes, this thing, it's a 'pure function' that only depends on its inputs and has no side effects"
// like how const is a constant value, constexpr is a constant expression
// so if somewhere you used it in a way that it could determine the exact arguments to the function at compile time, it will _run_ the function at compile time, and cook in the result as a constant
2019-01-06 17:45:51 -08:00
constexpr float expf_taylor_impl ( float x , uint8_t n )
{
if ( x < - 2 )
{
return 0.818f ;
}
else if ( x > 0 )
{
return 1 ;
}
x = x + 1 ;
float x_power = x ;
int fac = 1 ;
float sum = 1 ;
for ( int i = 1 ; i < = n ; i + + )
{
fac * = i ;
sum + = x_power / fac ;
x_power * = x ;
}
return sum / constant_e ;
}
float expf_taylor ( float x )
{
return expf_taylor_impl ( x , 4 ) ;
}
2020-11-22 05:26:17 -08:00
float tanf_taylor ( float x ) {
// This exists because the "normal" implementation, tanf, pulls in like 6kb of
// code and loookup tables
// This is only specified from [0, pi/2 - 0.01)
// Inside that range it has an error of less than 0.1%, and it gets worse as theta -> pi/2
// Precompute some exponents of x
float x2 = x * x ;
float x3 = x2 * x ;
float x4 = x3 * x ;
float x5 = x4 * x ;
float x6 = x5 * x ;
// x7 not used
float x8 = x6 * x2 ;
// 3-term Taylor Series for sin(theta)
float sin_val = x - ( x3 / 6 ) + ( x5 / 120 ) ;
// 5-term Taylor Series for cos(theta)
float cos_val = 1 - ( x2 / 2 ) + ( x4 / 24 ) - ( x6 / 720 ) + ( x8 / 40320 ) ;
// tan = sin / cos
return sin_val / cos_val ;
}