| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
|
|
| #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 'c': |
| if (usable_length - output_index < 1) { |
| output[output_index++] = '\0'; |
| return output_index; |
| } |
| output[output_index++] = va_arg(args, int32_t); |
| 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; |
| } |
|
|