|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "edge-impulse-sdk/tensorflow/lite/micro/micro_string.h" |
|
|
|
|
|
#include <cstdarg> |
|
|
#include <cstdint> |
|
|
#include <cstring> |
|
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
|
|
constexpr int kMaxIntCharsNeeded = 10 + 1; |
|
|
|
|
|
constexpr int kMaxHexCharsNeeded = 8 + 2; |
|
|
|
|
|
|
|
|
|
|
|
constexpr float kMaxFloatCharsNeeded = 7 + 3 + 3 + 1; |
|
|
|
|
|
|
|
|
const int kFastToBufferSize = 48; |
|
|
|
|
|
|
|
|
char* ReverseStringInPlace(char* start, char* end) { |
|
|
char* p1 = start; |
|
|
char* p2 = end - 1; |
|
|
while (p1 < p2) { |
|
|
char tmp = *p1; |
|
|
*p1++ = *p2; |
|
|
*p2-- = tmp; |
|
|
} |
|
|
return start; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
char* StrCatStr(char* main, int main_max_length, const char* to_append) { |
|
|
char* current = main; |
|
|
while (*current != 0) { |
|
|
++current; |
|
|
} |
|
|
char* current_end = main + (main_max_length - 1); |
|
|
while ((*to_append != 0) && (current < current_end)) { |
|
|
*current = *to_append; |
|
|
++current; |
|
|
++to_append; |
|
|
} |
|
|
*current = 0; |
|
|
return current; |
|
|
} |
|
|
|
|
|
|
|
|
char* FastUInt32ToBufferLeft(uint32_t i, char* buffer, int base) { |
|
|
char* start = buffer; |
|
|
do { |
|
|
int32_t digit = i % base; |
|
|
char character; |
|
|
if (digit < 10) { |
|
|
character = '0' + digit; |
|
|
} else { |
|
|
character = 'a' + (digit - 10); |
|
|
} |
|
|
*buffer++ = character; |
|
|
i /= base; |
|
|
} while (i > 0); |
|
|
*buffer = 0; |
|
|
ReverseStringInPlace(start, buffer); |
|
|
return buffer; |
|
|
} |
|
|
|
|
|
|
|
|
char* FastInt32ToBufferLeft(int32_t i, char* buffer) { |
|
|
uint32_t u = i; |
|
|
if (i < 0) { |
|
|
*buffer++ = '-'; |
|
|
u = -u; |
|
|
} |
|
|
return FastUInt32ToBufferLeft(u, buffer, 10); |
|
|
} |
|
|
|
|
|
|
|
|
char* StrCatInt32(char* main, int main_max_length, int32_t number) { |
|
|
char number_string[kFastToBufferSize]; |
|
|
FastInt32ToBufferLeft(number, number_string); |
|
|
return StrCatStr(main, main_max_length, number_string); |
|
|
} |
|
|
|
|
|
|
|
|
char* StrCatUInt32(char* main, int main_max_length, uint32_t number, int base) { |
|
|
char number_string[kFastToBufferSize]; |
|
|
FastUInt32ToBufferLeft(number, number_string, base); |
|
|
return StrCatStr(main, main_max_length, number_string); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char* FastFloatToBufferLeft(float f, char* buffer) { |
|
|
char* current = buffer; |
|
|
char* current_end = buffer + (kFastToBufferSize - 1); |
|
|
|
|
|
|
|
|
const uint32_t sign_mask = 0x80000000; |
|
|
const uint32_t exponent_mask = 0x7f800000; |
|
|
const int32_t exponent_shift = 23; |
|
|
const int32_t exponent_bias = 127; |
|
|
const uint32_t fraction_mask = 0x007fffff; |
|
|
uint32_t u; |
|
|
memcpy(&u, &f, sizeof(int32_t)); |
|
|
const int32_t exponent = |
|
|
((u & exponent_mask) >> exponent_shift) - exponent_bias; |
|
|
const uint32_t fraction = (u & fraction_mask); |
|
|
|
|
|
if (u & sign_mask) { |
|
|
*current = '-'; |
|
|
current += 1; |
|
|
} |
|
|
*current = 0; |
|
|
|
|
|
if (exponent == 128) { |
|
|
if (fraction == 0) { |
|
|
current = StrCatStr(current, (current_end - current), "Inf"); |
|
|
return current; |
|
|
} else { |
|
|
current = StrCatStr(current, (current_end - current), "NaN"); |
|
|
return current; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int32_t scale_shifts_size = 13; |
|
|
const int8_t scale_shifts[13] = {3, 4, 8, 11, 13, 14, 17, |
|
|
18, 19, 20, 21, 22, 23}; |
|
|
uint32_t scaled_fraction = fraction; |
|
|
for (int i = 0; i < scale_shifts_size; ++i) { |
|
|
scaled_fraction += (fraction >> scale_shifts[i]); |
|
|
} |
|
|
*current = '1'; |
|
|
current += 1; |
|
|
*current = '.'; |
|
|
current += 1; |
|
|
*current = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constexpr int kMaxFractionalDigits = 7; |
|
|
|
|
|
|
|
|
if (current_end - current <= kMaxFractionalDigits) { |
|
|
return current; |
|
|
} |
|
|
|
|
|
|
|
|
for (int i = 1; i < kMaxFractionalDigits; i++) { |
|
|
*(current + i) = '0'; |
|
|
} |
|
|
|
|
|
|
|
|
char* previous = current; |
|
|
current = StrCatUInt32(current, (current_end - current), scaled_fraction, 10); |
|
|
int fraction_digits = current - previous; |
|
|
int leading_zeros = kMaxFractionalDigits - fraction_digits; |
|
|
|
|
|
|
|
|
|
|
|
*current = '0'; |
|
|
|
|
|
|
|
|
if (leading_zeros != 0) { |
|
|
for (int i = 0; i < fraction_digits; i++) { |
|
|
current--; |
|
|
*(current + leading_zeros) = *current; |
|
|
*current = '0'; |
|
|
} |
|
|
current += kMaxFractionalDigits; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
while (*(current - 1) == '0' && (current - 1) > previous) { |
|
|
current--; |
|
|
} |
|
|
*current = 0; |
|
|
current = StrCatStr(current, (current_end - current), "*2^"); |
|
|
current = StrCatInt32(current, (current_end - current), exponent); |
|
|
return current; |
|
|
} |
|
|
|
|
|
int FormatInt32(char* output, int32_t i) { |
|
|
return static_cast<int>(FastInt32ToBufferLeft(i, output) - output); |
|
|
} |
|
|
|
|
|
int FormatUInt32(char* output, uint32_t i) { |
|
|
return static_cast<int>(FastUInt32ToBufferLeft(i, output, 10) - output); |
|
|
} |
|
|
|
|
|
int FormatHex(char* output, uint32_t i) { |
|
|
return static_cast<int>(FastUInt32ToBufferLeft(i, output, 16) - output); |
|
|
} |
|
|
|
|
|
int FormatFloat(char* output, float i) { |
|
|
return static_cast<int>(FastFloatToBufferLeft(i, output) - output); |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
extern "C" int MicroVsnprintf(char* output, int len, const char* format, |
|
|
va_list args) { |
|
|
int output_index = 0; |
|
|
const char* current = format; |
|
|
|
|
|
const int usable_length = len - 1; |
|
|
while (*current != '\0' && output_index < usable_length) { |
|
|
if (*current == '%') { |
|
|
current++; |
|
|
switch (*current) { |
|
|
case 'd': |
|
|
|
|
|
if (usable_length - output_index < kMaxIntCharsNeeded) { |
|
|
output[output_index++] = '\0'; |
|
|
return output_index; |
|
|
} |
|
|
output_index += |
|
|
FormatInt32(&output[output_index], va_arg(args, int32_t)); |
|
|
current++; |
|
|
break; |
|
|
case 'u': |
|
|
if (usable_length - output_index < kMaxIntCharsNeeded) { |
|
|
output[output_index++] = '\0'; |
|
|
return output_index; |
|
|
} |
|
|
output_index += |
|
|
FormatUInt32(&output[output_index], va_arg(args, uint32_t)); |
|
|
current++; |
|
|
break; |
|
|
case 'x': |
|
|
if (usable_length - output_index < kMaxHexCharsNeeded) { |
|
|
output[output_index++] = '\0'; |
|
|
return output_index; |
|
|
} |
|
|
output[output_index++] = '0'; |
|
|
output[output_index++] = 'x'; |
|
|
output_index += |
|
|
FormatHex(&output[output_index], va_arg(args, uint32_t)); |
|
|
current++; |
|
|
break; |
|
|
case 'f': |
|
|
if (usable_length - output_index < kMaxFloatCharsNeeded) { |
|
|
output[output_index++] = '\0'; |
|
|
return output_index; |
|
|
} |
|
|
output_index += |
|
|
FormatFloat(&output[output_index], va_arg(args, double)); |
|
|
current++; |
|
|
break; |
|
|
case '%': |
|
|
output[output_index++] = *current++; |
|
|
break; |
|
|
case 's': |
|
|
char* string = va_arg(args, char*); |
|
|
int string_idx = 0; |
|
|
while (string_idx + output_index < usable_length && |
|
|
string[string_idx] != '\0') { |
|
|
output[output_index++] = string[string_idx++]; |
|
|
} |
|
|
current++; |
|
|
} |
|
|
} else { |
|
|
output[output_index++] = *current++; |
|
|
} |
|
|
} |
|
|
output[output_index++] = '\0'; |
|
|
return output_index; |
|
|
} |
|
|
|
|
|
extern "C" int MicroSnprintf(char* output, int len, const char* format, ...) { |
|
|
va_list args; |
|
|
va_start(args, format); |
|
|
int bytes_written = MicroVsnprintf(output, len, format, args); |
|
|
va_end(args); |
|
|
return bytes_written; |
|
|
} |
|
|
|