diff --git a/FeatureRequest.md b/FeatureRequest.md index 1a09d17d..58b2acce 100644 --- a/FeatureRequest.md +++ b/FeatureRequest.md @@ -11,6 +11,20 @@ ____ + + +#### #15 Calibration for FishEye image + +* https://github.com/jomjol/AI-on-the-edge-device/issues/507 + +1. The development of such a correction algorithm with the libraries, that are available for the ESP32 environment. +2. New module for integration of the flow into the image processing flow. +3. Extension of the configuration (config.ini) and html-pages +4. Parameter adjustment and testing for every different fish-eye module +5. Maintenance for further updates / modules, ... + + + #### #14 Backup and restore option for configuration * https://github.com/jomjol/AI-on-the-edge-device/issues/459 diff --git a/README.md b/README.md index 351573f2..4467534a 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,15 @@ In other cases you can contact the developer via email: #include #include + +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif #include "esp_err.h" #include "esp_log.h" diff --git a/code/components/jomjol_fileserver_ota/server_help.cpp b/code/components/jomjol_fileserver_ota/server_help.cpp index 25ffbcc4..4bb06ffa 100644 --- a/code/components/jomjol_fileserver_ota/server_help.cpp +++ b/code/components/jomjol_fileserver_ota/server_help.cpp @@ -5,7 +5,14 @@ #include #include #include + +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif #include "esp_err.h" #include "esp_log.h" diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp index ce1b133c..c76a7ec2 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp @@ -6,7 +6,15 @@ #include "freertos/task.h" #include + +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif + #include "ClassLogFile.h" #include "time_sntp.h" #include "Helper.h" diff --git a/code/components/jomjol_flowcontroll/ClassFlowImage.cpp b/code/components/jomjol_flowcontroll/ClassFlowImage.cpp index 1473fe6c..c50b8cda 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowImage.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowImage.cpp @@ -2,7 +2,15 @@ #include #include #include + +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif + #include "time_sntp.h" #include "ClassLogFile.h" #include "CImageBasis.h" diff --git a/code/components/jomjol_helper/CMakeLists.txt b/code/components/jomjol_helper/CMakeLists.txt index 948e3829..c57b54d3 100644 --- a/code/components/jomjol_helper/CMakeLists.txt +++ b/code/components/jomjol_helper/CMakeLists.txt @@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "." - REQUIRES tfmicro jomjol_logfile) + REQUIRES tflite-lib jomjol_logfile) diff --git a/code/components/jomjol_helper/Helper.cpp b/code/components/jomjol_helper/Helper.cpp index d8537b86..fe299f54 100644 --- a/code/components/jomjol_helper/Helper.cpp +++ b/code/components/jomjol_helper/Helper.cpp @@ -6,7 +6,15 @@ #include "Helper.h" #include #include + +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif + #include #include diff --git a/code/components/jomjol_logfile/ClassLogFile.cpp b/code/components/jomjol_logfile/ClassLogFile.cpp index a026d985..04a4df4d 100644 --- a/code/components/jomjol_logfile/ClassLogFile.cpp +++ b/code/components/jomjol_logfile/ClassLogFile.cpp @@ -3,7 +3,15 @@ #include #include #include + +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif + #include "Helper.h" static const char *TAG = "log"; diff --git a/code/components/jomjol_mqtt/CMakeLists.txt b/code/components/jomjol_mqtt/CMakeLists.txt index 432727eb..f89faf81 100644 --- a/code/components/jomjol_mqtt/CMakeLists.txt +++ b/code/components/jomjol_mqtt/CMakeLists.txt @@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "." - REQUIRES tfmicro mqtt jomjol_logfile) + REQUIRES tflite-lib mqtt jomjol_logfile) diff --git a/code/components/jomjol_time_sntp/CMakeLists.txt b/code/components/jomjol_time_sntp/CMakeLists.txt index 948e3829..c57b54d3 100644 --- a/code/components/jomjol_time_sntp/CMakeLists.txt +++ b/code/components/jomjol_time_sntp/CMakeLists.txt @@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "." - REQUIRES tfmicro jomjol_logfile) + REQUIRES tflite-lib jomjol_logfile) diff --git a/code/components/tflite-lib/CMakeLists.txt b/code/components/tflite-lib/CMakeLists.txt new file mode 100644 index 00000000..fab7027a --- /dev/null +++ b/code/components/tflite-lib/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.5) + +set(tflite_dir "${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite") +set(tfmicro_dir "${tflite_dir}/micro") +set(tfmicro_frontend_dir "${tflite_dir}/experimental/microfrontend/lib") +set(tfmicro_kernels_dir "${tfmicro_dir}/kernels") + +file(GLOB srcs_micro + "${tfmicro_dir}/*.cc" + "${tfmicro_dir}/*.c") + +file(GLOB src_micro_frontend + "${tfmicro_frontend_dir}/*.c" + "${tfmicro_frontend_dir}/*.cc") +file(GLOB srcs_kernels + "${tfmicro_kernels_dir}/*.c" + "${tfmicro_kernels_dir}/*.cc") + +set(lib_srcs + "${srcs_micro}" + "${srcs_kernels}" + "${src_micro_frontend}" + "${tflite_dir}/kernels/kernel_util.cc" + "${tflite_dir}/micro/memory_planner/greedy_memory_planner.cc" + "${tflite_dir}/micro/memory_planner/linear_memory_planner.cc" + "${tflite_dir}/c/common.c" + "${tflite_dir}/core/api/error_reporter.cc" + "${tflite_dir}/core/api/flatbuffer_conversions.cc" + "${tflite_dir}/core/api/op_resolver.cc" + "${tflite_dir}/core/api/tensor_utils.cc" + "${tflite_dir}/kernels/internal/quantization_util.cc" + "${tflite_dir}/schema/schema_utils.cc") + +idf_component_register( + SRCS "${lib_srcs}" + INCLUDE_DIRS "." "third_party/gemmlowp" + "third_party/flatbuffers/include" + "third_party/ruy" + "third_party/kissfft") + +# Reduce the level of paranoia to be able to compile TF sources +target_compile_options(${COMPONENT_LIB} PRIVATE + -Wno-maybe-uninitialized + -Wno-missing-field-initializers + -Wno-type-limits) + +target_compile_options(${COMPONENT_LIB} PRIVATE -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP -DESP_NN -Wno-nonnull -Wno-nonnull -Wno-nonnull) +target_compile_options(${COMPONENT_LIB} PRIVATE $<$: -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP -DESP_NN -Wno-return-type -Wno-strict-aliasing -std=gnu++14 -Wno-return-type -Wno-strict-aliasing -std=gnu++14 -Wno-return-type -Wno-strict-aliasing -std=gnu++14 >) +target_compile_options(${COMPONENT_LIB} INTERFACE $<$>:-DTF_LITE_STATIC_MEMORY>) +target_link_libraries(${COMPONENT_LIB} PRIVATE -lm) diff --git a/code/components/tfmicro/tensorflow/lite/c/builtin_op_data.h b/code/components/tflite-lib/tensorflow/lite/c/builtin_op_data.h similarity index 97% rename from code/components/tfmicro/tensorflow/lite/c/builtin_op_data.h rename to code/components/tflite-lib/tensorflow/lite/c/builtin_op_data.h index ed5ac004..7f160972 100644 --- a/code/components/tfmicro/tensorflow/lite/c/builtin_op_data.h +++ b/code/components/tflite-lib/tensorflow/lite/c/builtin_op_data.h @@ -502,6 +502,22 @@ typedef struct { const char* shared_name; } TfLiteVarHandleParams; +typedef struct { + int seed; + int seed2; +} TfLiteRandomParams; + +typedef struct { + int num_boundaries; + // This points to the memory stored in the model (flatbuffer), + // and is not owned. + const float* boundaries; +} TfLiteBucketizeParams; + +typedef struct { + bool approximate; +} TfLiteGeluParams; + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/code/components/tfmicro/tensorflow/lite/c/c_api_types.h b/code/components/tflite-lib/tensorflow/lite/c/c_api_types.h similarity index 85% rename from code/components/tfmicro/tensorflow/lite/c/c_api_types.h rename to code/components/tflite-lib/tensorflow/lite/c/c_api_types.h index e9cd4c77..678dfae6 100644 --- a/code/components/tfmicro/tensorflow/lite/c/c_api_types.h +++ b/code/components/tflite-lib/tensorflow/lite/c/c_api_types.h @@ -43,6 +43,9 @@ extern "C" { #endif // _WIN32 #endif // SWIG +// Note that new error status values may be added in future in order to +// indicate more fine-grained internal states, therefore, applications should +// not rely on status values being members of the enum. typedef enum TfLiteStatus { kTfLiteOk = 0, @@ -54,7 +57,7 @@ typedef enum TfLiteStatus { // Generally referring to an error in applying a delegate due to // incompatibility between runtime and delegate, e.g., this error is returned - // when trying to apply a TfLite delegate onto a model graph that's already + // when trying to apply a TF Lite delegate onto a model graph that's already // immutable. kTfLiteApplicationError = 3, @@ -68,7 +71,12 @@ typedef enum TfLiteStatus { // Generally referring to data-reading issues in delegate serialization. // See tflite::delegates::Serialization. - kTfLiteDelegateDataReadError = 5, + kTfLiteDelegateDataReadError = 6, + + // Generally referring to issues when the TF Lite model has ops that cannot be + // resolved at runtime. This could happen when the specific op is not + // registered or built with the TF Lite framework. + kTfLiteUnresolvedOps = 7, } TfLiteStatus; // Types supported by tensor diff --git a/code/components/tfmicro/tensorflow/lite/c/common.c b/code/components/tflite-lib/tensorflow/lite/c/common.c similarity index 85% rename from code/components/tfmicro/tensorflow/lite/c/common.c rename to code/components/tflite-lib/tensorflow/lite/c/common.c index 5456a889..d149d22c 100644 --- a/code/components/tfmicro/tensorflow/lite/c/common.c +++ b/code/components/tflite-lib/tensorflow/lite/c/common.c @@ -21,9 +21,15 @@ limitations under the License. #include #endif // TF_LITE_STATIC_MEMORY -int TfLiteIntArrayGetSizeInBytes(int size) { +size_t TfLiteIntArrayGetSizeInBytes(int size) { static TfLiteIntArray dummy; - return sizeof(dummy) + sizeof(dummy.data[0]) * size; + + size_t computed_size = sizeof(dummy) + sizeof(dummy.data[0]) * size; +#if defined(_MSC_VER) + // Context for why this is needed is in http://b/189926408#comment21 + computed_size -= sizeof(dummy.data[0]); +#endif + return computed_size; } int TfLiteIntArrayEqual(const TfLiteIntArray* a, const TfLiteIntArray* b) { @@ -45,7 +51,7 @@ int TfLiteIntArrayEqualsArray(const TfLiteIntArray* a, int b_size, #ifndef TF_LITE_STATIC_MEMORY TfLiteIntArray* TfLiteIntArrayCreate(int size) { - int alloc_size = TfLiteIntArrayGetSizeInBytes(size); + size_t alloc_size = TfLiteIntArrayGetSizeInBytes(size); if (alloc_size <= 0) return NULL; TfLiteIntArray* ret = (TfLiteIntArray*)malloc(alloc_size); if (!ret) return ret; @@ -68,7 +74,13 @@ void TfLiteIntArrayFree(TfLiteIntArray* a) { free(a); } int TfLiteFloatArrayGetSizeInBytes(int size) { static TfLiteFloatArray dummy; - return sizeof(dummy) + sizeof(dummy.data[0]) * size; + + int computed_size = sizeof(dummy) + sizeof(dummy.data[0]) * size; +#if defined(_MSC_VER) + // Context for why this is needed is in http://b/189926408#comment21 + computed_size -= sizeof(dummy.data[0]); +#endif + return computed_size; } #ifndef TF_LITE_STATIC_MEMORY @@ -176,6 +188,26 @@ void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims, tensor->quantization.params = NULL; } +TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst) { + if (!src || !dst) + return kTfLiteOk; + if (src->bytes != dst->bytes) + return kTfLiteError; + if (src == dst) + return kTfLiteOk; + + dst->type = src->type; + if (dst->dims) + TfLiteIntArrayFree(dst->dims); + dst->dims = TfLiteIntArrayCopy(src->dims); + memcpy(dst->data.raw, src->data.raw, src->bytes); + dst->buffer_handle = src->buffer_handle; + dst->data_is_stale = src->data_is_stale; + dst->delegate = src->delegate; + + return kTfLiteOk; +} + void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor) { if (tensor->allocation_type != kTfLiteDynamic && tensor->allocation_type != kTfLitePersistentRo) { diff --git a/code/components/tfmicro/tensorflow/lite/c/common.h b/code/components/tflite-lib/tensorflow/lite/c/common.h similarity index 95% rename from code/components/tfmicro/tensorflow/lite/c/common.h rename to code/components/tflite-lib/tensorflow/lite/c/common.h index e8c89429..7056d1e2 100644 --- a/code/components/tfmicro/tensorflow/lite/c/common.h +++ b/code/components/tflite-lib/tensorflow/lite/c/common.h @@ -80,12 +80,16 @@ typedef struct TfLiteExternalContext { // indices typedef struct TfLiteIntArray { int size; -// gcc 6.1+ have a bug where flexible members aren't properly handled -// https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c -#if (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \ - __GNUC_MINOR__ >= 1) || \ - defined(HEXAGON) || \ + +#if defined(_MSC_VER) + // Context for why this is needed is in http://b/189926408#comment21 + int data[1]; +#elif (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \ + __GNUC_MINOR__ >= 1) || \ + defined(HEXAGON) || \ (defined(__clang__) && __clang_major__ == 7 && __clang_minor__ == 1) + // gcc 6.1+ have a bug where flexible members aren't properly handled + // https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c int data[0]; #else int data[]; @@ -94,7 +98,7 @@ typedef struct TfLiteIntArray { // Given the size (number of elements) in a TfLiteIntArray, calculate its size // in bytes. -int TfLiteIntArrayGetSizeInBytes(int size); +size_t TfLiteIntArrayGetSizeInBytes(int size); #ifndef TF_LITE_STATIC_MEMORY // Create a array of a given `size` (uninitialized entries). @@ -121,11 +125,15 @@ void TfLiteIntArrayFree(TfLiteIntArray* a); // Fixed size list of floats. Used for per-channel quantization. typedef struct TfLiteFloatArray { int size; -// gcc 6.1+ have a bug where flexible members aren't properly handled -// https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c -// This also applies to the toolchain used for Qualcomm Hexagon DSPs. -#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \ - __GNUC_MINOR__ >= 1 +#if defined(_MSC_VER) + // Context for why this is needed is in http://b/189926408#comment21 + float data[1]; +#elif (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \ + __GNUC_MINOR__ >= 1) || \ + defined(HEXAGON) || \ + (defined(__clang__) && __clang_major__ == 7 && __clang_minor__ == 1) + // gcc 6.1+ have a bug where flexible members aren't properly handled + // https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c float data[0]; #else float data[]; @@ -562,6 +570,10 @@ typedef struct TfLiteNode { // Outputs to this node expressed as indices into the simulator's tensors. TfLiteIntArray* outputs; + // intermediate tensors to this node expressed as indices into the simulator's + // tensors. + TfLiteIntArray* intermediates; + // Opaque data provided by the node implementer through `Registration.init`. void* user_data; @@ -614,6 +626,16 @@ void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims, const void* allocation, bool is_variable, TfLiteTensor* tensor); +// Copies the contents of 'src' in 'dst'. +// Function does nothing if either 'src' or 'dst' is passed as nullptr and +// return kTfLiteOk. +// Returns kTfLiteError if 'src' and 'dst' doesn't have matching data size. +// Note function copies contents, so it won't create new data pointer +// or change allocation type. +// All Tensor related properties will be copied from 'src' to 'dst' like +// quantization, sparsity, ... +TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst); + // Resize the allocated data of a (dynamic) tensor. Tensors with allocation // types other than kTfLiteDynamic will be ignored. void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor); @@ -819,6 +841,9 @@ typedef struct TfLiteContext { typedef struct TfLiteRegistration { // Initializes the op from serialized data. + // Called only *once* for the lifetime of the op, so any one-time allocations + // should be made here (unless they depend on tensor sizes). + // // If a built-in op: // `buffer` is the op's params data (TfLiteLSTMParams*). // `length` is zero. @@ -841,6 +866,7 @@ typedef struct TfLiteRegistration { // prepare is called when the inputs this node depends on have been resized. // context->ResizeTensor() can be called to request output tensors to be // resized. + // Can be called multiple times for the lifetime of the op. // // Returns kTfLiteOk on success. TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node); diff --git a/code/components/tfmicro/tensorflow/lite/core/api/error_reporter.cc b/code/components/tflite-lib/tensorflow/lite/core/api/error_reporter.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/core/api/error_reporter.cc rename to code/components/tflite-lib/tensorflow/lite/core/api/error_reporter.cc diff --git a/code/components/tfmicro/tensorflow/lite/core/api/error_reporter.h b/code/components/tflite-lib/tensorflow/lite/core/api/error_reporter.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/core/api/error_reporter.h rename to code/components/tflite-lib/tensorflow/lite/core/api/error_reporter.h diff --git a/code/components/tfmicro/tensorflow/lite/core/api/flatbuffer_conversions.cc b/code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.cc similarity index 90% rename from code/components/tfmicro/tensorflow/lite/core/api/flatbuffer_conversions.cc rename to code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.cc index 5eee4406..dfa0ccfd 100644 --- a/code/components/tfmicro/tensorflow/lite/core/api/flatbuffer_conversions.cc +++ b/code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.cc @@ -1,4 +1,4 @@ -/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -131,6 +131,17 @@ TfLitePadding ConvertPadding(Padding padding) { return kTfLitePaddingUnknown; } +// Converts the flatbuffer mirror padding enum to what is used at runtime. +TfLiteMirrorPaddingMode ConvertMirrorPadding(MirrorPadMode padding) { + switch (padding) { + case MirrorPadMode_REFLECT: + return kTfLiteMirrorPaddingReflect; + case MirrorPadMode_SYMMETRIC: + return kTfLiteMirrorPaddingSymmetric; + } + return kTfLiteMirrorPaddingUnknown; +} + #ifndef TF_LITE_STATIC_MEMORY TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, ErrorReporter* error_reporter, @@ -181,6 +192,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, return ParseArgMin(op, error_reporter, allocator, builtin_data); } + case BuiltinOperator_ASSIGN_VARIABLE: { + return ParseAssignVariable(op, error_reporter, allocator, builtin_data); + } + case BuiltinOperator_AVERAGE_POOL_2D: { return ParsePool(op, error_reporter, allocator, builtin_data); } @@ -193,6 +208,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, return ParseBatchToSpaceNd(op, error_reporter, allocator, builtin_data); } + case BuiltinOperator_CALL_ONCE: { + return ParseCallOnce(op, error_reporter, allocator, builtin_data); + } + case BuiltinOperator_CEIL: { return ParseCeil(op, error_reporter, allocator, builtin_data); } @@ -325,6 +344,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, return ParsePool(op, error_reporter, allocator, builtin_data); } + case BuiltinOperator_MIRROR_PAD: { + return ParseMirrorPad(op, error_reporter, allocator, builtin_data); + } + case BuiltinOperator_MEAN: { return ParseReducer(op, error_reporter, allocator, builtin_data); } @@ -369,6 +392,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, return ParseQuantize(op, error_reporter, allocator, builtin_data); } + case BuiltinOperator_READ_VARIABLE: { + return ParseReadVariable(op, error_reporter, allocator, builtin_data); + } + case BuiltinOperator_REDUCE_ANY: { return ParseReducer(op, error_reporter, allocator, builtin_data); } @@ -486,6 +513,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, return ParseUnpack(op, error_reporter, allocator, builtin_data); } + case BuiltinOperator_VAR_HANDLE: { + return ParseVarHandle(op, error_reporter, allocator, builtin_data); + } + case BuiltinOperator_ZEROS_LIKE: { return ParseZerosLike(op, error_reporter, allocator, builtin_data); } @@ -606,21 +637,8 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, return kTfLiteOk; } case BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM: { - auto params = - safe_allocator.Allocate(); - TF_LITE_ENSURE(error_reporter, params != nullptr); - if (const auto* seq_lstm_params = - op->builtin_options_as_UnidirectionalSequenceLSTMOptions()) { - params->activation = - ConvertActivation(seq_lstm_params->fused_activation_function()); - params->cell_clip = seq_lstm_params->cell_clip(); - params->proj_clip = seq_lstm_params->proj_clip(); - params->time_major = seq_lstm_params->time_major(); - params->asymmetric_quantize_inputs = - seq_lstm_params->asymmetric_quantize_inputs(); - } - *builtin_data = params.release(); - return kTfLiteOk; + return ParseUnidirectionalSequenceLSTM(op, error_reporter, allocator, + builtin_data); } case BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM: { auto params = @@ -693,19 +711,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, *builtin_data = params.release(); return kTfLiteOk; } - case BuiltinOperator_MIRROR_PAD: { - auto params = safe_allocator.Allocate(); - TF_LITE_ENSURE(error_reporter, params != nullptr); - const auto* mirror_pad_params = op->builtin_options_as_MirrorPadOptions(); - if (mirror_pad_params != nullptr) { - params->mode = - mirror_pad_params->mode() == tflite::MirrorPadMode_REFLECT - ? TfLiteMirrorPaddingMode::kTfLiteMirrorPaddingReflect - : TfLiteMirrorPaddingMode::kTfLiteMirrorPaddingSymmetric; - } - *builtin_data = params.release(); - return kTfLiteOk; - } case BuiltinOperator_UNIQUE: { auto params = safe_allocator.Allocate(); TF_LITE_ENSURE(error_reporter, params != nullptr); @@ -750,16 +755,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, *builtin_data = params.release(); return kTfLiteOk; } - case BuiltinOperator_CALL_ONCE: { - auto params = safe_allocator.Allocate(); - TF_LITE_ENSURE(error_reporter, params != nullptr); - if (const auto* call_once_params = - op->builtin_options_as_CallOnceOptions()) { - params->init_subgraph_index = call_once_params->init_subgraph_index(); - } - *builtin_data = params.release(); - return kTfLiteOk; - } case BuiltinOperator_CONV_3D: case BuiltinOperator_CONV_3D_TRANSPOSE: { auto params = safe_allocator.Allocate(); @@ -793,17 +788,69 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, *builtin_data = params.release(); return kTfLiteOk; } - case BuiltinOperator_VAR_HANDLE: { - auto params = safe_allocator.Allocate(); + case BuiltinOperator_MULTINOMIAL: { + auto params = safe_allocator.Allocate(); TF_LITE_ENSURE(error_reporter, params != nullptr); - params->container = nullptr; - params->shared_name = nullptr; - if (const auto* var_handle_params = - op->builtin_options_as_VarHandleOptions()) { - if (var_handle_params->container()) - params->container = var_handle_params->container()->c_str(); - if (var_handle_params->shared_name()) - params->shared_name = var_handle_params->shared_name()->c_str(); + if (const auto* multinomial_params = + op->builtin_options_as_RandomOptions()) { + params->seed = multinomial_params->seed(); + params->seed2 = multinomial_params->seed2(); + } + *builtin_data = params.release(); + return kTfLiteOk; + } + case BuiltinOperator_RANDOM_STANDARD_NORMAL: { + auto params = safe_allocator.Allocate(); + TF_LITE_ENSURE(error_reporter, params != nullptr); + if (const auto* random_std_normal_params = + op->builtin_options_as_RandomOptions()) { + params->seed = random_std_normal_params->seed(); + params->seed2 = random_std_normal_params->seed2(); + } + *builtin_data = params.release(); + return kTfLiteOk; + } + case BuiltinOperator_BUCKETIZE: { + auto params = safe_allocator.Allocate(); + TF_LITE_ENSURE(error_reporter, params != nullptr); + if (const auto* bucketize_params = + op->builtin_options_as_BucketizeOptions()) { + const flatbuffers::Vector* boundaries = + bucketize_params->boundaries(); + if (boundaries == nullptr) { + TF_LITE_REPORT_ERROR( + error_reporter, + "boundaries array not provided for operation 'bucketize'.\n"); + return kTfLiteError; + } + params->num_boundaries = boundaries->size(); + if (boundaries->data() == nullptr) { + TF_LITE_REPORT_ERROR(error_reporter, + "boundaries.data() returned nullptr for " + "operation 'bucketize'.\n"); + return kTfLiteError; + } + params->boundaries = boundaries->data(); + } + *builtin_data = params.release(); + return kTfLiteOk; + } + case BuiltinOperator_RANDOM_UNIFORM: { + auto params = safe_allocator.Allocate(); + TF_LITE_ENSURE(error_reporter, params != nullptr); + if (const auto* random_uniform_params = + op->builtin_options_as_RandomOptions()) { + params->seed = random_uniform_params->seed(); + params->seed2 = random_uniform_params->seed2(); + } + *builtin_data = params.release(); + return kTfLiteOk; + } + case BuiltinOperator_GELU: { + auto params = safe_allocator.Allocate(); + TF_LITE_ENSURE(error_reporter, params != nullptr); + if (const auto* gelu_params = op->builtin_options_as_GeluOptions()) { + params->approximate = gelu_params->approximate(); } *builtin_data = params.release(); return kTfLiteOk; @@ -844,8 +891,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type, case BuiltinOperator_HASHTABLE_FIND: case BuiltinOperator_HASHTABLE_IMPORT: case BuiltinOperator_HASHTABLE_SIZE: - case BuiltinOperator_READ_VARIABLE: - case BuiltinOperator_ASSIGN_VARIABLE: case BuiltinOperator_BROADCAST_ARGS: return kTfLiteOk; case BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES: @@ -1003,6 +1048,14 @@ TfLiteStatus ParseArgMin(const Operator* op, ErrorReporter* error_reporter, return kTfLiteOk; } +// We have this parse function instead of directly returning kTfLiteOk from the +// switch-case in ParseOpData because this function is used as part of the +// selective registration for the OpResolver implementation in micro. +TfLiteStatus ParseAssignVariable(const Operator*, ErrorReporter*, + BuiltinDataAllocator*, void**) { + return kTfLiteOk; +} + // We have this parse function instead of directly returning kTfLiteOk from the // switch-case in ParseOpData because this function is used as part of the // selective registration for the OpResolver implementation in micro. @@ -1032,6 +1085,33 @@ TfLiteStatus ParseBatchToSpaceNd(const Operator*, ErrorReporter*, return kTfLiteOk; } +TfLiteStatus ParseCallOnce(const Operator* op, ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data) { + CheckParsePointerParams(op, error_reporter, allocator, builtin_data); + + SafeBuiltinDataAllocator safe_allocator(allocator); + std::unique_ptr + params = safe_allocator.Allocate(); + TF_LITE_ENSURE(error_reporter, params != nullptr); + + const CallOnceOptions* schema_params = + op->builtin_options_as_CallOnceOptions(); + + if (schema_params != nullptr) { + params->init_subgraph_index = schema_params->init_subgraph_index(); + + } else { + // TODO(b/157480169): We should either return kTfLiteError or fill in some + // reasonable defaults in the params struct. We are not doing so until we + // better undertand the ramifications of changing the legacy behavior. + } + + *builtin_data = params.release(); + return kTfLiteOk; +} + // We have this parse function instead of directly returning kTfLiteOk from the // switch-case in ParseOpData because this function is used as part of the // selective registration for the OpResolver implementation in micro. @@ -1541,6 +1621,32 @@ TfLiteStatus ParseMinimum(const Operator*, ErrorReporter*, return kTfLiteOk; } +TfLiteStatus ParseMirrorPad(const Operator* op, ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data) { + CheckParsePointerParams(op, error_reporter, allocator, builtin_data); + + SafeBuiltinDataAllocator safe_allocator(allocator); + std::unique_ptr + params = safe_allocator.Allocate(); + TF_LITE_ENSURE(error_reporter, params != nullptr); + + const MirrorPadOptions* schema_params = + op->builtin_options_as_MirrorPadOptions(); + + if (schema_params != nullptr) { + params->mode = ConvertMirrorPadding(schema_params->mode()); + } else { + // TODO(b/157480169): We should either return kTfLiteError or fill in some + // reasonable defaults in the params struct. We are not doing so until we + // better undertand the ramifications of changing the legacy behavior. + } + + *builtin_data = params.release(); + return kTfLiteOk; +} + TfLiteStatus ParseMul(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data) { CheckParsePointerParams(op, error_reporter, allocator, builtin_data); @@ -1676,6 +1782,14 @@ TfLiteStatus ParseQuantize(const Operator*, ErrorReporter*, return kTfLiteOk; } +// We have this parse function instead of directly returning kTfLiteOk from the +// switch-case in ParseOpData because this function is used as part of the +// selective registration for the OpResolver implementation in micro. +TfLiteStatus ParseReadVariable(const Operator*, ErrorReporter*, + BuiltinDataAllocator*, void**) { + return kTfLiteOk; +} + TfLiteStatus ParseReducer(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data) { @@ -1856,6 +1970,14 @@ TfLiteStatus ParseSin(const Operator*, ErrorReporter*, BuiltinDataAllocator*, return kTfLiteOk; } +// We have this parse function instead of directly returning kTfLiteOk from the +// switch-case in ParseOpData because this function is used as part of the +// selective registration for the OpResolver implementation in micro. +TfLiteStatus ParseSlice(const Operator*, ErrorReporter*, BuiltinDataAllocator*, + void**) { + return kTfLiteOk; +} + TfLiteStatus ParseSoftmax(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data) { @@ -1962,6 +2084,29 @@ TfLiteStatus ParseSplitV(const Operator* op, ErrorReporter* error_reporter, return kTfLiteOk; } +TfLiteStatus ParseUnidirectionalSequenceLSTM(const Operator* op, + ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data) { + CheckParsePointerParams(op, error_reporter, allocator, builtin_data); + SafeBuiltinDataAllocator safe_allocator(allocator); + auto params = + safe_allocator.Allocate(); + TF_LITE_ENSURE(error_reporter, params != nullptr); + if (const auto* seq_lstm_params = + op->builtin_options_as_UnidirectionalSequenceLSTMOptions()) { + params->activation = + ConvertActivation(seq_lstm_params->fused_activation_function()); + params->cell_clip = seq_lstm_params->cell_clip(); + params->proj_clip = seq_lstm_params->proj_clip(); + params->time_major = seq_lstm_params->time_major(); + params->asymmetric_quantize_inputs = + seq_lstm_params->asymmetric_quantize_inputs(); + } + *builtin_data = params.release(); + return kTfLiteOk; +} + TfLiteStatus ParseSqueeze(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data) { @@ -2161,6 +2306,37 @@ TfLiteStatus ParseUnpack(const Operator* op, ErrorReporter* error_reporter, return kTfLiteOk; } +TfLiteStatus ParseVarHandle(const Operator* op, ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data) { + CheckParsePointerParams(op, error_reporter, allocator, builtin_data); + + SafeBuiltinDataAllocator safe_allocator(allocator); + std::unique_ptr + params = safe_allocator.Allocate(); + TF_LITE_ENSURE(error_reporter, params != nullptr); + + const VarHandleOptions* schema_params = + op->builtin_options_as_VarHandleOptions(); + + if (schema_params != nullptr) { + if (schema_params->container()) { + params->container = schema_params->container()->c_str(); + } + if (schema_params->shared_name()) { + params->shared_name = schema_params->shared_name()->c_str(); + } + } else { + // TODO(b/157480169): We should either return kTfLiteError or fill in some + // reasonable defaults in the params struct. We are not doing so until we + // better undertand the ramifications of changing the legacy behavior. + } + + *builtin_data = params.release(); + return kTfLiteOk; +} + // We have this parse function instead of directly returning kTfLiteOk from the // switch-case in ParseOpData because this function is used as part of the // selective registration for the OpResolver implementation in micro. diff --git a/code/components/tfmicro/tensorflow/lite/core/api/flatbuffer_conversions.h b/code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.h similarity index 91% rename from code/components/tfmicro/tensorflow/lite/core/api/flatbuffer_conversions.h rename to code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.h index 263a839b..8cf889d8 100644 --- a/code/components/tfmicro/tensorflow/lite/core/api/flatbuffer_conversions.h +++ b/code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.h @@ -1,4 +1,4 @@ -/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -84,6 +84,11 @@ TfLiteStatus ParseArgMax(const Operator* op, ErrorReporter* error_reporter, TfLiteStatus ParseArgMin(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); +TfLiteStatus ParseAssignVariable(const Operator* op, + ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data); + TfLiteStatus ParseBatchMatMul(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); @@ -93,6 +98,10 @@ TfLiteStatus ParseBatchToSpaceNd(const Operator* op, BuiltinDataAllocator* allocator, void** builtin_data); +TfLiteStatus ParseCallOnce(const Operator* op, ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data); + TfLiteStatus ParseCeil(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); @@ -229,6 +238,10 @@ TfLiteStatus ParseMaximum(const Operator* op, ErrorReporter* error_reporter, TfLiteStatus ParseMinimum(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); +TfLiteStatus ParseMirrorPad(const Operator* op, ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data); + TfLiteStatus ParseMul(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); @@ -261,6 +274,11 @@ TfLiteStatus ParseQuantize(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); +TfLiteStatus ParseReadVariable(const Operator* op, + ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data); + TfLiteStatus ParseReducer(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); @@ -295,6 +313,9 @@ TfLiteStatus ParseShape(const Operator* op, ErrorReporter* error_reporter, TfLiteStatus ParseSin(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); +TfLiteStatus ParseSlice(const Operator* op, ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, void** builtin_data); + TfLiteStatus ParseSoftmax(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); @@ -349,6 +370,15 @@ TfLiteStatus ParseTransposeConv(const Operator* op, TfLiteStatus ParseUnpack(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); +TfLiteStatus ParseUnidirectionalSequenceLSTM(const Operator* op, + ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data); + +TfLiteStatus ParseVarHandle(const Operator* op, ErrorReporter* error_reporter, + BuiltinDataAllocator* allocator, + void** builtin_data); + TfLiteStatus ParseZerosLike(const Operator* op, ErrorReporter* error_reporter, BuiltinDataAllocator* allocator, void** builtin_data); diff --git a/code/components/tfmicro/tensorflow/lite/core/api/op_resolver.cc b/code/components/tflite-lib/tensorflow/lite/core/api/op_resolver.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/core/api/op_resolver.cc rename to code/components/tflite-lib/tensorflow/lite/core/api/op_resolver.cc diff --git a/code/components/tfmicro/tensorflow/lite/core/api/op_resolver.h b/code/components/tflite-lib/tensorflow/lite/core/api/op_resolver.h similarity index 82% rename from code/components/tfmicro/tensorflow/lite/core/api/op_resolver.h rename to code/components/tflite-lib/tensorflow/lite/core/api/op_resolver.h index 471db813..49ac778e 100644 --- a/code/components/tfmicro/tensorflow/lite/core/api/op_resolver.h +++ b/code/components/tflite-lib/tensorflow/lite/core/api/op_resolver.h @@ -15,6 +15,7 @@ limitations under the License. #ifndef TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_ #define TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_ +#include #include #include @@ -36,15 +37,26 @@ class OpResolver { virtual const TfLiteRegistration* FindOp(const char* op, int version) const = 0; + using TfLiteDelegatePtrVector = + std::vector>; // Returns optional delegates for resolving and handling ops in the flatbuffer // model. This may be used in addition to the standard TfLiteRegistration // lookup for graph resolution. - using TfLiteDelegatePtrVector = - std::vector>; + // WARNING: This API is deprecated, GetDelegateCreators is preferred. virtual TfLiteDelegatePtrVector GetDelegates(int num_threads) const { - return TfLiteDelegatePtrVector(); + return {}; } + // Represent a function that creates a TfLite delegate instance. + using TfLiteDelegateCreator = + std::function( + int /*num_threads*/)>; + using TfLiteDelegateCreators = std::vector; + // Returns a vector of delegate creators to create optional delegates for + // resolving and handling ops in the flatbuffer model. This may be used in + // addition to the standard TfLiteRegistration lookup for graph resolution. + virtual TfLiteDelegateCreators GetDelegateCreators() const { return {}; } + virtual ~OpResolver() {} private: diff --git a/code/components/tfmicro/tensorflow/lite/core/api/tensor_utils.cc b/code/components/tflite-lib/tensorflow/lite/core/api/tensor_utils.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/core/api/tensor_utils.cc rename to code/components/tflite-lib/tensorflow/lite/core/api/tensor_utils.cc diff --git a/code/components/tfmicro/tensorflow/lite/core/api/tensor_utils.h b/code/components/tflite-lib/tensorflow/lite/core/api/tensor_utils.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/core/api/tensor_utils.h rename to code/components/tflite-lib/tensorflow/lite/core/api/tensor_utils.h diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/bits.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/bits.h new file mode 100644 index 00000000..04b3ba6f --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/bits.h @@ -0,0 +1,102 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_ + +#ifdef __cplusplus +#include + +extern "C" { +#endif + +static inline int CountLeadingZeros32Slow(uint64_t n) { + int zeroes = 28; + if (n >> 16) zeroes -= 16, n >>= 16; + if (n >> 8) zeroes -= 8, n >>= 8; + if (n >> 4) zeroes -= 4, n >>= 4; + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; +} + +static inline int CountLeadingZeros32(uint32_t n) { +#if defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse(&result, n)) { + return 31 - result; + } + return 32; +#elif defined(__GNUC__) + + // Handle 0 as a special case because __builtin_clz(0) is undefined. + if (n == 0) { + return 32; + } + return __builtin_clz(n); +#else + return CountLeadingZeros32Slow(n); +#endif +} + +static inline int MostSignificantBit32(uint32_t n) { + return 32 - CountLeadingZeros32(n); +} + +static inline int CountLeadingZeros64Slow(uint64_t n) { + int zeroes = 60; + if (n >> 32) zeroes -= 32, n >>= 32; + if (n >> 16) zeroes -= 16, n >>= 16; + if (n >> 8) zeroes -= 8, n >>= 8; + if (n >> 4) zeroes -= 4, n >>= 4; + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; +} + +static inline int CountLeadingZeros64(uint64_t n) { +#if defined(_MSC_VER) && defined(_M_X64) + // MSVC does not have __builtin_clzll. Use _BitScanReverse64. + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse64(&result, n)) { + return 63 - result; + } + return 64; +#elif defined(_MSC_VER) + // MSVC does not have __builtin_clzll. Compose two calls to _BitScanReverse + unsigned long result = 0; // NOLINT(runtime/int) + if ((n >> 32) && _BitScanReverse(&result, n >> 32)) { + return 31 - result; + } + if (_BitScanReverse(&result, n)) { + return 63 - result; + } + return 64; +#elif defined(__GNUC__) + + // Handle 0 as a special case because __builtin_clzll(0) is undefined. + if (n == 0) { + return 64; + } + return __builtin_clzll(n); +#else + return CountLeadingZeros64Slow(n); +#endif +} + +static inline int MostSignificantBit64(uint64_t n) { + return 64 - CountLeadingZeros64(n); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft.cc b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft.cc new file mode 100644 index 00000000..62442fba --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft.cc @@ -0,0 +1,52 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/fft.h" +#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h" + +#include + + +void FftCompute(struct FftState* state, const int16_t* input, + int input_scale_shift) { + const size_t input_size = state->input_size; + const size_t fft_size = state->fft_size; + + int16_t* fft_input = state->input; + // First, scale the input by the given shift. + size_t i; + for (i = 0; i < input_size; ++i) { + fft_input[i] = static_cast(static_cast(input[i]) + << input_scale_shift); + } + // Zero out whatever else remains in the top part of the input. + for (; i < fft_size; ++i) { + fft_input[i] = 0; + } + + // Apply the FFT. + kissfft_fixed16::kiss_fftr( + reinterpret_cast(state->scratch), + state->input, + reinterpret_cast(state->output)); +} + +void FftInit(struct FftState* state) { + // All the initialization is done in FftPopulateState() +} + +void FftReset(struct FftState* state) { + memset(state->input, 0, state->fft_size * sizeof(*state->input)); + memset(state->output, 0, (state->fft_size / 2 + 1) * sizeof(*state->output)); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft.h new file mode 100644 index 00000000..aaffa69d --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft.h @@ -0,0 +1,50 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct complex_int16_t { + int16_t real; + int16_t imag; +}; + +struct FftState { + int16_t* input; + struct complex_int16_t* output; + size_t fft_size; + size_t input_size; + void* scratch; + size_t scratch_size; +}; + +void FftCompute(struct FftState* state, const int16_t* input, + int input_scale_shift); + +void FftInit(struct FftState* state); + +void FftReset(struct FftState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft_util.cc b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft_util.cc new file mode 100644 index 00000000..81efe14d --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft_util.cc @@ -0,0 +1,69 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/fft_util.h" +#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h" + +#include + +int FftPopulateState(struct FftState* state, size_t input_size) { + state->input_size = input_size; + state->fft_size = 1; + while (state->fft_size < state->input_size) { + state->fft_size <<= 1; + } + + state->input = reinterpret_cast( + malloc(state->fft_size * sizeof(*state->input))); + if (state->input == nullptr) { + fprintf(stderr, "Failed to alloc fft input buffer\n"); + return 0; + } + + state->output = reinterpret_cast( + malloc((state->fft_size / 2 + 1) * sizeof(*state->output) * 2)); + if (state->output == nullptr) { + fprintf(stderr, "Failed to alloc fft output buffer\n"); + return 0; + } + + // Ask kissfft how much memory it wants. + size_t scratch_size = 0; + kissfft_fixed16::kiss_fftr_cfg kfft_cfg = kissfft_fixed16::kiss_fftr_alloc( + state->fft_size, 0, nullptr, &scratch_size); + if (kfft_cfg != nullptr) { + fprintf(stderr, "Kiss memory sizing failed.\n"); + return 0; + } + state->scratch = malloc(scratch_size); + if (state->scratch == nullptr) { + fprintf(stderr, "Failed to alloc fft scratch buffer\n"); + return 0; + } + state->scratch_size = scratch_size; + // Let kissfft configure the scratch space we just allocated + kfft_cfg = kissfft_fixed16::kiss_fftr_alloc(state->fft_size, 0, + state->scratch, &scratch_size); + if (kfft_cfg != state->scratch) { + fprintf(stderr, "Kiss memory preallocation strategy failed.\n"); + return 0; + } + return 1; +} + +void FftFreeStateContents(struct FftState* state) { + free(state->input); + free(state->output); + free(state->scratch); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft_util.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft_util.h new file mode 100644 index 00000000..6a471301 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/fft_util.h @@ -0,0 +1,34 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_ + +#include "tensorflow/lite/experimental/microfrontend/lib/fft.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Prepares and FFT for the given input size. +int FftPopulateState(struct FftState* state, size_t input_size); + +// Frees any allocated buffers. +void FftFreeStateContents(struct FftState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank.c new file mode 100644 index 00000000..80f8738f --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank.c @@ -0,0 +1,134 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h" + +#include + +#include "tensorflow/lite/experimental/microfrontend/lib/bits.h" + +void FilterbankConvertFftComplexToEnergy(struct FilterbankState* state, + struct complex_int16_t* fft_output, + int32_t* energy) { + const int end_index = state->end_index; + int i; + energy += state->start_index; + fft_output += state->start_index; + for (i = state->start_index; i < end_index; ++i) { + const int32_t real = fft_output->real; + const int32_t imag = fft_output->imag; + fft_output++; + const uint32_t mag_squared = (real * real) + (imag * imag); + *energy++ = mag_squared; + } +} + +void FilterbankAccumulateChannels(struct FilterbankState* state, + const int32_t* energy) { + uint64_t* work = state->work; + uint64_t weight_accumulator = 0; + uint64_t unweight_accumulator = 0; + + const int16_t* channel_frequency_starts = state->channel_frequency_starts; + const int16_t* channel_weight_starts = state->channel_weight_starts; + const int16_t* channel_widths = state->channel_widths; + + int num_channels_plus_1 = state->num_channels + 1; + int i; + for (i = 0; i < num_channels_plus_1; ++i) { + const int32_t* magnitudes = energy + *channel_frequency_starts++; + const int16_t* weights = state->weights + *channel_weight_starts; + const int16_t* unweights = state->unweights + *channel_weight_starts++; + const int width = *channel_widths++; + int j; + for (j = 0; j < width; ++j) { + weight_accumulator += *weights++ * ((uint64_t)*magnitudes); + unweight_accumulator += *unweights++ * ((uint64_t)*magnitudes); + ++magnitudes; + } + *work++ = weight_accumulator; + weight_accumulator = unweight_accumulator; + unweight_accumulator = 0; + } +} + +static uint16_t Sqrt32(uint32_t num) { + if (num == 0) { + return 0; + } + uint32_t res = 0; + int max_bit_number = 32 - MostSignificantBit32(num); + max_bit_number |= 1; + uint32_t bit = 1U << (31 - max_bit_number); + int iterations = (31 - max_bit_number) / 2 + 1; + while (iterations--) { + if (num >= res + bit) { + num -= res + bit; + res = (res >> 1U) + bit; + } else { + res >>= 1U; + } + bit >>= 2U; + } + // Do rounding - if we have the bits. + if (num > res && res != 0xFFFF) { + ++res; + } + return res; +} + +static uint32_t Sqrt64(uint64_t num) { + // Take a shortcut and just use 32 bit operations if the upper word is all + // clear. This will cause a slight off by one issue for numbers close to 2^32, + // but it probably isn't going to matter (and gives us a big performance win). + if ((num >> 32) == 0) { + return Sqrt32((uint32_t)num); + } + uint64_t res = 0; + int max_bit_number = 64 - MostSignificantBit64(num); + max_bit_number |= 1; + uint64_t bit = 1ULL << (63 - max_bit_number); + int iterations = (63 - max_bit_number) / 2 + 1; + while (iterations--) { + if (num >= res + bit) { + num -= res + bit; + res = (res >> 1U) + bit; + } else { + res >>= 1U; + } + bit >>= 2U; + } + // Do rounding - if we have the bits. + if (num > res && res != 0xFFFFFFFFLL) { + ++res; + } + return res; +} + +uint32_t* FilterbankSqrt(struct FilterbankState* state, int scale_down_shift) { + const int num_channels = state->num_channels; + const uint64_t* work = state->work + 1; + // Reuse the work buffer since we're fine clobbering it at this point to hold + // the output. + uint32_t* output = (uint32_t*)state->work; + int i; + for (i = 0; i < num_channels; ++i) { + *output++ = Sqrt64(*work++) >> scale_down_shift; + } + return (uint32_t*)state->work; +} + +void FilterbankReset(struct FilterbankState* state) { + memset(state->work, 0, (state->num_channels + 1) * sizeof(*state->work)); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank.h new file mode 100644 index 00000000..1e6d3885 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank.h @@ -0,0 +1,63 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_ + +#include +#include + +#include "tensorflow/lite/experimental/microfrontend/lib/fft.h" + +#define kFilterbankBits 12 + +#ifdef __cplusplus +extern "C" { +#endif + +struct FilterbankState { + int num_channels; + int start_index; + int end_index; + int16_t* channel_frequency_starts; + int16_t* channel_weight_starts; + int16_t* channel_widths; + int16_t* weights; + int16_t* unweights; + uint64_t* work; +}; + +// Converts the relevant complex values of an FFT output into energy (the +// square magnitude). +void FilterbankConvertFftComplexToEnergy(struct FilterbankState* state, + struct complex_int16_t* fft_output, + int32_t* energy); + +// Computes the mel-scale filterbank on the given energy array. Output is cached +// internally - to fetch it, you need to call FilterbankSqrt. +void FilterbankAccumulateChannels(struct FilterbankState* state, + const int32_t* energy); + +// Applies an integer square root to the 64 bit intermediate values of the +// filterbank, and returns a pointer to them. Memory will be invalidated the +// next time FilterbankAccumulateChannels is called. +uint32_t* FilterbankSqrt(struct FilterbankState* state, int scale_down_shift); + +void FilterbankReset(struct FilterbankState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank_util.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank_util.c new file mode 100644 index 00000000..f18ebf54 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank_util.c @@ -0,0 +1,220 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h" + +#include +#include +#include + +#define kFilterbankIndexAlignment 4 +#define kFilterbankChannelBlockSize 4 + +void FilterbankFillConfigWithDefaults(struct FilterbankConfig* config) { + config->num_channels = 32; + config->lower_band_limit = 125.0f; + config->upper_band_limit = 7500.0f; + config->output_scale_shift = 7; +} + +static float FreqToMel(float freq) { return 1127.0 * log1p(freq / 700.0); } + +static void CalculateCenterFrequencies(const int num_channels, + const float lower_frequency_limit, + const float upper_frequency_limit, + float* center_frequencies) { + assert(lower_frequency_limit >= 0.0f); + assert(upper_frequency_limit > lower_frequency_limit); + + const float mel_low = FreqToMel(lower_frequency_limit); + const float mel_hi = FreqToMel(upper_frequency_limit); + const float mel_span = mel_hi - mel_low; + const float mel_spacing = mel_span / ((float)num_channels); + int i; + for (i = 0; i < num_channels; ++i) { + center_frequencies[i] = mel_low + (mel_spacing * (i + 1)); + } +} + +static void QuantizeFilterbankWeights(const float float_weight, int16_t* weight, + int16_t* unweight) { + *weight = floor(float_weight * (1 << kFilterbankBits) + 0.5); + *unweight = floor((1.0 - float_weight) * (1 << kFilterbankBits) + 0.5); +} + +int FilterbankPopulateState(const struct FilterbankConfig* config, + struct FilterbankState* state, int sample_rate, + int spectrum_size) { + state->num_channels = config->num_channels; + const int num_channels_plus_1 = config->num_channels + 1; + + // How should we align things to index counts given the byte alignment? + const int index_alignment = + (kFilterbankIndexAlignment < sizeof(int16_t) + ? 1 + : kFilterbankIndexAlignment / sizeof(int16_t)); + + state->channel_frequency_starts = + malloc(num_channels_plus_1 * sizeof(*state->channel_frequency_starts)); + state->channel_weight_starts = + malloc(num_channels_plus_1 * sizeof(*state->channel_weight_starts)); + state->channel_widths = + malloc(num_channels_plus_1 * sizeof(*state->channel_widths)); + state->work = malloc(num_channels_plus_1 * sizeof(*state->work)); + + float* center_mel_freqs = + malloc(num_channels_plus_1 * sizeof(*center_mel_freqs)); + int16_t* actual_channel_starts = + malloc(num_channels_plus_1 * sizeof(*actual_channel_starts)); + int16_t* actual_channel_widths = + malloc(num_channels_plus_1 * sizeof(*actual_channel_widths)); + + if (state->channel_frequency_starts == NULL || + state->channel_weight_starts == NULL || state->channel_widths == NULL || + center_mel_freqs == NULL || actual_channel_starts == NULL || + actual_channel_widths == NULL) { + free(center_mel_freqs); + free(actual_channel_starts); + free(actual_channel_widths); + fprintf(stderr, "Failed to allocate channel buffers\n"); + return 0; + } + + CalculateCenterFrequencies(num_channels_plus_1, config->lower_band_limit, + config->upper_band_limit, center_mel_freqs); + + // Always exclude DC. + const float hz_per_sbin = 0.5 * sample_rate / ((float)spectrum_size - 1); + state->start_index = 1.5 + config->lower_band_limit / hz_per_sbin; + state->end_index = 0; // Initialized to zero here, but actually set below. + + // For each channel, we need to figure out what frequencies belong to it, and + // how much padding we need to add so that we can efficiently multiply the + // weights and unweights for accumulation. To simplify the multiplication + // logic, all channels will have some multiplication to do (even if there are + // no frequencies that accumulate to that channel) - they will be directed to + // a set of zero weights. + int chan_freq_index_start = state->start_index; + int weight_index_start = 0; + int needs_zeros = 0; + + int chan; + for (chan = 0; chan < num_channels_plus_1; ++chan) { + // Keep jumping frequencies until we overshoot the bound on this channel. + int freq_index = chan_freq_index_start; + while (FreqToMel((freq_index)*hz_per_sbin) <= center_mel_freqs[chan]) { + ++freq_index; + } + + const int width = freq_index - chan_freq_index_start; + actual_channel_starts[chan] = chan_freq_index_start; + actual_channel_widths[chan] = width; + + if (width == 0) { + // This channel doesn't actually get anything from the frequencies, it's + // always zero. We need then to insert some 'zero' weights into the + // output, and just redirect this channel to do a single multiplication at + // this point. For simplicity, the zeros are placed at the beginning of + // the weights arrays, so we have to go and update all the other + // weight_starts to reflect this shift (but only once). + state->channel_frequency_starts[chan] = 0; + state->channel_weight_starts[chan] = 0; + state->channel_widths[chan] = kFilterbankChannelBlockSize; + if (!needs_zeros) { + needs_zeros = 1; + int j; + for (j = 0; j < chan; ++j) { + state->channel_weight_starts[j] += kFilterbankChannelBlockSize; + } + weight_index_start += kFilterbankChannelBlockSize; + } + } else { + // How far back do we need to go to ensure that we have the proper + // alignment? + const int aligned_start = + (chan_freq_index_start / index_alignment) * index_alignment; + const int aligned_width = (chan_freq_index_start - aligned_start + width); + const int padded_width = + (((aligned_width - 1) / kFilterbankChannelBlockSize) + 1) * + kFilterbankChannelBlockSize; + + state->channel_frequency_starts[chan] = aligned_start; + state->channel_weight_starts[chan] = weight_index_start; + state->channel_widths[chan] = padded_width; + weight_index_start += padded_width; + } + chan_freq_index_start = freq_index; + } + + // Allocate the two arrays to store the weights - weight_index_start contains + // the index of what would be the next set of weights that we would need to + // add, so that's how many weights we need to allocate. + state->weights = calloc(weight_index_start, sizeof(*state->weights)); + state->unweights = calloc(weight_index_start, sizeof(*state->unweights)); + + // If the alloc failed, we also need to nuke the arrays. + if (state->weights == NULL || state->unweights == NULL) { + free(center_mel_freqs); + free(actual_channel_starts); + free(actual_channel_widths); + fprintf(stderr, "Failed to allocate weights or unweights\n"); + return 0; + } + + // Next pass, compute all the weights. Since everything has been memset to + // zero, we only need to fill in the weights that correspond to some frequency + // for a channel. + const float mel_low = FreqToMel(config->lower_band_limit); + for (chan = 0; chan < num_channels_plus_1; ++chan) { + int frequency = actual_channel_starts[chan]; + const int num_frequencies = actual_channel_widths[chan]; + const int frequency_offset = + frequency - state->channel_frequency_starts[chan]; + const int weight_start = state->channel_weight_starts[chan]; + const float denom_val = (chan == 0) ? mel_low : center_mel_freqs[chan - 1]; + + int j; + for (j = 0; j < num_frequencies; ++j, ++frequency) { + const float weight = + (center_mel_freqs[chan] - FreqToMel(frequency * hz_per_sbin)) / + (center_mel_freqs[chan] - denom_val); + + // Make the float into an integer for the weights (and unweights). + const int weight_index = weight_start + frequency_offset + j; + QuantizeFilterbankWeights(weight, state->weights + weight_index, + state->unweights + weight_index); + } + if (frequency > state->end_index) { + state->end_index = frequency; + } + } + + free(center_mel_freqs); + free(actual_channel_starts); + free(actual_channel_widths); + if (state->end_index >= spectrum_size) { + fprintf(stderr, "Filterbank end_index is above spectrum size.\n"); + return 0; + } + return 1; +} + +void FilterbankFreeStateContents(struct FilterbankState* state) { + free(state->channel_frequency_starts); + free(state->channel_weight_starts); + free(state->channel_widths); + free(state->weights); + free(state->unweights); + free(state->work); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h new file mode 100644 index 00000000..781d1024 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h @@ -0,0 +1,50 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_ + +#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct FilterbankConfig { + // number of frequency channel buckets for filterbank + int num_channels; + // maximum frequency to include + float upper_band_limit; + // minimum frequency to include + float lower_band_limit; + // unused + int output_scale_shift; +}; + +// Fills the frontendConfig with "sane" defaults. +void FilterbankFillConfigWithDefaults(struct FilterbankConfig* config); + +// Allocates any buffers. +int FilterbankPopulateState(const struct FilterbankConfig* config, + struct FilterbankState* state, int sample_rate, + int spectrum_size); + +// Frees any allocated buffers. +void FilterbankFreeStateContents(struct FilterbankState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend.c new file mode 100644 index 00000000..9de2a879 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend.c @@ -0,0 +1,72 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h" + +#include "tensorflow/lite/experimental/microfrontend/lib/bits.h" + +struct FrontendOutput FrontendProcessSamples(struct FrontendState* state, + const int16_t* samples, + size_t num_samples, + size_t* num_samples_read) { + struct FrontendOutput output; + output.values = NULL; + output.size = 0; + + // Try to apply the window - if it fails, return and wait for more data. + if (!WindowProcessSamples(&state->window, samples, num_samples, + num_samples_read)) { + return output; + } + + // Apply the FFT to the window's output (and scale it so that the fixed point + // FFT can have as much resolution as possible). + int input_shift = + 15 - MostSignificantBit32(state->window.max_abs_output_value); + FftCompute(&state->fft, state->window.output, input_shift); + + // We can re-ruse the fft's output buffer to hold the energy. + int32_t* energy = (int32_t*)state->fft.output; + + FilterbankConvertFftComplexToEnergy(&state->filterbank, state->fft.output, + energy); + + FilterbankAccumulateChannels(&state->filterbank, energy); + uint32_t* scaled_filterbank = FilterbankSqrt(&state->filterbank, input_shift); + + // Apply noise reduction. + NoiseReductionApply(&state->noise_reduction, scaled_filterbank); + + if (state->pcan_gain_control.enable_pcan) { + PcanGainControlApply(&state->pcan_gain_control, scaled_filterbank); + } + + // Apply the log and scale. + int correction_bits = + MostSignificantBit32(state->fft.fft_size) - 1 - (kFilterbankBits / 2); + uint16_t* logged_filterbank = + LogScaleApply(&state->log_scale, scaled_filterbank, + state->filterbank.num_channels, correction_bits); + + output.size = state->filterbank.num_channels; + output.values = logged_filterbank; + return output; +} + +void FrontendReset(struct FrontendState* state) { + WindowReset(&state->window); + FftReset(&state->fft); + FilterbankReset(&state->filterbank); + NoiseReductionReset(&state->noise_reduction); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend.h new file mode 100644 index 00000000..883df5fd --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend.h @@ -0,0 +1,64 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_ + +#include +#include + +#include "tensorflow/lite/experimental/microfrontend/lib/fft.h" +#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h" +#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h" +#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h" +#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h" +#include "tensorflow/lite/experimental/microfrontend/lib/window.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct FrontendState { + struct WindowState window; + struct FftState fft; + struct FilterbankState filterbank; + struct NoiseReductionState noise_reduction; + struct PcanGainControlState pcan_gain_control; + struct LogScaleState log_scale; +}; + +struct FrontendOutput { + const uint16_t* values; + size_t size; +}; + +// Main entry point to processing frontend samples. Updates num_samples_read to +// contain the number of samples that have been consumed from the input array. +// Returns a struct containing the generated output. If not enough samples were +// added to generate a feature vector, the returned size will be 0 and the +// values pointer will be NULL. Note that the output pointer will be invalidated +// as soon as FrontendProcessSamples is called again, so copy the contents +// elsewhere if you need to use them later. +struct FrontendOutput FrontendProcessSamples(struct FrontendState* state, + const int16_t* samples, + size_t num_samples, + size_t* num_samples_read); + +void FrontendReset(struct FrontendState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend_util.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend_util.c new file mode 100644 index 00000000..27224f6d --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend_util.c @@ -0,0 +1,85 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h" + +#include +#include + +#include "tensorflow/lite/experimental/microfrontend/lib/bits.h" + +void FrontendFillConfigWithDefaults(struct FrontendConfig* config) { + WindowFillConfigWithDefaults(&config->window); + FilterbankFillConfigWithDefaults(&config->filterbank); + NoiseReductionFillConfigWithDefaults(&config->noise_reduction); + PcanGainControlFillConfigWithDefaults(&config->pcan_gain_control); + LogScaleFillConfigWithDefaults(&config->log_scale); +} + +int FrontendPopulateState(const struct FrontendConfig* config, + struct FrontendState* state, int sample_rate) { + memset(state, 0, sizeof(*state)); + + if (!WindowPopulateState(&config->window, &state->window, sample_rate)) { + fprintf(stderr, "Failed to populate window state\n"); + return 0; + } + + if (!FftPopulateState(&state->fft, state->window.size)) { + fprintf(stderr, "Failed to populate fft state\n"); + return 0; + } + FftInit(&state->fft); + + if (!FilterbankPopulateState(&config->filterbank, &state->filterbank, + sample_rate, state->fft.fft_size / 2 + 1)) { + fprintf(stderr, "Failed to populate filterbank state\n"); + return 0; + } + + if (!NoiseReductionPopulateState(&config->noise_reduction, + &state->noise_reduction, + state->filterbank.num_channels)) { + fprintf(stderr, "Failed to populate noise reduction state\n"); + return 0; + } + + int input_correction_bits = + MostSignificantBit32(state->fft.fft_size) - 1 - (kFilterbankBits / 2); + if (!PcanGainControlPopulateState( + &config->pcan_gain_control, &state->pcan_gain_control, + state->noise_reduction.estimate, state->filterbank.num_channels, + state->noise_reduction.smoothing_bits, input_correction_bits)) { + fprintf(stderr, "Failed to populate pcan gain control state\n"); + return 0; + } + + if (!LogScalePopulateState(&config->log_scale, &state->log_scale)) { + fprintf(stderr, "Failed to populate log scale state\n"); + return 0; + } + + FrontendReset(state); + + // All good, return a true value. + return 1; +} + +void FrontendFreeStateContents(struct FrontendState* state) { + WindowFreeStateContents(&state->window); + FftFreeStateContents(&state->fft); + FilterbankFreeStateContents(&state->filterbank); + NoiseReductionFreeStateContents(&state->noise_reduction); + PcanGainControlFreeStateContents(&state->pcan_gain_control); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend_util.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend_util.h new file mode 100644 index 00000000..895ce6cd --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/frontend_util.h @@ -0,0 +1,52 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_ + +#include "tensorflow/lite/experimental/microfrontend/lib/fft_util.h" +#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h" +#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h" +#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h" +#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h" +#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h" +#include "tensorflow/lite/experimental/microfrontend/lib/window_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct FrontendConfig { + struct WindowConfig window; + struct FilterbankConfig filterbank; + struct NoiseReductionConfig noise_reduction; + struct PcanGainControlConfig pcan_gain_control; + struct LogScaleConfig log_scale; +}; + +// Fills the frontendConfig with "sane" defaults. +void FrontendFillConfigWithDefaults(struct FrontendConfig* config); + +// Allocates any buffers. +int FrontendPopulateState(const struct FrontendConfig* config, + struct FrontendState* state, int sample_rate); + +// Frees any allocated buffers. +void FrontendFreeStateContents(struct FrontendState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h new file mode 100644 index 00000000..33556dab --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h @@ -0,0 +1,48 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_ + +// This header file should be included in all variants of kiss_fft_$type.{h,cc} +// so that their sub-included source files do not mistakenly wrap libc header +// files within their kissfft_$type namespaces. +// E.g, This header avoids kissfft_int16.h containing: +// namespace kiss_fft_int16 { +// #include "kiss_fft.h" +// } +// where kiss_fft_.h contains: +// #include +// +// TRICK: By including the following header files here, their preprocessor +// header guards prevent them being re-defined inside of the kiss_fft_$type +// namespaces declared within the kiss_fft_$type.{h,cc} sources. +// Note that the original kiss_fft*.h files are untouched since they +// may be used in libraries that include them directly. + +#include +#include +#include +#include +#include + +#ifdef FIXED_POINT +#include +#endif + +#ifdef USE_SIMD +#include +#endif +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.cc b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.cc new file mode 100644 index 00000000..55457f4d --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.cc @@ -0,0 +1,8 @@ +#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h" + +#define FIXED_POINT 16 +namespace kissfft_fixed16 { +#include "kiss_fft.c" +#include "tools/kiss_fftr.c" +} // namespace kissfft_fixed16 +#undef FIXED_POINT diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h new file mode 100644 index 00000000..9abe686b --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h @@ -0,0 +1,34 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_INT16_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_INT16_H_ + +#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h" + +// Wrap 16-bit kiss fft in its own namespace. Enables us to link an application +// with different kiss fft resultions (16/32 bit interger, float, double) +// without getting a linker error. +#define FIXED_POINT 16 +namespace kissfft_fixed16 { +#include "kiss_fft.h" +#include "tools/kiss_fftr.h" +} // namespace kissfft_fixed16 +#undef FIXED_POINT +#undef kiss_fft_scalar +#undef KISS_FFT_H + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_INT16_H_ + diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_lut.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_lut.c new file mode 100644 index 00000000..f59618e0 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_lut.c @@ -0,0 +1,30 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/log_lut.h" +const uint16_t kLogLut[] +#ifndef _MSC_VER + __attribute__((aligned(4))) +#endif // _MSV_VER + = {0, 224, 442, 654, 861, 1063, 1259, 1450, 1636, 1817, 1992, 2163, + 2329, 2490, 2646, 2797, 2944, 3087, 3224, 3358, 3487, 3611, 3732, 3848, + 3960, 4068, 4172, 4272, 4368, 4460, 4549, 4633, 4714, 4791, 4864, 4934, + 5001, 5063, 5123, 5178, 5231, 5280, 5326, 5368, 5408, 5444, 5477, 5507, + 5533, 5557, 5578, 5595, 5610, 5622, 5631, 5637, 5640, 5641, 5638, 5633, + 5626, 5615, 5602, 5586, 5568, 5547, 5524, 5498, 5470, 5439, 5406, 5370, + 5332, 5291, 5249, 5203, 5156, 5106, 5054, 5000, 4944, 4885, 4825, 4762, + 4697, 4630, 4561, 4490, 4416, 4341, 4264, 4184, 4103, 4020, 3935, 3848, + 3759, 3668, 3575, 3481, 3384, 3286, 3186, 3084, 2981, 2875, 2768, 2659, + 2549, 2437, 2323, 2207, 2090, 1971, 1851, 1729, 1605, 1480, 1353, 1224, + 1094, 963, 830, 695, 559, 421, 282, 142, 0, 0}; diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_lut.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_lut.h new file mode 100644 index 00000000..b2448a32 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_lut.h @@ -0,0 +1,40 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Number of segments in the log lookup table. The table will be kLogSegments+1 +// in length (with some padding). +#define kLogSegments 128 +#define kLogSegmentsLog2 7 + +// Scale used by lookup table. +#define kLogScale 65536 +#define kLogScaleLog2 16 +#define kLogCoeff 45426 + +extern const uint16_t kLogLut[]; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale.c new file mode 100644 index 00000000..c27a50a6 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale.c @@ -0,0 +1,83 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h" + +#include "tensorflow/lite/experimental/microfrontend/lib/bits.h" +#include "tensorflow/lite/experimental/microfrontend/lib/log_lut.h" + +#define kuint16max 0x0000FFFF + +// The following functions implement integer logarithms of various sizes. The +// approximation is calculated according to method described in +// www.inti.gob.ar/electronicaeinformatica/instrumentacion/utic/ +// publicaciones/SPL2007/Log10-spl07.pdf +// It first calculates log2 of the input and then converts it to natural +// logarithm. + +static uint32_t Log2FractionPart(const uint32_t x, const uint32_t log2x) { + // Part 1 + int32_t frac = x - (1LL << log2x); + if (log2x < kLogScaleLog2) { + frac <<= kLogScaleLog2 - log2x; + } else { + frac >>= log2x - kLogScaleLog2; + } + // Part 2 + const uint32_t base_seg = frac >> (kLogScaleLog2 - kLogSegmentsLog2); + const uint32_t seg_unit = + (((uint32_t)1) << kLogScaleLog2) >> kLogSegmentsLog2; + + const int32_t c0 = kLogLut[base_seg]; + const int32_t c1 = kLogLut[base_seg + 1]; + const int32_t seg_base = seg_unit * base_seg; + const int32_t rel_pos = ((c1 - c0) * (frac - seg_base)) >> kLogScaleLog2; + return frac + c0 + rel_pos; +} + +static uint32_t Log(const uint32_t x, const uint32_t scale_shift) { + const uint32_t integer = MostSignificantBit32(x) - 1; + const uint32_t fraction = Log2FractionPart(x, integer); + const uint32_t log2 = (integer << kLogScaleLog2) + fraction; + const uint32_t round = kLogScale / 2; + const uint32_t loge = (((uint64_t)kLogCoeff) * log2 + round) >> kLogScaleLog2; + // Finally scale to our output scale + const uint32_t loge_scaled = ((loge << scale_shift) + round) >> kLogScaleLog2; + return loge_scaled; +} + +uint16_t* LogScaleApply(struct LogScaleState* state, uint32_t* signal, + int signal_size, int correction_bits) { + const int scale_shift = state->scale_shift; + uint16_t* output = (uint16_t*)signal; + uint16_t* ret = output; + int i; + for (i = 0; i < signal_size; ++i) { + uint32_t value = *signal++; + if (state->enable_log) { + if (correction_bits < 0) { + value >>= -correction_bits; + } else { + value <<= correction_bits; + } + if (value > 1) { + value = Log(value, scale_shift); + } else { + value = 0; + } + } + *output++ = (value < kuint16max) ? value : kuint16max; + } + return ret; +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale.h new file mode 100644 index 00000000..a383f32f --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale.h @@ -0,0 +1,39 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct LogScaleState { + int enable_log; + int scale_shift; +}; + +// Applies a fixed point logarithm to the signal and converts it to 16 bit. Note +// that the signal array will be modified. +uint16_t* LogScaleApply(struct LogScaleState* state, uint32_t* signal, + int signal_size, int correction_bits); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_ diff --git a/code/components/tfmicro/tensorflow/lite/micro/benchmarks/keyword_scrambled_model_data.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale_util.c similarity index 55% rename from code/components/tfmicro/tensorflow/lite/micro/benchmarks/keyword_scrambled_model_data.h rename to code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale_util.c index ce34426c..0e3dd1d1 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/benchmarks/keyword_scrambled_model_data.h +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale_util.c @@ -1,4 +1,4 @@ -/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -12,11 +12,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h" -#ifndef TENSORFLOW_LITE_MICRO_BENCHMARKS_KEYWORD_SCRAMBLED_MODEL_DATA_H_ -#define TENSORFLOW_LITE_MICRO_BENCHMARKS_KEYWORD_SCRAMBLED_MODEL_DATA_H_ +void LogScaleFillConfigWithDefaults(struct LogScaleConfig* config) { + config->enable_log = 1; + config->scale_shift = 6; +} -extern const unsigned char g_keyword_scrambled_model_data[]; -extern const unsigned int g_keyword_scrambled_model_data_length; - -#endif // TENSORFLOW_LITE_MICRO_BENCHMARKS_KEYWORD_SCRAMBLED_MODEL_DATA_H_ +int LogScalePopulateState(const struct LogScaleConfig* config, + struct LogScaleState* state) { + state->enable_log = config->enable_log; + state->scale_shift = config->scale_shift; + return 1; +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h new file mode 100644 index 00000000..11f7d9ee --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h @@ -0,0 +1,45 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_ + +#include +#include + +#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct LogScaleConfig { + // set to false (0) to disable this module + int enable_log; + // scale results by 2^(scale_shift) + int scale_shift; +}; + +// Populates the LogScaleConfig with "sane" default values. +void LogScaleFillConfigWithDefaults(struct LogScaleConfig* config); + +// Allocates any buffers. +int LogScalePopulateState(const struct LogScaleConfig* config, + struct LogScaleState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction.c new file mode 100644 index 00000000..16b30e66 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction.c @@ -0,0 +1,51 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h" + +#include + +void NoiseReductionApply(struct NoiseReductionState* state, uint32_t* signal) { + int i; + for (i = 0; i < state->num_channels; ++i) { + const uint32_t smoothing = + ((i & 1) == 0) ? state->even_smoothing : state->odd_smoothing; + const uint32_t one_minus_smoothing = (1 << kNoiseReductionBits) - smoothing; + + // Update the estimate of the noise. + const uint32_t signal_scaled_up = signal[i] << state->smoothing_bits; + uint32_t estimate = + (((uint64_t)signal_scaled_up * smoothing) + + ((uint64_t)state->estimate[i] * one_minus_smoothing)) >> + kNoiseReductionBits; + state->estimate[i] = estimate; + + // Make sure that we can't get a negative value for the signal - estimate. + if (estimate > signal_scaled_up) { + estimate = signal_scaled_up; + } + + const uint32_t floor = + ((uint64_t)signal[i] * state->min_signal_remaining) >> + kNoiseReductionBits; + const uint32_t subtracted = + (signal_scaled_up - estimate) >> state->smoothing_bits; + const uint32_t output = subtracted > floor ? subtracted : floor; + signal[i] = output; + } +} + +void NoiseReductionReset(struct NoiseReductionState* state) { + memset(state->estimate, 0, sizeof(*state->estimate) * state->num_channels); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h new file mode 100644 index 00000000..46d3f52e --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h @@ -0,0 +1,46 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_ + +#define kNoiseReductionBits 14 + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct NoiseReductionState { + int smoothing_bits; + uint16_t even_smoothing; + uint16_t odd_smoothing; + uint16_t min_signal_remaining; + int num_channels; + uint32_t* estimate; +}; + +// Removes stationary noise from each channel of the signal using a low pass +// filter. +void NoiseReductionApply(struct NoiseReductionState* state, uint32_t* signal); + +void NoiseReductionReset(struct NoiseReductionState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.c new file mode 100644 index 00000000..a6c9234e --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.c @@ -0,0 +1,45 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h" + +#include + +void NoiseReductionFillConfigWithDefaults(struct NoiseReductionConfig* config) { + config->smoothing_bits = 10; + config->even_smoothing = 0.025; + config->odd_smoothing = 0.06; + config->min_signal_remaining = 0.05; +} + +int NoiseReductionPopulateState(const struct NoiseReductionConfig* config, + struct NoiseReductionState* state, + int num_channels) { + state->smoothing_bits = config->smoothing_bits; + state->odd_smoothing = config->odd_smoothing * (1 << kNoiseReductionBits); + state->even_smoothing = config->even_smoothing * (1 << kNoiseReductionBits); + state->min_signal_remaining = + config->min_signal_remaining * (1 << kNoiseReductionBits); + state->num_channels = num_channels; + state->estimate = calloc(state->num_channels, sizeof(*state->estimate)); + if (state->estimate == NULL) { + fprintf(stderr, "Failed to alloc estimate buffer\n"); + return 0; + } + return 1; +} + +void NoiseReductionFreeStateContents(struct NoiseReductionState* state) { + free(state->estimate); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h new file mode 100644 index 00000000..fa555391 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h @@ -0,0 +1,50 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_ + +#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct NoiseReductionConfig { + // scale the signal up by 2^(smoothing_bits) before reduction + int smoothing_bits; + // smoothing coefficient for even-numbered channels + float even_smoothing; + // smoothing coefficient for odd-numbered channels + float odd_smoothing; + // fraction of signal to preserve (1.0 disables this module) + float min_signal_remaining; +}; + +// Populates the NoiseReductionConfig with "sane" default values. +void NoiseReductionFillConfigWithDefaults(struct NoiseReductionConfig* config); + +// Allocates any buffers. +int NoiseReductionPopulateState(const struct NoiseReductionConfig* config, + struct NoiseReductionState* state, + int num_channels); + +// Frees any allocated buffers. +void NoiseReductionFreeStateContents(struct NoiseReductionState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.c new file mode 100644 index 00000000..22d58767 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.c @@ -0,0 +1,56 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h" + +#include "tensorflow/lite/experimental/microfrontend/lib/bits.h" + +int16_t WideDynamicFunction(const uint32_t x, const int16_t* lut) { + if (x <= 2) { + return lut[x]; + } + + const int16_t interval = MostSignificantBit32(x); + lut += 4 * interval - 6; + + const int16_t frac = + ((interval < 11) ? (x << (11 - interval)) : (x >> (interval - 11))) & + 0x3FF; + + int32_t result = ((int32_t)lut[2] * frac) >> 5; + result += (int32_t)((uint32_t)lut[1] << 5); + result *= frac; + result = (result + (1 << 14)) >> 15; + result += lut[0]; + return (int16_t)result; +} + +uint32_t PcanShrink(const uint32_t x) { + if (x < (2 << kPcanSnrBits)) { + return (x * x) >> (2 + 2 * kPcanSnrBits - kPcanOutputBits); + } else { + return (x >> (kPcanSnrBits - kPcanOutputBits)) - (1 << kPcanOutputBits); + } +} + +void PcanGainControlApply(struct PcanGainControlState* state, + uint32_t* signal) { + int i; + for (i = 0; i < state->num_channels; ++i) { + const uint32_t gain = + WideDynamicFunction(state->noise_estimate[i], state->gain_lut); + const uint32_t snr = ((uint64_t)signal[i] * gain) >> state->snr_shift; + signal[i] = PcanShrink(snr); + } +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h new file mode 100644 index 00000000..3f6222be --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h @@ -0,0 +1,47 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_ + +#include +#include + +#define kPcanSnrBits 12 +#define kPcanOutputBits 6 + +#ifdef __cplusplus +extern "C" { +#endif + +// Details at https://research.google/pubs/pub45911.pdf +struct PcanGainControlState { + int enable_pcan; + uint32_t* noise_estimate; + int num_channels; + int16_t* gain_lut; + int32_t snr_shift; +}; + +int16_t WideDynamicFunction(const uint32_t x, const int16_t* lut); + +uint32_t PcanShrink(const uint32_t x); + +void PcanGainControlApply(struct PcanGainControlState* state, uint32_t* signal); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.c new file mode 100644 index 00000000..e850d439 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.c @@ -0,0 +1,92 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h" + +#include +#include + +#define kint16max 0x00007FFF + +void PcanGainControlFillConfigWithDefaults( + struct PcanGainControlConfig* config) { + config->enable_pcan = 0; + config->strength = 0.95; + config->offset = 80.0; + config->gain_bits = 21; +} + +int16_t PcanGainLookupFunction(const struct PcanGainControlConfig* config, + int32_t input_bits, uint32_t x) { + const float x_as_float = ((float)x) / ((uint32_t)1 << input_bits); + const float gain_as_float = + ((uint32_t)1 << config->gain_bits) * + powf(x_as_float + config->offset, -config->strength); + + if (gain_as_float > kint16max) { + return kint16max; + } + return (int16_t)(gain_as_float + 0.5f); +} + +int PcanGainControlPopulateState(const struct PcanGainControlConfig* config, + struct PcanGainControlState* state, + uint32_t* noise_estimate, + const int num_channels, + const uint16_t smoothing_bits, + const int32_t input_correction_bits) { + state->enable_pcan = config->enable_pcan; + if (!state->enable_pcan) { + return 1; + } + state->noise_estimate = noise_estimate; + state->num_channels = num_channels; + state->gain_lut = malloc(kWideDynamicFunctionLUTSize * sizeof(int16_t)); + if (state->gain_lut == NULL) { + fprintf(stderr, "Failed to allocate gain LUT\n"); + return 0; + } + state->snr_shift = config->gain_bits - input_correction_bits - kPcanSnrBits; + + const int32_t input_bits = smoothing_bits - input_correction_bits; + state->gain_lut[0] = PcanGainLookupFunction(config, input_bits, 0); + state->gain_lut[1] = PcanGainLookupFunction(config, input_bits, 1); + state->gain_lut -= 6; + int interval; + for (interval = 2; interval <= kWideDynamicFunctionBits; ++interval) { + const uint32_t x0 = (uint32_t)1 << (interval - 1); + const uint32_t x1 = x0 + (x0 >> 1); + const uint32_t x2 = + (interval == kWideDynamicFunctionBits) ? x0 + (x0 - 1) : 2 * x0; + + const int16_t y0 = PcanGainLookupFunction(config, input_bits, x0); + const int16_t y1 = PcanGainLookupFunction(config, input_bits, x1); + const int16_t y2 = PcanGainLookupFunction(config, input_bits, x2); + + const int32_t diff1 = (int32_t)y1 - y0; + const int32_t diff2 = (int32_t)y2 - y0; + const int32_t a1 = 4 * diff1 - diff2; + const int32_t a2 = diff2 - a1; + + state->gain_lut[4 * interval] = y0; + state->gain_lut[4 * interval + 1] = (int16_t)a1; + state->gain_lut[4 * interval + 2] = (int16_t)a2; + } + state->gain_lut += 6; + return 1; +} + +void PcanGainControlFreeStateContents(struct PcanGainControlState* state) { + free(state->gain_lut); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h new file mode 100644 index 00000000..d4bfaa2e --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h @@ -0,0 +1,57 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_ + +#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h" + +#define kWideDynamicFunctionBits 32 +#define kWideDynamicFunctionLUTSize (4 * kWideDynamicFunctionBits - 3) + +#ifdef __cplusplus +extern "C" { +#endif + +struct PcanGainControlConfig { + // set to false (0) to disable this module + int enable_pcan; + // gain normalization exponent (0.0 disables, 1.0 full strength) + float strength; + // positive value added in the normalization denominator + float offset; + // number of fractional bits in the gain + int gain_bits; +}; + +void PcanGainControlFillConfigWithDefaults( + struct PcanGainControlConfig* config); + +int16_t PcanGainLookupFunction(const struct PcanGainControlConfig* config, + int32_t input_bits, uint32_t x); + +int PcanGainControlPopulateState(const struct PcanGainControlConfig* config, + struct PcanGainControlState* state, + uint32_t* noise_estimate, + const int num_channels, + const uint16_t smoothing_bits, + const int32_t input_correction_bits); + +void PcanGainControlFreeStateContents(struct PcanGainControlState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window.c new file mode 100644 index 00000000..10da6762 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window.c @@ -0,0 +1,70 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/window.h" + +#include + +int WindowProcessSamples(struct WindowState* state, const int16_t* samples, + size_t num_samples, size_t* num_samples_read) { + const int size = state->size; + + // Copy samples from the samples buffer over to our local input. + size_t max_samples_to_copy = state->size - state->input_used; + if (max_samples_to_copy > num_samples) { + max_samples_to_copy = num_samples; + } + memcpy(state->input + state->input_used, samples, + max_samples_to_copy * sizeof(*samples)); + *num_samples_read = max_samples_to_copy; + state->input_used += max_samples_to_copy; + + if (state->input_used < state->size) { + // We don't have enough samples to compute a window. + return 0; + } + + // Apply the window to the input. + const int16_t* coefficients = state->coefficients; + const int16_t* input = state->input; + int16_t* output = state->output; + int i; + int16_t max_abs_output_value = 0; + for (i = 0; i < size; ++i) { + int16_t new_value = + (((int32_t)*input++) * *coefficients++) >> kFrontendWindowBits; + *output++ = new_value; + if (new_value < 0) { + new_value = -new_value; + } + if (new_value > max_abs_output_value) { + max_abs_output_value = new_value; + } + } + // Shuffle the input down by the step size, and update how much we have used. + memmove(state->input, state->input + state->step, + sizeof(*state->input) * (state->size - state->step)); + state->input_used -= state->step; + state->max_abs_output_value = max_abs_output_value; + + // Indicate that the output buffer is valid for the next stage. + return 1; +} + +void WindowReset(struct WindowState* state) { + memset(state->input, 0, state->size * sizeof(*state->input)); + memset(state->output, 0, state->size * sizeof(*state->output)); + state->input_used = 0; + state->max_abs_output_value = 0; +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window.h new file mode 100644 index 00000000..bad81514 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window.h @@ -0,0 +1,49 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_ + +#include +#include + +#define kFrontendWindowBits 12 + +#ifdef __cplusplus +extern "C" { +#endif + +struct WindowState { + size_t size; + int16_t* coefficients; + size_t step; + + int16_t* input; + size_t input_used; + int16_t* output; + int16_t max_abs_output_value; +}; + +// Applies a window to the samples coming in, stepping forward at the given +// rate. +int WindowProcessSamples(struct WindowState* state, const int16_t* samples, + size_t num_samples, size_t* num_samples_read); + +void WindowReset(struct WindowState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window_util.c b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window_util.c new file mode 100644 index 00000000..eee6e7b5 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window_util.c @@ -0,0 +1,73 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#include "tensorflow/lite/experimental/microfrontend/lib/window_util.h" + +#include +#include +#include +#include + +// Some platforms don't have M_PI +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +void WindowFillConfigWithDefaults(struct WindowConfig* config) { + config->size_ms = 25; + config->step_size_ms = 10; +} + +int WindowPopulateState(const struct WindowConfig* config, + struct WindowState* state, int sample_rate) { + state->size = config->size_ms * sample_rate / 1000; + state->step = config->step_size_ms * sample_rate / 1000; + + state->coefficients = malloc(state->size * sizeof(*state->coefficients)); + if (state->coefficients == NULL) { + fprintf(stderr, "Failed to allocate window coefficients\n"); + return 0; + } + + // Populate the window values. + const float arg = M_PI * 2.0 / ((float)state->size); + int i; + for (i = 0; i < state->size; ++i) { + float float_value = 0.5 - (0.5 * cos(arg * (i + 0.5))); + // Scale it to fixed point and round it. + state->coefficients[i] = + floor(float_value * (1 << kFrontendWindowBits) + 0.5); + } + + state->input_used = 0; + state->input = malloc(state->size * sizeof(*state->input)); + if (state->input == NULL) { + fprintf(stderr, "Failed to allocate window input\n"); + return 0; + } + + state->output = malloc(state->size * sizeof(*state->output)); + if (state->output == NULL) { + fprintf(stderr, "Failed to allocate window output\n"); + return 0; + } + + return 1; +} + +void WindowFreeStateContents(struct WindowState* state) { + free(state->coefficients); + free(state->input); + free(state->output); +} diff --git a/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window_util.h b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window_util.h new file mode 100644 index 00000000..68e4de9e --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/window_util.h @@ -0,0 +1,45 @@ +/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_ +#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_ + +#include "tensorflow/lite/experimental/microfrontend/lib/window.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct WindowConfig { + // length of window frame in milliseconds + size_t size_ms; + // length of step for next frame in milliseconds + size_t step_size_ms; +}; + +// Populates the WindowConfig with "sane" default values. +void WindowFillConfigWithDefaults(struct WindowConfig* config); + +// Allocates any buffers. +int WindowPopulateState(const struct WindowConfig* config, + struct WindowState* state, int sample_rate); + +// Frees any allocated buffers. +void WindowFreeStateContents(struct WindowState* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_ diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/common.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/common.h similarity index 92% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/common.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/common.h index 098b6beb..5e8778f1 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/common.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/common.h @@ -75,6 +75,7 @@ float ActivationFunction(float x) { inline void BiasAndClamp(float clamp_min, float clamp_max, int bias_size, const float* bias_data, int array_size, float* array_data) { + if (bias_size == 0) return; // Note: see b/132215220: in May 2019 we thought it would be OK to replace // this with the Eigen one-liner: // return (array.colwise() + bias).cwiseMin(clamp_max).cwiseMin(clamp_max). @@ -138,6 +139,100 @@ inline void BiasAndClamp(float clamp_min, float clamp_max, int bias_size, #endif } +// Single-rounding MultiplyByQuantizedMultiplier +#if TFLITE_SINGLE_ROUNDING +inline int32_t MultiplyByQuantizedMultiplier(int32_t x, + int32_t quantized_multiplier, + int shift) { + TFLITE_DCHECK(quantized_multiplier >= 0); + TFLITE_DCHECK(shift >= -31 && shift <= 30); + + const int64_t total_shift = 31 - shift; + const int64_t round = static_cast(1) << (total_shift - 1); + int64_t result = x * static_cast(quantized_multiplier) + round; + result = result >> total_shift; + + TFLITE_DCHECK(result >= std::numeric_limits::min() && + result <= std::numeric_limits::max()); + return static_cast(result); +} + +inline int32_t MultiplyByQuantizedMultiplierSmallerThanOneExp( + int32_t x, int32_t quantized_multiplier, int shift) { + TFLITE_DCHECK_LE(shift, 0); + return MultiplyByQuantizedMultiplier(x, quantized_multiplier, shift); +} + +inline int32_t MultiplyByQuantizedMultiplierGreaterThanOne( + int32_t x, int32_t quantized_multiplier, int shift) { + TFLITE_DCHECK_GE(shift, 0); + return MultiplyByQuantizedMultiplier(x, quantized_multiplier, shift); +} + +inline int32_t MultiplyByQuantizedMultiplier(int64_t x, + int32_t quantized_multiplier, + int shift) { + // Inputs: + // - quantized_multiplier has fixed point at bit 31 + // - shift is -31 to +7 (negative for right shift) + // + // Assumptions: The following input ranges are assumed + // - quantize_scale>=0 (the usual range is (1<<30) to (1>>31)-1) + // - scaling is chosen so final scaled result fits in int32_t + // - input x is in the range -(1<<47) <= x < (1<<47) + TFLITE_DCHECK(quantized_multiplier >= 0); + TFLITE_DCHECK(shift >= -31 && shift < 8); + TFLITE_DCHECK(x >= -(static_cast(1) << 47) && + x < (static_cast(1) << 47)); + + const int32_t reduced_multiplier = + (quantized_multiplier < 0x7FFF0000) + ? ((quantized_multiplier + (1 << 15)) >> 16) + : 0x7FFF; + const int64_t total_shift = 15 - shift; + const int64_t round = static_cast(1) << (total_shift - 1); + int64_t result = x * static_cast(reduced_multiplier) + round; + result = result >> total_shift; + + TFLITE_DCHECK(result >= std::numeric_limits::min() && + result <= std::numeric_limits::max()); + return static_cast(result); +} + +#ifdef USE_NEON +inline int32x4x4_t MultiplyByQuantizedMultiplier4Rows( + int32x4x4_t input_val, int32_t quantized_multiplier, int shift) { + TFLITE_DCHECK(quantized_multiplier >= 0); + + const int right_shift = std::min(-1, shift); + const int left_shift = shift - right_shift; + + const int32x4_t multiplier_dup = vdupq_n_s32(quantized_multiplier); + const int32x4_t left_shift_dup = vdupq_n_s32(left_shift); + const int32x4_t right_shift_dup = vdupq_n_s32(right_shift); + + int32x4x4_t result; + result.val[0] = vrshlq_s32( + vqdmulhq_s32(vshlq_s32(input_val.val[0], left_shift_dup), multiplier_dup), + right_shift_dup); + + result.val[1] = vrshlq_s32( + vqdmulhq_s32(vshlq_s32(input_val.val[1], left_shift_dup), multiplier_dup), + right_shift_dup); + + result.val[2] = vrshlq_s32( + vqdmulhq_s32(vshlq_s32(input_val.val[2], left_shift_dup), multiplier_dup), + right_shift_dup); + + result.val[3] = vrshlq_s32( + vqdmulhq_s32(vshlq_s32(input_val.val[3], left_shift_dup), multiplier_dup), + right_shift_dup); + + return result; +} +#endif // USE_NEON +// Double-rounding MultiplyByQuantizedMultiplier +#else inline int32_t MultiplyByQuantizedMultiplierSmallerThanOneExp( int32_t x, int32_t quantized_multiplier, int left_shift) { using gemmlowp::RoundingDivideByPOT; @@ -224,7 +319,8 @@ inline int32x4x4_t MultiplyByQuantizedMultiplier4Rows( return result; } -#endif +#endif // USE_NEON +#endif // TFLITE_SINGLE_ROUNDING template int CountLeadingZeros(T integer_input) { diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/compatibility.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/compatibility.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/compatibility.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/compatibility.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/cppmath.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/cppmath.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/cppmath.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/cppmath.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/max.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/max.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/max.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/max.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/min.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/min.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/min.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/min.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/optimized/neon_check.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/optimized/neon_check.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/optimized/neon_check.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/optimized/neon_check.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/portable_tensor.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/portable_tensor.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/portable_tensor.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/portable_tensor.h diff --git a/code/components/tflite-lib/tensorflow/lite/kernels/internal/portable_tensor_utils.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/portable_tensor_utils.h new file mode 100644 index 00000000..0671ce73 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/portable_tensor_utils.h @@ -0,0 +1,124 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_PORTABLE_TENSOR_UTILS_H_ +#define TENSORFLOW_LITE_KERNELS_INTERNAL_PORTABLE_TENSOR_UTILS_H_ + +#include +#include +#include + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" + +#if defined(_MSC_VER) +#define __restrict__ __restrict +#endif + +namespace tflite { + +namespace tensor_utils { + +// Multiplies a matrix with a scalar and reduce the result on each row to a +// scalar. +// Parameters: +// - matrix: matrix of size n_row * n_col +// - scalar: the scalar that is multiplied to each element in the matrix +// - n_row: the row count of the matrix +// - n_col: the column count of the matrix +// - output: the 32bit output +// Note: We do not need saturation because the int8 * int8 is safe from overflow +// in (2^31-1) / (2^14) = 131072, which is bigger than the n_row. Non-zero +// initial output value is not exceptionally large. +void MatrixScalarMultiplyAccumulate(const int8_t* matrix, int32_t scalar, + int32_t n_row, int32_t n_col, + int32_t* output); + +// Add another vector for each batch in the batch vector. +template +void VectorBatchVectorAdd(const T* vector, int v_size, int n_batch, + T* batch_vector) { + for (int b = 0; b < n_batch; b++) { + for (int i = 0; i < v_size; ++i) { + batch_vector[i] += vector[i]; + } + batch_vector += v_size; + } +} + +// Cwise product of two vectors. +template +inline void VectorVectorCwiseProduct(const T* __restrict__ vector1, + const T* __restrict__ vector2, int v_size, + T* __restrict__ result) { + for (int v = 0; v < v_size; v++) { + *result++ = *vector1++ * *vector2++; + } +} + +// Cwise product of a vector and a batch-vector. +template +inline void VectorBatchVectorCwiseProduct(const T* vector, int v_size, + const T* batch_vector, int n_batch, + T* result) { + for (int b = 0; b < n_batch; b++) { + VectorVectorCwiseProduct(vector, batch_vector, v_size, result); + // Update the pointers. + result += v_size; + batch_vector += v_size; + } +} + +// Cwise product and accumulate of two vectors. Since it's a MAC operation, the +// assumption here is that result array is initialized to valid values. +template +inline void VectorVectorCwiseProductAccumulate(const T* __restrict__ vector1, + const T* __restrict__ vector2, + int v_size, + T* __restrict__ result) { + for (int v = 0; v < v_size; v++) { + *result++ += *vector1++ * *vector2++; + } +} + +// Cwise product and accumulate of a vector and a batch-vector. Since it's a MAC +// operation, the assumption here is that result array is initialized to valid +// values. +template +inline void VectorBatchVectorCwiseProductAccumulate(const T* vector, int v_size, + const T* batch_vector, + int n_batch, T* result) { + for (int b = 0; b < n_batch; b++) { + VectorVectorCwiseProductAccumulate(vector, batch_vector, v_size, result); + // Update the pointers. + result += v_size; + batch_vector += v_size; + } +} + +// Batch vector initialization with another vector. +template +void VectorBatchVectorAssign(const T* vector, int v_size, int n_batch, + T* batch_vector) { + for (int b = 0; b < n_batch; b++) { + std::copy_n(vector, v_size, batch_vector + b * v_size); + } +} + +} // namespace tensor_utils + +} // namespace tflite + +#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_PORTABLE_TENSOR_UTILS_H_ diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/quantization_util.cc b/code/components/tflite-lib/tensorflow/lite/kernels/internal/quantization_util.cc similarity index 93% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/quantization_util.cc rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/quantization_util.cc index ed0fe439..62045d67 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/quantization_util.cc +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/quantization_util.cc @@ -52,6 +52,11 @@ constexpr uint32_t kFractionRoundingThreshold = 0x00200000; void QuantizeMultiplier(double double_multiplier, int32_t* quantized_multiplier, int* shift) { +#if TFLITE_SINGLE_ROUNDING + // Single-rounding MultiplyByQuantizedMultiplier only supports positive + // multipliers. + // TFLITE_DCHECK(double_multiplier >= 0); +#endif if (double_multiplier == 0.) { *quantized_multiplier = 0; *shift = 0; @@ -65,10 +70,10 @@ void QuantizeMultiplier(double double_multiplier, int32_t* quantized_multiplier, int64_t q_fixed = IntegerFrExp(double_multiplier, shift); #else // TFLITE_EMULATE_FLOAT const double q = std::frexp(double_multiplier, shift); - auto q_fixed = static_cast(TfLiteRound(q * (1ll << 31))); + auto q_fixed = static_cast(TfLiteRound(q * (1LL << 31))); #endif // TFLITE_EMULATE_FLOAT - TFLITE_CHECK(q_fixed <= (1ll << 31)); - if (q_fixed == (1ll << 31)) { + TFLITE_CHECK(q_fixed <= (1LL << 31)); + if (q_fixed == (1LL << 31)) { q_fixed /= 2; ++*shift; } @@ -87,6 +92,14 @@ void QuantizeMultiplier(double double_multiplier, int32_t* quantized_multiplier, *shift = 0; q_fixed = 0; } +#if TFLITE_SINGLE_ROUNDING + // Single-rounding MultiplyByQuantizedMultiplier doesn't support a shift > 30, + // saturate it. + if (*shift > 30) { + *shift = 30; + q_fixed = (1LL << 31) - 1; + } +#endif *quantized_multiplier = static_cast(q_fixed); } @@ -278,6 +291,12 @@ void PreprocessSoftmaxScaling(double beta, double input_scale, // result is double equivalent of Q0.31 (actually with more precision). Thus // this generates a Q(input_integer_bits).(31-input_integer_bits) // representation. +#if TFLITE_SINGLE_ROUNDING + const double max_real_multiplier = (1LL << 30) - 1.0; +#else + const double max_real_multiplier = (1LL << 31) - 1.0; +#endif + #ifdef TFLITE_EMULATE_FLOAT const double input_beta = IntegerDoubleMultiply(beta, input_scale); int shift; @@ -285,12 +304,14 @@ void PreprocessSoftmaxScaling(double beta, double input_scale, shift += (31 - input_integer_bits); double input_beta_real_multiplier = DoubleFromFractionAndShift(fraction, shift); - if (IntegerDoubleCompare(input_beta_real_multiplier, (1ll << 31) - 1.0) > 0) { - input_beta_real_multiplier = (1ll << 31) - 1.0; + if (IntegerDoubleCompare(input_beta_real_multiplier, max_real_multiplier) > + 0) { + input_beta_real_multiplier = max_real_multiplier; } #else // TFLITE_EMULATE_FLOAT - const double input_beta_real_multiplier = std::min( - beta * input_scale * (1 << (31 - input_integer_bits)), (1ll << 31) - 1.0); + const double input_beta_real_multiplier = + std::min(beta * input_scale * (1 << (31 - input_integer_bits)), + max_real_multiplier); #endif // TFLITE_EMULATE_FLOAT QuantizeMultiplierGreaterThanOne(input_beta_real_multiplier, @@ -324,8 +345,8 @@ int CalculateInputRadius(int input_integer_bits, int input_left_shift, #else // TFLITE_EMULATE_FLOAT const double max_input_rescaled = 1.0 * ((1 << input_integer_bits) - 1) * - (1ll << (total_signed_bits - input_integer_bits)) / - (1ll << input_left_shift); + (1LL << (total_signed_bits - input_integer_bits)) / + (1LL << input_left_shift); // Tighten bound using floor. Suppose that we could use the exact value. // After scaling the difference, the result would be at the maximum. Thus we // must ensure that our value has lower magnitude. diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/quantization_util.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/quantization_util.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/quantization_util.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/quantization_util.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/add.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/add.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/add.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/add.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/add_n.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/add_n.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/add_n.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/add_n.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/arg_min_max.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/arg_min_max.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/arg_min_max.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/arg_min_max.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/batch_matmul.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/batch_matmul.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/batch_matmul.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/batch_matmul.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/batch_to_space_nd.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/batch_to_space_nd.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/batch_to_space_nd.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/batch_to_space_nd.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/binary_function.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/binary_function.h similarity index 82% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/binary_function.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/binary_function.h index 1711940c..0b124af8 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/binary_function.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/binary_function.h @@ -43,16 +43,27 @@ inline void BroadcastBinaryFunction4DSlow( NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape, &desc1, &desc2); + const int* dims_data = + reinterpret_cast(output_shape.DimsDataUpTo5D()); for (int b = 0; b < output_shape.Dims(0); ++b) { + int out_idx_b = b * dims_data[1]; + int in_idx1_b = desc1.strides[0] * b; + int in_idx2_b = desc2.strides[0] * b; for (int y = 0; y < output_shape.Dims(1); ++y) { + int out_idx_y = (out_idx_b + y) * dims_data[2]; + int in_idx1_y = in_idx1_b + desc1.strides[1] * y; + int in_idx2_y = in_idx2_b + desc2.strides[1] * y; for (int x = 0; x < output_shape.Dims(2); ++x) { + int out_idx_x = (out_idx_y + x) * dims_data[3]; + int in1_idx = in_idx1_y + desc1.strides[2] * x; + int in2_idx = in_idx2_y + desc2.strides[2] * x; for (int c = 0; c < output_shape.Dims(3); ++c) { - auto out_idx = Offset(output_shape, b, y, x, c); - auto in1_idx = SubscriptToIndex(desc1, b, y, x, c); - auto in2_idx = SubscriptToIndex(desc2, b, y, x, c); + auto out_idx = out_idx_x + c; auto in1_val = input1_data[in1_idx]; auto in2_val = input2_data[in2_idx]; output_data[out_idx] = func(in1_val, in2_val); + in1_idx += desc1.strides[3]; + in2_idx += desc2.strides[3]; } } } diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/ceil.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/ceil.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/ceil.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/ceil.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/comparisons.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/comparisons.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/comparisons.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/comparisons.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/concatenation.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/concatenation.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/concatenation.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/concatenation.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/conv.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/conv.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/conv.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/conv.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/cumsum.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/cumsum.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/cumsum.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/cumsum.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/depth_to_space.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/depth_to_space.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/depth_to_space.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/depth_to_space.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h similarity index 94% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h index 20bf83df..d4fba139 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h @@ -68,6 +68,27 @@ inline int32_t DepthwiseConvRound(int32_t x, int32_t quantized_multiplier, return MultiplyByQuantizedMultiplier(x, quantized_multiplier, shift); } +// Single-rounding MultiplyByQuantizedMultiplier +#if TFLITE_SINGLE_ROUNDING +template <> +inline int32_t DepthwiseConvRound( + int32_t x, int32_t quantized_multiplier, int shift) { + using gemmlowp::RoundingDivideByPOT; + using gemmlowp::SaturatingRoundingDoublingHighMul; + int left_shift = shift > 0 ? shift : 0; + int right_shift = shift > 0 ? 0 : -shift; + return RoundingDivideByPOT(SaturatingRoundingDoublingHighMul( + x * (1 << left_shift), quantized_multiplier), + right_shift); +} + +template <> +inline int32_t DepthwiseConvRound( + int32_t x, int32_t quantized_multiplier, int shift) { + return MultiplyByQuantizedMultiplier(x, quantized_multiplier, shift); +} +// Double-rounding MultiplyByQuantizedMultiplier +#else template <> inline int32_t DepthwiseConvRound( int32_t x, int32_t quantized_multiplier, int shift) { @@ -86,6 +107,7 @@ inline int32_t DepthwiseConvRound( rounding_offset) >> right_shift; } +#endif // TFLITE_SINGLE_ROUNDING template struct DepthwiseConvBasicKernel { diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/dequantize.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/dequantize.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/dequantize.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/dequantize.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/elu.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/elu.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/elu.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/elu.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/exp.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/exp.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/exp.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/exp.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/fill.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/fill.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/fill.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/fill.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/floor.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/floor.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/floor.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/floor.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/floor_div.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/floor_div.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/floor_div.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/floor_div.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/floor_mod.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/floor_mod.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/floor_mod.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/floor_mod.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/fully_connected.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/fully_connected.h similarity index 99% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/fully_connected.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/fully_connected.h index d5ad9d67..9bf2e5df 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/fully_connected.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/fully_connected.h @@ -15,6 +15,7 @@ limitations under the License. #ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FULLY_CONNECTED_H_ #define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FULLY_CONNECTED_H_ +#include "ruy/profiler/instrumentation.h" // from @ruy #include "tensorflow/lite/kernels/internal/common.h" #include "tensorflow/lite/kernels/internal/cppmath.h" #include "tensorflow/lite/kernels/internal/quantization_util.h" diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/hard_swish.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/hard_swish.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/hard_swish.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/hard_swish.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/add.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/add.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/add.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/add.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/conv.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/conv.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/conv.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/conv.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/depthwise_conv.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/depthwise_conv.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/depthwise_conv.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/depthwise_conv.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/fully_connected.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/fully_connected.h similarity index 94% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/fully_connected.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/fully_connected.h index 2bc3e794..1a469fa9 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/fully_connected.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/fully_connected.h @@ -74,12 +74,13 @@ inline void FullyConnected( const int32_t output_activation_min = params.quantized_activation_min; const int32_t output_activation_max = params.quantized_activation_max; TFLITE_DCHECK_GE(filter_shape.DimensionsCount(), 2); - TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 2); + TFLITE_DCHECK_GE(output_shape.DimensionsCount(), 1); TFLITE_DCHECK_LE(output_activation_min, output_activation_max); const int filter_dim_count = filter_shape.DimensionsCount(); - const int batches = output_shape.Dims(0); - const int output_depth = output_shape.Dims(1); + const int output_dim_count = output_shape.DimensionsCount(); + const int batches = FlatSizeSkipDim(output_shape, output_dim_count - 1); + const int output_depth = output_shape.Dims(output_dim_count - 1); TFLITE_DCHECK_LE(output_depth, filter_shape.Dims(filter_dim_count - 2)); const int accum_depth = filter_shape.Dims(filter_dim_count - 1); for (int b = 0; b < batches; ++b) { diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/l2normalization.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/l2normalization.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/l2normalization.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/l2normalization.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/logistic.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/logistic.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/logistic.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/logistic.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/mean.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/mean.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/mean.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/mean.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/mul.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/mul.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/mul.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/mul.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/tanh.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/tanh.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/tanh.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/tanh.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/transpose_conv.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/transpose_conv.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/integer_ops/transpose_conv.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/integer_ops/transpose_conv.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/l2normalization.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/l2normalization.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/l2normalization.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/l2normalization.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/leaky_relu.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/leaky_relu.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/leaky_relu.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/leaky_relu.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/log_softmax.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/log_softmax.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/log_softmax.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/log_softmax.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/logistic.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/logistic.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/logistic.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/logistic.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/maximum_minimum.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/maximum_minimum.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/maximum_minimum.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/maximum_minimum.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/mul.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/mul.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/mul.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/mul.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/neg.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/neg.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/neg.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/neg.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/pad.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/pad.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/pad.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/pad.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/pooling.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/pooling.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/pooling.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/pooling.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc similarity index 99% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc index 0af86e36..4cc51cb4 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc @@ -77,8 +77,8 @@ void PortableAsymmetricQuantizeFloats(const float* values, const int size, const double qmin_double = kMinScale; const double qmax_double = kMaxScale; const auto minmax = std::minmax_element(values, values + size); - const double rmin = std::fmin(0, *minmax.first); - const double rmax = std::fmax(0, *minmax.second); + const double rmin = static_cast(std::min(0.0f, *minmax.first)); + const double rmax = static_cast(std::max(0.0f, *minmax.second)); if (rmin == rmax) { memset(quantized_values, 0, size * sizeof(int8_t)); *scaling_factor = 1; @@ -495,7 +495,7 @@ void PortableApplyLayerNormFloat(const int16_t* input, const float weighted_normalized_value = normalized_value * layer_norm_weights[i] * layer_norm_scale + bias[i] * bias_scale; - const int32_t quant_output = static_cast(std::round( + const int32_t quant_output = static_cast(round( weighted_normalized_value * static_cast(std::pow(2, 12)))); output[index] = std::min(int16_max, std::max(int16_min, quant_output)); } diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/portable_tensor_utils_impl.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/portable_tensor_utils_impl.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/portable_tensor_utils_impl.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/portable_tensor_utils_impl.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/prelu.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/prelu.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/prelu.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/prelu.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/quantize.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/quantize.h similarity index 58% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/quantize.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/quantize.h index 6f3f9aeb..f304b641 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/quantize.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/quantize.h @@ -17,6 +17,7 @@ limitations under the License. #include #include +#include #include "tensorflow/lite/kernels/internal/common.h" #include "tensorflow/lite/kernels/internal/compatibility.h" @@ -49,6 +50,39 @@ inline void AffineQuantize(const tflite::QuantizationParams& op_params, } } +// Quantizes per-channel. +template +inline void PerChannelQuantize( + const tflite::PerChannelQuantizationParams& op_params, + const RuntimeShape& input_shape, const InputT* input_data, + const RuntimeShape& output_shape, OutputT* output_data) { + // Ensure flat size is same. + MatchingFlatSize(input_shape, output_shape); + + const int32_t* zero_point = op_params.zero_point; + const float* scale = op_params.scale; + const int32_t quantized_dimension = op_params.quantized_dimension; + const int32_t num_dims = input_shape.DimensionsCount(); + const int32_t* dims_data = input_shape.DimsData(); + std::vector current_dim(num_dims, 0); + static constexpr int32_t min_val = std::numeric_limits::min(); + static constexpr int32_t max_val = std::numeric_limits::max(); + + do { + size_t offset = + ReducedOutputOffset(num_dims, reinterpret_cast(dims_data), + current_dim.data(), 0, nullptr); + const InputT val = input_data[offset]; + const int channel = current_dim[quantized_dimension]; + int32_t unclamped = static_cast(TfLiteRound( + val / static_cast(scale[channel]))) + + zero_point[channel]; + int32_t clamped = std::min(std::max(unclamped, min_val), max_val); + output_data[offset] = static_cast(clamped); + } while (NextIndex(num_dims, reinterpret_cast(dims_data), + current_dim.data())); +} + } // namespace reference_ops } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/reduce.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/reduce.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/reduce.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/reduce.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/requantize.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/requantize.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/requantize.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/requantize.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/resize_bilinear.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/resize_bilinear.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/resize_bilinear.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/resize_bilinear.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/round.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/round.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/round.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/round.h diff --git a/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/slice.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/slice.h new file mode 100644 index 00000000..cb73ea0d --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/slice.h @@ -0,0 +1,80 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SLICE_H_ +#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SLICE_H_ + +#include "tensorflow/lite/kernels/internal/portable_tensor.h" +#include "tensorflow/lite/kernels/internal/types.h" + +namespace tflite { + +namespace reference_ops { + +template +inline void Slice(const tflite::SliceParams& op_params, + const RuntimeShape& input_shape, + const RuntimeShape& output_shape, + SequentialTensorWriter* writer) { + const RuntimeShape ext_shape = RuntimeShape::ExtendedShape(5, input_shape); + TFLITE_DCHECK_LE(op_params.begin_count, 5); + TFLITE_DCHECK_LE(op_params.size_count, 5); + const int begin_count = op_params.begin_count; + const int size_count = op_params.size_count; + // We front-pad the begin and size vectors. + int start[5]; + int stop[5]; + for (int i = 0; i < 5; ++i) { + int padded_i = 5 - i; + start[i] = + begin_count < padded_i ? 0 : op_params.begin[begin_count - padded_i]; + stop[i] = + (size_count < padded_i || op_params.size[size_count - padded_i] == -1) + ? ext_shape.Dims(i) + : start[i] + op_params.size[size_count - padded_i]; + } + + for (int i0 = start[0]; i0 < stop[0]; ++i0) { + for (int i1 = start[1]; i1 < stop[1]; ++i1) { + for (int i2 = start[2]; i2 < stop[2]; ++i2) { + for (int i3 = start[3]; i3 < stop[3]; ++i3) { + for (int i4 = start[4]; i4 < stop[4]; ++i4) { + writer->Write(Offset(ext_shape, i0, i1, i2, i3, i4)); + } + } + } + } + } +} + +template +inline void Slice(const tflite::SliceParams& op_params, + const RuntimeShape& input_shape, const T* input_data, + const RuntimeShape& output_shape, T* output_data) { + SequentialTensorWriter writer(input_data, output_data); + return Slice(op_params, input_shape, output_shape, &writer); +} + +template +inline void Slice(const tflite::SliceParams& op_params, + const RuntimeShape& input_shape, const TfLiteTensor* input, + const RuntimeShape& output_shape, TfLiteTensor* output) { + SequentialTensorWriter writer(input, output); + return Slice(op_params, input_shape, output_shape, &writer); +} + +} // namespace reference_ops +} // namespace tflite + +#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SLICE_H_ diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/softmax.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/softmax.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/softmax.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/softmax.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/space_to_batch_nd.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/space_to_batch_nd.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/space_to_batch_nd.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/space_to_batch_nd.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/space_to_depth.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/space_to_depth.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/space_to_depth.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/space_to_depth.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/strided_slice.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/strided_slice.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/strided_slice.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/strided_slice.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/sub.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/sub.h similarity index 77% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/sub.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/sub.h index b8b8b732..3fa43ce9 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/sub.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/sub.h @@ -105,63 +105,6 @@ inline void BroadcastSubSlow(const ArithmeticParams& params, NDOpsHelper(output_desc, sub_func); } -template -inline void BroadcastSubSlow(const ArithmeticParams& params, - const RuntimeShape& input1_shape, - const uint8_t* input1_data, - const RuntimeShape& input2_shape, - const uint8_t* input2_data, - const RuntimeShape& output_shape, - uint8_t* output_data) { - ruy::profiler::ScopeLabel label("BroadcastSubSlow/uint8_t"); - TFLITE_DCHECK_LE(input1_shape.DimensionsCount(), N); - TFLITE_DCHECK_LE(input2_shape.DimensionsCount(), N); - TFLITE_DCHECK_LE(output_shape.DimensionsCount(), N); - NdArrayDesc desc1; - NdArrayDesc desc2; - NdArrayDesc output_desc; - NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1, - &desc2); - CopyDimsToDesc(RuntimeShape::ExtendedShape(N, output_shape), &output_desc); - - // In Tensorflow, the dimensions are canonically named (batch_number, row, - // col, channel), with extents (batches, height, width, depth), with the - // trailing dimension changing most rapidly (channels has the smallest stride, - // typically 1 element). - // - // In generated C code, we store arrays with the dimensions reversed. The - // first dimension has smallest stride. - // - // We name our variables by their Tensorflow convention, but generate C code - // nesting loops such that the innermost loop has the smallest stride for the - // best cache behavior. - auto sub_func = [&](int indexes[N]) { - const int32_t input1_val = - params.input1_offset + input1_data[SubscriptToIndex(desc1, indexes)]; - const int32_t input2_val = - params.input2_offset + input2_data[SubscriptToIndex(desc2, indexes)]; - const int32_t shifted_input1_val = input1_val * (1 << params.left_shift); - const int32_t shifted_input2_val = input2_val * (1 << params.left_shift); - const int32_t scaled_input1_val = - MultiplyByQuantizedMultiplierSmallerThanOneExp( - shifted_input1_val, params.input1_multiplier, params.input1_shift); - const int32_t scaled_input2_val = - MultiplyByQuantizedMultiplierSmallerThanOneExp( - shifted_input2_val, params.input2_multiplier, params.input2_shift); - const int32_t raw_sub = scaled_input1_val - scaled_input2_val; - const int32_t raw_output = - MultiplyByQuantizedMultiplierSmallerThanOneExp( - raw_sub, params.output_multiplier, params.output_shift) + - params.output_offset; - const int32_t clamped_output = - std::min(params.quantized_activation_max, - std::max(params.quantized_activation_min, raw_output)); - output_data[SubscriptToIndex(output_desc, indexes)] = - static_cast(clamped_output); - }; - NDOpsHelper(output_desc, sub_func); -} - template inline void BroadcastSubSlow(const ArithmeticParams& params, const RuntimeShape& input1_shape, @@ -202,60 +145,6 @@ inline void BroadcastSubSlow(const ArithmeticParams& params, NDOpsHelper(output_desc, sub_func); } -template -inline void BroadcastSubSlow(const ArithmeticParams& params, - const RuntimeShape& input1_shape, - const int8_t* input1_data, - const RuntimeShape& input2_shape, - const int8_t* input2_data, - const RuntimeShape& output_shape, - int8_t* output_data) { - ruy::profiler::ScopeLabel label("BroadcastSubSlow/int8_t"); - NdArrayDesc desc1; - NdArrayDesc desc2; - NdArrayDesc output_desc; - NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1, - &desc2); - CopyDimsToDesc(RuntimeShape::ExtendedShape(N, output_shape), &output_desc); - - // In Tensorflow, the dimensions are canonically named (batch_number, row, - // col, channel), with extents (batches, height, width, depth), with the - // trailing dimension changing most rapidly (channels has the smallest stride, - // typically 1 element). - // - // In generated C code, we store arrays with the dimensions reversed. The - // first dimension has smallest stride. - // - // We name our variables by their Tensorflow convention, but generate C code - // nesting loops such that the innermost loop has the smallest stride for the - // best cache behavior. - auto sub_func = [&](int indexes[N]) { - const int32_t input1_val = - params.input1_offset + input1_data[SubscriptToIndex(desc1, indexes)]; - const int32_t input2_val = - params.input2_offset + input2_data[SubscriptToIndex(desc2, indexes)]; - const int32_t shifted_input1_val = input1_val * (1 << params.left_shift); - const int32_t shifted_input2_val = input2_val * (1 << params.left_shift); - const int32_t scaled_input1_val = - MultiplyByQuantizedMultiplierSmallerThanOneExp( - shifted_input1_val, params.input1_multiplier, params.input1_shift); - const int32_t scaled_input2_val = - MultiplyByQuantizedMultiplierSmallerThanOneExp( - shifted_input2_val, params.input2_multiplier, params.input2_shift); - const int32_t raw_sub = scaled_input1_val - scaled_input2_val; - const int32_t raw_output = - MultiplyByQuantizedMultiplierSmallerThanOneExp( - raw_sub, params.output_multiplier, params.output_shift) + - params.output_offset; - const int32_t clamped_output = - std::min(params.quantized_activation_max, - std::max(params.quantized_activation_min, raw_output)); - output_data[SubscriptToIndex(output_desc, indexes)] = - static_cast(clamped_output); - }; - NDOpsHelper(output_desc, sub_func); -} - template void BroadcastSubSlow(const ArithmeticParams& params, const RuntimeShape& input1_shape, @@ -376,19 +265,37 @@ inline void BroadcastSub16POTSlow(const ArithmeticParams& params, NDOpsHelper(output_desc, sub_func); } -// Element-wise Sub that can often be used for inner loop of broadcast sub as -// well as the non-broadcast sub. -inline void SubElementwise(int size, const ArithmeticParams& params, - const uint8_t* input1_data, - const uint8_t* input2_data, uint8_t* output_data) { - TFLITE_DCHECK_GT(params.input1_offset, -256); - TFLITE_DCHECK_GT(params.input2_offset, -256); - TFLITE_DCHECK_LT(params.input1_offset, 256); - TFLITE_DCHECK_LT(params.input2_offset, 256); +template +void BroadcastQuantSubSlow(const ArithmeticParams& params, + const RuntimeShape& input1_shape, + const T* input1_data, + const RuntimeShape& input2_shape, + const T* input2_data, + const RuntimeShape& output_shape, T* output_data) { + ruy::profiler::ScopeLabel label("BroadcastQuantSubSlow/T"); + NdArrayDesc desc1; + NdArrayDesc desc2; + NdArrayDesc output_desc; + NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1, + &desc2); + CopyDimsToDesc(RuntimeShape::ExtendedShape(N, output_shape), &output_desc); - for (int i = 0; i < size; ++i) { - const int32_t input1_val = params.input1_offset + input1_data[i]; - const int32_t input2_val = params.input2_offset + input2_data[i]; + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + auto sub_func = [&](int indexes[N]) { + const int32_t input1_val = + params.input1_offset + input1_data[SubscriptToIndex(desc1, indexes)]; + const int32_t input2_val = + params.input2_offset + input2_data[SubscriptToIndex(desc2, indexes)]; const int32_t shifted_input1_val = input1_val * (1 << params.left_shift); const int32_t shifted_input2_val = input2_val * (1 << params.left_shift); const int32_t scaled_input1_val = @@ -405,21 +312,18 @@ inline void SubElementwise(int size, const ArithmeticParams& params, const int32_t clamped_output = std::min(params.quantized_activation_max, std::max(params.quantized_activation_min, raw_output)); - output_data[i] = static_cast(clamped_output); - } + output_data[SubscriptToIndex(output_desc, indexes)] = + static_cast(clamped_output); + }; + NDOpsHelper(output_desc, sub_func); } // Element-wise add that can often be used for inner loop of broadcast add as // well as the non-broadcast add. +template inline void SubElementwise(int size, const ArithmeticParams& params, - const int8_t* input1_data, const int8_t* input2_data, - int8_t* output_data) { - const int32_t int8_max_value = std::numeric_limits::max(); - TFLITE_DCHECK_GE(params.input1_offset, -1 * int8_max_value); - TFLITE_DCHECK_GE(params.input2_offset, -1 * int8_max_value); - TFLITE_DCHECK_LE(params.input1_offset, int8_max_value); - TFLITE_DCHECK_LE(params.input2_offset, int8_max_value); - + const T* input1_data, const T* input2_data, + T* output_data) { for (int i = 0; i < size; ++i) { const int32_t input1_val = params.input1_offset + input1_data[i]; const int32_t input2_val = params.input2_offset + input2_data[i]; @@ -439,7 +343,7 @@ inline void SubElementwise(int size, const ArithmeticParams& params, const int32_t clamped_output = std::min(params.quantized_activation_max, std::max(params.quantized_activation_min, raw_output)); - output_data[i] = static_cast(clamped_output); + output_data[i] = static_cast(clamped_output); } } @@ -469,11 +373,27 @@ inline void Sub(const ArithmeticParams& params, const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); - const int32_t int8_max_value = std::numeric_limits::max(); - TFLITE_DCHECK_GE(params.input1_offset, -1 * int8_max_value); - TFLITE_DCHECK_GE(params.input2_offset, -1 * int8_max_value); - TFLITE_DCHECK_LE(params.input1_offset, int8_max_value); - TFLITE_DCHECK_LE(params.input2_offset, int8_max_value); + TFLITE_DCHECK_GE(params.input1_offset, -128); + TFLITE_DCHECK_GE(params.input2_offset, -128); + // offset = -quantization_params.zero_point in PrepareGeneralSubOp(). + // So it's maximum can be 128 not 127. + TFLITE_DCHECK_LE(params.input1_offset, 128); + TFLITE_DCHECK_LE(params.input2_offset, 128); + SubElementwise(flat_size, params, input1_data, input2_data, output_data); +} + +inline void Sub(const ArithmeticParams& params, + const RuntimeShape& input1_shape, const int16_t* input1_data, + const RuntimeShape& input2_shape, const int16_t* input2_data, + const RuntimeShape& output_shape, int16_t* output_data) { + TFLITE_DCHECK_LE(params.quantized_activation_min, + params.quantized_activation_max); + + const int flat_size = + MatchingElementsSize(input1_shape, input2_shape, output_shape); + + TFLITE_DCHECK_EQ(params.input1_offset, 0); + TFLITE_DCHECK_EQ(params.input2_offset, 0); SubElementwise(flat_size, params, input1_data, input2_data, output_data); } diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/tanh.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/tanh.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/tanh.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/tanh.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/transpose.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/transpose.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/transpose.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/transpose.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/reference/transpose_conv.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/transpose_conv.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/reference/transpose_conv.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/transpose_conv.h diff --git a/code/components/tflite-lib/tensorflow/lite/kernels/internal/runtime_shape.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/runtime_shape.h new file mode 100644 index 00000000..13693643 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/runtime_shape.h @@ -0,0 +1,155 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_RUNTIME_SHAPE_H_ +#define TENSORFLOW_LITE_KERNELS_INTERNAL_RUNTIME_SHAPE_H_ + +namespace tflite { + +template +struct Dims { + int sizes[N]; + int strides[N]; +}; + +class RuntimeShape { + public: + RuntimeShape& operator=(RuntimeShape const&) = delete; + + RuntimeShape() : size_(0) {} + + explicit RuntimeShape(int dimensions_count) : size_(dimensions_count) {} + + RuntimeShape(int shape_size, int32_t value) : size_(shape_size) { + for (int i = 0; i < shape_size; ++i) { + SetDim(i, value); + } + } + + RuntimeShape(int dimensions_count, const int32_t* dims_data) + : size_(dimensions_count) { + ReplaceWith(dimensions_count, dims_data); + } + + bool operator==(const RuntimeShape& comp) const { + return this->size_ == comp.size_ && + std::memcmp(DimsData(), comp.DimsData(), size_ * sizeof(int32_t)) == + 0; + } + + ~RuntimeShape() {} + + int32_t DimensionsCount() const { return size_; } + int32_t Dims(int i) const { + TFLITE_DCHECK_GE(i, 0); + TFLITE_DCHECK_LT(i, size_); + return dims_[i]; + } + void SetDim(int i, int32_t val) { + TFLITE_DCHECK_GE(i, 0); + TFLITE_DCHECK_LT(i, size_); + dims_[i] = val; + } + + static RuntimeShape ExtendedShape(int new_shape_size, + const RuntimeShape& shape) { + return RuntimeShape(new_shape_size, shape, 1); + } + int32_t* DimsData() { return dims_; } + const int32_t* DimsData() const { return dims_; } + const int32_t* DimsDataUpTo5D() const { return dims_; } + + void ReplaceWith(int dimensions_count, const int32_t* dims_data) { + size_ = dimensions_count; + int32_t* dst_dims = DimsData(); + std::memcpy(dst_dims, dims_data, dimensions_count * sizeof(int32_t)); + } + + // Returns the total count of elements, that is the size when flattened into a + // vector. + int FlatSize() const { + int buffer_size = 1; + const int* dims_data = reinterpret_cast(DimsData()); + for (int i = 0; i < size_; i++) { + buffer_size *= dims_data[i]; + } + return buffer_size; + } + + private: + // For use only by ExtendedShape(), written to guarantee (return-value) copy + // elision in C++17. + // This creates a shape padded to the desired size with the specified value. + RuntimeShape(int new_shape_size, const RuntimeShape& shape, int pad_value) + : size_(new_shape_size) { + // If the following check fails, it is likely because a 4D-only kernel is + // being used with an array of larger dimension count. + TFLITE_CHECK_GE(new_shape_size, shape.DimensionsCount()); + const int size_increase = new_shape_size - shape.DimensionsCount(); + for (int i = 0; i < size_increase; ++i) { + SetDim(i, pad_value); + } + std::memcpy(DimsData() + size_increase, shape.DimsData(), + sizeof(int32_t) * shape.DimensionsCount()); + } + + // A maximum of 4 dimensions are supported on TFLM. + static constexpr int kMaxSize = 5; + int32_t size_; + union { + int32_t dims_[kMaxSize]; + }; +}; + +// Since tensors with '0' in their shape are valid in TF, these offset functions +// allow that as long as the corresponding index is also 0. It is upto the +// calling ops to ensure that they perform verification checks on tensor shapes +// if they don't support a particular behavior. + +inline int Offset(const RuntimeShape& shape, int i0, int i1, int i2, int i3) { + TFLITE_DCHECK_EQ(shape.DimensionsCount(), 4); + const int* dims_data = reinterpret_cast(shape.DimsData()); + TFLITE_DCHECK((dims_data[0] == 0 && i0 == 0) || + (i0 >= 0 && i0 < dims_data[0])); + TFLITE_DCHECK((dims_data[1] == 0 && i1 == 0) || + (i1 >= 0 && i1 < dims_data[1])); + TFLITE_DCHECK((dims_data[2] == 0 && i2 == 0) || + (i2 >= 0 && i2 < dims_data[2])); + TFLITE_DCHECK((dims_data[3] == 0 && i3 == 0) || + (i3 >= 0 && i3 < dims_data[3])); + return ((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3; +} + +inline int Offset(const RuntimeShape& shape, int i0, int i1, int i2, int i3, + int i4) { + TFLITE_DCHECK_EQ(shape.DimensionsCount(), 5); + const int* dims_data = reinterpret_cast(shape.DimsData()); + TFLITE_DCHECK((dims_data[0] == 0 && i0 == 0) || + (i0 >= 0 && i0 < dims_data[0])); + TFLITE_DCHECK((dims_data[1] == 0 && i1 == 0) || + (i1 >= 0 && i1 < dims_data[1])); + TFLITE_DCHECK((dims_data[2] == 0 && i2 == 0) || + (i2 >= 0 && i2 < dims_data[2])); + TFLITE_DCHECK((dims_data[3] == 0 && i3 == 0) || + (i3 >= 0 && i3 < dims_data[3])); + TFLITE_DCHECK((dims_data[4] == 0 && i4 == 0) || + (i4 >= 0 && i4 < dims_data[4])); + return (((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3) * + dims_data[4] + + i4; +} + +} // namespace tflite + +#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_RUNTIME_SHAPE_H_ diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/strided_slice_logic.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/strided_slice_logic.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/strided_slice_logic.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/strided_slice_logic.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/tensor_ctypes.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/tensor_ctypes.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/tensor_ctypes.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/tensor_ctypes.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/internal/types.h b/code/components/tflite-lib/tensorflow/lite/kernels/internal/types.h similarity index 80% rename from code/components/tfmicro/tensorflow/lite/kernels/internal/types.h rename to code/components/tflite-lib/tensorflow/lite/kernels/internal/types.h index 5cd3f687..77644bc0 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/internal/types.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/internal/types.h @@ -21,6 +21,7 @@ limitations under the License. #include #include "tensorflow/lite/kernels/internal/compatibility.h" +#include "tensorflow/lite/kernels/internal/runtime_shape.h" namespace tflite { @@ -139,211 +140,22 @@ inline bool operator==(const QuantizationParams& qp1, return qp1.zero_point == qp2.zero_point && qp1.scale == qp2.scale; } -template -struct Dims { - int sizes[N]; - int strides[N]; +// Quantization parameters for each channel, determining the mapping of +// quantized values to real values. See QuantizationParams for a single set of +// parameters per tensor. This has one parameters set per each channel. +// +// The correspondence is as follows: +// +// real_value = scale[channel] * (quantized_value - zero_point[channel]); +// +struct PerChannelQuantizationParams { + // The following members typically point to the corresponding members of a + // TfLiteAffineQuantization struct. + const float* scale; + const int32_t* zero_point; + int32_t quantized_dimension; }; -class RuntimeShape { - public: - // Shapes with dimensions up to 5 are stored directly in the structure, while - // larger shapes are separately allocated. - static constexpr int kMaxSmallSize = 5; - - RuntimeShape& operator=(RuntimeShape const&) = delete; - - RuntimeShape() : size_(0) {} - - explicit RuntimeShape(int dimensions_count) : size_(dimensions_count) { - if (dimensions_count > kMaxSmallSize) { -#ifdef TF_LITE_STATIC_MEMORY - TFLITE_CHECK(false && "No shape resizing supported on this platform"); -#else // TF_LITE_STATIC_MEMORY - dims_pointer_ = new int32_t[dimensions_count]; -#endif // TF_LITE_STATIC_MEMORY - } - } - - RuntimeShape(int shape_size, int32_t value) : size_(0) { - Resize(shape_size); - for (int i = 0; i < shape_size; ++i) { - SetDim(i, value); - } - } - - RuntimeShape(int dimensions_count, const int32_t* dims_data) : size_(0) { - ReplaceWith(dimensions_count, dims_data); - } - - RuntimeShape(const std::initializer_list init_list) : size_(0) { - BuildFrom(init_list); - } - - // Avoid using this constructor. We should be able to delete it when C++17 - // rolls out. - RuntimeShape(RuntimeShape const& other) : size_(other.DimensionsCount()) { - if (size_ > kMaxSmallSize) { -#ifdef TF_LITE_STATIC_MEMORY - TFLITE_CHECK(false && "No shape resizing supported on this platform"); -#else - dims_pointer_ = new int32_t[size_]; -#endif - } - std::memcpy(DimsData(), other.DimsData(), sizeof(int32_t) * size_); - } - - bool operator==(const RuntimeShape& comp) const { - return this->size_ == comp.size_ && - std::memcmp(DimsData(), comp.DimsData(), size_ * sizeof(int32_t)) == - 0; - } - - ~RuntimeShape() { - if (size_ > kMaxSmallSize) { -#ifdef TF_LITE_STATIC_MEMORY - TFLITE_CHECK(false && "No shape resizing supported on this platform"); -#else // TF_LITE_STATIC_MEMORY - delete[] dims_pointer_; -#endif // TF_LITE_STATIC_MEMORY - } - } - - inline int32_t DimensionsCount() const { return size_; } - inline int32_t Dims(int i) const { - TFLITE_DCHECK_GE(i, 0); - TFLITE_DCHECK_LT(i, size_); - return size_ > kMaxSmallSize ? dims_pointer_[i] : dims_[i]; - } - inline void SetDim(int i, int32_t val) { - TFLITE_DCHECK_GE(i, 0); - TFLITE_DCHECK_LT(i, size_); - if (size_ > kMaxSmallSize) { - dims_pointer_[i] = val; - } else { - dims_[i] = val; - } - } - - inline int32_t* DimsData() { - return size_ > kMaxSmallSize ? dims_pointer_ : dims_; - } - inline const int32_t* DimsData() const { - return size_ > kMaxSmallSize ? dims_pointer_ : dims_; - } - // The caller must ensure that the shape is no bigger than 5-D. - inline const int32_t* DimsDataUpTo5D() const { return dims_; } - - inline void Resize(int dimensions_count) { - if (size_ > kMaxSmallSize) { -#ifdef TF_LITE_STATIC_MEMORY - TFLITE_CHECK(false && "No shape resizing supported on this platform"); -#else // TF_LITE_STATIC_MEMORY - delete[] dims_pointer_; -#endif // TF_LITE_STATIC_MEMORY - } - size_ = dimensions_count; - if (dimensions_count > kMaxSmallSize) { -#ifdef TF_LITE_STATIC_MEMORY - TFLITE_CHECK(false && "No shape resizing supported on this platform"); -#else // TF_LITE_STATIC_MEMORY - dims_pointer_ = new int32_t[dimensions_count]; -#endif // TF_LITE_STATIC_MEMORY - } - } - - inline void ReplaceWith(int dimensions_count, const int32_t* dims_data) { - Resize(dimensions_count); - int32_t* dst_dims = DimsData(); - std::memcpy(dst_dims, dims_data, dimensions_count * sizeof(int32_t)); - } - - template - inline void BuildFrom(const T& src_iterable) { - const int dimensions_count = - std::distance(src_iterable.begin(), src_iterable.end()); - Resize(dimensions_count); - int32_t* data = DimsData(); - for (auto it : src_iterable) { - *data = it; - ++data; - } - } - - // This will probably be factored out. Old code made substantial use of 4-D - // shapes, and so this function is used to extend smaller shapes. Note that - // (a) as Dims<4>-dependent code is eliminated, the reliance on this should be - // reduced, and (b) some kernels are stricly 4-D, but then the shapes of their - // inputs should already be 4-D, so this function should not be needed. - inline static RuntimeShape ExtendedShape(int new_shape_size, - const RuntimeShape& shape) { - return RuntimeShape(new_shape_size, shape, 1); - } - - inline void BuildFrom(const std::initializer_list init_list) { - BuildFrom>(init_list); - } - - // Returns the total count of elements, that is the size when flattened into a - // vector. - inline int FlatSize() const { - int buffer_size = 1; - const int* dims_data = reinterpret_cast(DimsData()); - for (int i = 0; i < size_; i++) { - buffer_size *= dims_data[i]; - } - return buffer_size; - } - - bool operator!=(const RuntimeShape& comp) const { return !((*this) == comp); } - - private: - // For use only by ExtendedShape(), written to guarantee (return-value) copy - // elision in C++17. - // This creates a shape padded to the desired size with the specified value. - RuntimeShape(int new_shape_size, const RuntimeShape& shape, int pad_value) - : size_(0) { - // If the following check fails, it is likely because a 4D-only kernel is - // being used with an array of larger dimension count. - TFLITE_CHECK_GE(new_shape_size, shape.DimensionsCount()); - Resize(new_shape_size); - const int size_increase = new_shape_size - shape.DimensionsCount(); - for (int i = 0; i < size_increase; ++i) { - SetDim(i, pad_value); - } - std::memcpy(DimsData() + size_increase, shape.DimsData(), - sizeof(int32_t) * shape.DimensionsCount()); - } - - int32_t size_; - union { - int32_t dims_[kMaxSmallSize]; - int32_t* dims_pointer_; - }; -}; - -// Converts inference-style shape to legacy tflite::Dims<4>. -inline tflite::Dims<4> ToRuntimeDims(const tflite::RuntimeShape& array_shape) { - tflite::Dims<4> result; - const int dimensions_count = array_shape.DimensionsCount(); - TFLITE_CHECK_LE(dimensions_count, 4); - int cum_prod = 1; - for (int i = 0; i < 4; i++) { - const int new_dim = - (i < dimensions_count) ? array_shape.Dims(dimensions_count - 1 - i) : 1; - result.sizes[i] = new_dim; - result.strides[i] = cum_prod; - cum_prod *= new_dim; - } - return result; -} - -// TODO(b/80418076): Move to legacy ops file, update invocations. -inline RuntimeShape DimsToShape(const tflite::Dims<4>& dims) { - return RuntimeShape( - {dims.sizes[3], dims.sizes[2], dims.sizes[1], dims.sizes[0]}); -} - // Gets next index to iterate through a multidimensional array. inline bool NextIndex(const int num_dims, const int* dims, int* current) { if (num_dims == 0) { @@ -405,43 +217,6 @@ inline size_t ReducedOutputOffset(const int num_dims, const int* dims, // calling ops to ensure that they perform verification checks on tensor shapes // if they don't support a particular behavior. -inline int Offset(const RuntimeShape& shape, int i0, int i1, int i2, int i3) { - TFLITE_DCHECK_EQ(shape.DimensionsCount(), 4); - const int* dims_data = reinterpret_cast(shape.DimsDataUpTo5D()); - TFLITE_DCHECK((dims_data[0] == 0 && i0 == 0) || - (i0 >= 0 && i0 < dims_data[0])); - TFLITE_DCHECK((dims_data[1] == 0 && i1 == 0) || - (i1 >= 0 && i1 < dims_data[1])); - TFLITE_DCHECK((dims_data[2] == 0 && i2 == 0) || - (i2 >= 0 && i2 < dims_data[2])); - TFLITE_DCHECK((dims_data[3] == 0 && i3 == 0) || - (i3 >= 0 && i3 < dims_data[3])); - return ((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3; -} - -inline int Offset(const RuntimeShape& shape, int i0, int i1, int i2, int i3, - int i4) { - TFLITE_DCHECK_EQ(shape.DimensionsCount(), 5); - const int* dims_data = reinterpret_cast(shape.DimsDataUpTo5D()); - TFLITE_DCHECK((dims_data[0] == 0 && i0 == 0) || - (i0 >= 0 && i0 < dims_data[0])); - TFLITE_DCHECK((dims_data[1] == 0 && i1 == 0) || - (i1 >= 0 && i1 < dims_data[1])); - TFLITE_DCHECK((dims_data[2] == 0 && i2 == 0) || - (i2 >= 0 && i2 < dims_data[2])); - TFLITE_DCHECK((dims_data[3] == 0 && i3 == 0) || - (i3 >= 0 && i3 < dims_data[3])); - TFLITE_DCHECK((dims_data[4] == 0 && i4 == 0) || - (i4 >= 0 && i4 < dims_data[4])); - return (((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3) * - dims_data[4] + - i4; -} - -inline int Offset(const RuntimeShape& shape, int* index) { - return Offset(shape, index[0], index[1], index[2], index[3]); -} - inline int Offset(const Dims<4>& dims, int i0, int i1, int i2, int i3) { TFLITE_DCHECK((i0 == 0 && dims.sizes[0] == 0) || (i0 >= 0 && i0 < dims.sizes[0])); diff --git a/code/components/tfmicro/tensorflow/lite/kernels/kernel_util.cc b/code/components/tflite-lib/tensorflow/lite/kernels/kernel_util.cc similarity index 95% rename from code/components/tfmicro/tensorflow/lite/kernels/kernel_util.cc rename to code/components/tflite-lib/tensorflow/lite/kernels/kernel_util.cc index 6a53757b..75529296 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/kernel_util.cc +++ b/code/components/tflite-lib/tensorflow/lite/kernels/kernel_util.cc @@ -410,21 +410,45 @@ bool HaveSameShapes(const TfLiteTensor* input1, const TfLiteTensor* input2) { } #ifndef TF_LITE_STATIC_MEMORY +TfLiteStatus GetOutputShapeFromInput(TfLiteContext* context, + const TfLiteTensor* input, + TfLiteIntArray** output_shape) { + if (NumDimensions(input) != 1) { + TF_LITE_KERNEL_LOG(const_cast(context), + "Invalid %dD input tensor (must be a 1D tensor).", + NumDimensions(input)); + return kTfLiteError; + } + const int output_dims = SizeOfDimension(input, 0); + std::unique_ptr shape( + TfLiteIntArrayCreate(output_dims), TfLiteIntArrayFree); + for (int i = 0; i < output_dims; i++) { + shape->data[i] = input->data.i32[i]; + } + *output_shape = shape.release(); + return kTfLiteOk; +} // TODO(b/172067338): Having this function be part of TF_LITE_STATIC_MEMORY // build results in a 6KB size increase, even though the function is unsused for // that build. What appears to be happening is that while the linker drops the // unsused function, the string library that gets pulled in is not dropped, // resulting in the increased binary size. -std::string GetShapeDebugString(const TfLiteIntArray* shape) { +const std::string GetShapeDebugString(const TfLiteIntArray* shape) { std::string str; for (int d = 0; d < shape->size; ++d) { if (str.empty()) str = "[" + std::to_string(shape->data[d]); else - str += ", " + std::to_string(shape->data[d]); + // Don't add space after "," to make the output consistent with + // tensorflow::shape_inference::InferenceContext::DebugString() + str += "," + std::to_string(shape->data[d]); + } + if (str.empty()) { + str = "[]"; + } else { + str += "]"; } - str += "]"; return str; } diff --git a/code/components/tfmicro/tensorflow/lite/kernels/kernel_util.h b/code/components/tflite-lib/tensorflow/lite/kernels/kernel_util.h similarity index 93% rename from code/components/tfmicro/tensorflow/lite/kernels/kernel_util.h rename to code/components/tflite-lib/tensorflow/lite/kernels/kernel_util.h index ffae0701..d082e7b0 100644 --- a/code/components/tfmicro/tensorflow/lite/kernels/kernel_util.h +++ b/code/components/tflite-lib/tensorflow/lite/kernels/kernel_util.h @@ -18,6 +18,9 @@ limitations under the License. #include #include +#ifndef TF_LITE_STATIC_MEMORY +#include +#endif // TF_LITE_STATIC_MEMORY #include "tensorflow/lite/c/builtin_op_data.h" #include "tensorflow/lite/c/common.h" @@ -149,8 +152,12 @@ inline int SizeOfDimension(const TfLiteTensor* t, int dim) { return t->dims->data[dim]; } -inline int NumInputs(const TfLiteNode* node) { return node->inputs->size; } -inline int NumOutputs(const TfLiteNode* node) { return node->outputs->size; } +inline int NumInputs(const TfLiteNode* node) { + return node->inputs == nullptr ? 0 : node->inputs->size; +} +inline int NumOutputs(const TfLiteNode* node) { + return node->outputs == nullptr ? 0 : node->outputs->size; +} #ifndef TF_LITE_STATIC_MEMORY inline int NumIntermediates(const TfLiteNode* node) { @@ -179,6 +186,11 @@ inline bool IsConstantTensor(const TfLiteTensor* tensor) { return tensor->allocation_type == kTfLiteMmapRo; } +inline bool IsConstantOrPersistentTensor(const TfLiteTensor* tensor) { + return IsConstantTensor(tensor) || + (tensor->allocation_type == kTfLitePersistentRo); +} + // Determines whether tensor is dynamic. Note that a tensor can be non-const and // not dynamic. This function specifically checks for a dynamic tensor. inline bool IsDynamicTensor(const TfLiteTensor* tensor) { @@ -271,6 +283,16 @@ void CalculateActivationRange(TfLiteFusedActivation activation, // Return true if the given tensors have the same shape. bool HaveSameShapes(const TfLiteTensor* input1, const TfLiteTensor* input2); +#if !defined(TF_LITE_STATIC_MEMORY) +// Gets the output shape from the input tensor. +TfLiteStatus GetOutputShapeFromInput(TfLiteContext* context, + const TfLiteTensor* input, + TfLiteIntArray** output_shape); + +const std::string GetShapeDebugString(const TfLiteIntArray* shape); + +#endif // !defined(TF_LITE_STATIC_MEMORY) + // Calculates the output_shape that is necessary for element-wise operations // with broadcasting involving the two input tensors. TfLiteStatus CalculateShapeForBroadcast(TfLiteContext* context, diff --git a/code/components/tfmicro/tensorflow/lite/kernels/op_macros.h b/code/components/tflite-lib/tensorflow/lite/kernels/op_macros.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/op_macros.h rename to code/components/tflite-lib/tensorflow/lite/kernels/op_macros.h diff --git a/code/components/tfmicro/tensorflow/lite/kernels/padding.h b/code/components/tflite-lib/tensorflow/lite/kernels/padding.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/kernels/padding.h rename to code/components/tflite-lib/tensorflow/lite/kernels/padding.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/all_ops_resolver.cc b/code/components/tflite-lib/tensorflow/lite/micro/all_ops_resolver.cc similarity index 95% rename from code/components/tfmicro/tensorflow/lite/micro/all_ops_resolver.cc rename to code/components/tflite-lib/tensorflow/lite/micro/all_ops_resolver.cc index 5abdc3f9..8777cd28 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/all_ops_resolver.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/all_ops_resolver.cc @@ -26,8 +26,10 @@ AllOpsResolver::AllOpsResolver() { AddAddN(); AddArgMax(); AddArgMin(); + AddAssignVariable(); AddAveragePool2D(); AddBatchToSpaceNd(); + AddCallOnce(); AddCeil(); AddConcatenation(); AddConv2D(); @@ -40,7 +42,9 @@ AllOpsResolver::AllOpsResolver() { AddElu(); AddEqual(); AddEthosU(); + AddExp(); AddExpandDims(); + AddFill(); AddFloor(); AddFloorDiv(); AddFloorMod(); @@ -70,6 +74,7 @@ AllOpsResolver::AllOpsResolver() { AddPadV2(); AddPrelu(); AddQuantize(); + AddReadVariable(); AddReduceMax(); AddRelu(); AddRelu6(); @@ -92,9 +97,10 @@ AllOpsResolver::AllOpsResolver() { AddSub(); AddSvdf(); AddTanh(); - AddTransposeConv(); AddTranspose(); + AddTransposeConv(); AddUnpack(); + AddVarHandle(); } } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/all_ops_resolver.h b/code/components/tflite-lib/tensorflow/lite/micro/all_ops_resolver.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/all_ops_resolver.h rename to code/components/tflite-lib/tensorflow/lite/micro/all_ops_resolver.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/compatibility.h b/code/components/tflite-lib/tensorflow/lite/micro/compatibility.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/compatibility.h rename to code/components/tflite-lib/tensorflow/lite/micro/compatibility.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/debug_log.cc b/code/components/tflite-lib/tensorflow/lite/micro/debug_log.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/debug_log.cc rename to code/components/tflite-lib/tensorflow/lite/micro/debug_log.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/debug_log.h b/code/components/tflite-lib/tensorflow/lite/micro/debug_log.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/debug_log.h rename to code/components/tflite-lib/tensorflow/lite/micro/debug_log.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/flatbuffer_utils.cc b/code/components/tflite-lib/tensorflow/lite/micro/flatbuffer_utils.cc similarity index 70% rename from code/components/tfmicro/tensorflow/lite/micro/flatbuffer_utils.cc rename to code/components/tflite-lib/tensorflow/lite/micro/flatbuffer_utils.cc index ab3d98a7..9996172b 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/flatbuffer_utils.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/flatbuffer_utils.cc @@ -61,4 +61,24 @@ uint32_t NumSubgraphOperators(const Model* model, int subgraph_idx) { return NumSubgraphOperators(subgraph); } +TfLiteIntArray* FlatBufferVectorToTfLiteTypeArray( + const flatbuffers::Vector* flatbuffer_array) { + // On little-endian machines, TfLiteIntArray happens to have the same memory + // layout as flatbuffers:Vector, so we can reinterpret_cast the + // flatbuffer vector and avoid a copy and malloc. + // TODO(b/188459715): audit this usage of const_cast. + return const_cast( + reinterpret_cast(flatbuffer_array)); +} + +TfLiteFloatArray* FlatBufferVectorToTfLiteTypeArray( + const flatbuffers::Vector* flatbuffer_array) { + // On little-endian machines, TfLiteFloatArray happens to have the same memory + // layout as flatbuffers:Vector, so we can reinterpret_cast the + // flatbuffer vector and avoid a copy and malloc. + // TODO(b/188459715): audit this usage of const_cast. + return const_cast( + reinterpret_cast(flatbuffer_array)); +} + } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/flatbuffer_utils.h b/code/components/tflite-lib/tensorflow/lite/micro/flatbuffer_utils.h similarity index 85% rename from code/components/tfmicro/tensorflow/lite/micro/flatbuffer_utils.h rename to code/components/tflite-lib/tensorflow/lite/micro/flatbuffer_utils.h index ab675799..b4e0cdc2 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/flatbuffer_utils.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/flatbuffer_utils.h @@ -18,6 +18,7 @@ limitations under the License. #include "flatbuffers/flatbuffers.h" #include "flatbuffers/flexbuffers.h" +#include "tensorflow/lite/c/common.h" #include "tensorflow/lite/schema/schema_generated.h" namespace tflite { @@ -51,6 +52,14 @@ class FlexbufferWrapper : public flexbuffers::Vector { uint32_t NumSubgraphOperators(const SubGraph* subgraph); uint32_t NumSubgraphOperators(const Model* model, int subgraph_idx); +// Converts a flatbuffer array to a TfLiteArray. +// TODO(b/188459715): These function convert a const input to a non-const via a +// const_cast. It is unclear exactly why this is required. +TfLiteIntArray* FlatBufferVectorToTfLiteTypeArray( + const flatbuffers::Vector* flatbuffer_array); +TfLiteFloatArray* FlatBufferVectorToTfLiteTypeArray( + const flatbuffers::Vector* flatbuffer_array); + } // namespace tflite #endif // THIRD_PARTY_TFLITE_MICRO_TENSORFLOW_LITE_MICRO_FLATBUFFER_UTILS_H_ diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/activation_utils.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/activation_utils.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/activation_utils.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/activation_utils.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/activations.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/activations.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/activations.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/activations.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/activations.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/activations.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/activations.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/activations.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/activations_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/activations_common.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/activations_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/activations_common.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/add.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/add.cc similarity index 58% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/add.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/add.cc index c02a7497..75523d14 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/add.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/add.cc @@ -1,4 +1,4 @@ -/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,89 +23,15 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/kernels/op_macros.h" +#include "tensorflow/lite/micro/kernels/add.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" #include "tensorflow/lite/micro/memory_helpers.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" namespace tflite { -namespace ops { -namespace micro { -namespace add { - -constexpr int kInputTensor1 = 0; -constexpr int kInputTensor2 = 1; -constexpr int kOutputTensor = 0; - -struct OpData { - bool requires_broadcast; - - // These fields are used in both the general 8-bit -> 8bit quantized path, - // and the special 16-bit -> 16bit quantized path - int input1_shift; - int input2_shift; - int32_t output_activation_min; - int32_t output_activation_max; - - // These fields are used only in the general 8-bit -> 8bit quantized path - int32_t input1_multiplier; - int32_t input2_multiplier; - int32_t output_multiplier; - int output_shift; - int left_shift; - int32_t input1_offset; - int32_t input2_offset; - int32_t output_offset; - - // Used only for float evals: - float output_activation_min_f32; - float output_activation_max_f32; -}; - -TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteAddParams* params, - const TfLiteTensor* input1, - const TfLiteTensor* input2, TfLiteTensor* output, - OpData* data) { - data->requires_broadcast = !HaveSameShapes(input1, input2); - - if (output->type == kTfLiteInt8 || output->type == kTfLiteInt16) { - // 8bit -> 8bit general quantized path, with general rescalings - data->input1_offset = -input1->params.zero_point; - data->input2_offset = -input2->params.zero_point; - data->output_offset = output->params.zero_point; - data->left_shift = (output->type == kTfLiteInt16) ? 15 : 20; - const double twice_max_input_scale = - 2 * static_cast( - std::max(input1->params.scale, input2->params.scale)); - const double real_input1_multiplier = - static_cast(input1->params.scale) / twice_max_input_scale; - const double real_input2_multiplier = - static_cast(input2->params.scale) / twice_max_input_scale; - const double real_output_multiplier = - twice_max_input_scale / - ((1 << data->left_shift) * static_cast(output->params.scale)); - - QuantizeMultiplierSmallerThanOneExp( - real_input1_multiplier, &data->input1_multiplier, &data->input1_shift); - - QuantizeMultiplierSmallerThanOneExp( - real_input2_multiplier, &data->input2_multiplier, &data->input2_shift); - - QuantizeMultiplierSmallerThanOneExp( - real_output_multiplier, &data->output_multiplier, &data->output_shift); - - TF_LITE_ENSURE_STATUS(CalculateActivationRangeQuantized( - context, params->activation, output, &data->output_activation_min, - &data->output_activation_max)); - } else if (output->type == kTfLiteFloat32) { - CalculateActivationRange(params->activation, - &data->output_activation_min_f32, - &data->output_activation_max_f32); - } - - return kTfLiteOk; -} void EvalAdd(TfLiteContext* context, TfLiteNode* node, TfLiteAddParams* params, - const OpData* data, const TfLiteEvalTensor* input1, + const OpDataAdd* data, const TfLiteEvalTensor* input1, const TfLiteEvalTensor* input2, TfLiteEvalTensor* output) { tflite::ArithmeticParams op_params; SetActivationParams(data->output_activation_min_f32, @@ -129,7 +55,7 @@ void EvalAdd(TfLiteContext* context, TfLiteNode* node, TfLiteAddParams* params, } TfLiteStatus EvalAddQuantized(TfLiteContext* context, TfLiteNode* node, - TfLiteAddParams* params, const OpData* data, + TfLiteAddParams* params, const OpDataAdd* data, const TfLiteEvalTensor* input1, const TfLiteEvalTensor* input2, TfLiteEvalTensor* output) { @@ -192,51 +118,31 @@ TfLiteStatus EvalAddQuantized(TfLiteContext* context, TfLiteNode* node, break; } default: - TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.", - TfLiteTypeGetName(output->type), output->type); + MicroPrintf("Type %s (%d) not supported.", + TfLiteTypeGetName(output->type), output->type); return kTfLiteError; } return kTfLiteOk; } -void* Init(TfLiteContext* context, const char* buffer, size_t length) { +void* AddInit(TfLiteContext* context, const char* buffer, size_t length) { TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); - return context->AllocatePersistentBuffer(context, sizeof(OpData)); + return context->AllocatePersistentBuffer(context, sizeof(OpDataAdd)); } -TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { - TFLITE_DCHECK(node->user_data != nullptr); - TFLITE_DCHECK(node->builtin_data != nullptr); - - const TfLiteTensor* input1 = GetInput(context, node, kInputTensor1); - TF_LITE_ENSURE(context, input1 != nullptr); - const TfLiteTensor* input2 = GetInput(context, node, kInputTensor2); - TF_LITE_ENSURE(context, input2 != nullptr); - TfLiteTensor* output = GetOutput(context, node, kOutputTensor); - TF_LITE_ENSURE(context, output != nullptr); - - OpData* data = static_cast(node->user_data); - auto* params = reinterpret_cast(node->builtin_data); - - TF_LITE_ENSURE_STATUS( - CalculateOpData(context, params, input1, input2, output, data)); - - return kTfLiteOk; -} - -TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { +TfLiteStatus AddEval(TfLiteContext* context, TfLiteNode* node) { auto* params = reinterpret_cast(node->builtin_data); TFLITE_DCHECK(node->user_data != nullptr); - const OpData* data = static_cast(node->user_data); + const OpDataAdd* data = static_cast(node->user_data); const TfLiteEvalTensor* input1 = - tflite::micro::GetEvalInput(context, node, kInputTensor1); + tflite::micro::GetEvalInput(context, node, kAddInputTensor1); const TfLiteEvalTensor* input2 = - tflite::micro::GetEvalInput(context, node, kInputTensor2); + tflite::micro::GetEvalInput(context, node, kAddInputTensor2); TfLiteEvalTensor* output = - tflite::micro::GetEvalOutput(context, node, kOutputTensor); + tflite::micro::GetEvalOutput(context, node, kAddOutputTensor); if (output->type == kTfLiteFloat32) { EvalAdd(context, node, params, data, input1, input2, output); @@ -244,27 +150,23 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { TF_LITE_ENSURE_OK(context, EvalAddQuantized(context, node, params, data, input1, input2, output)); } else { - TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.", - TfLiteTypeGetName(output->type), output->type); + MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(output->type), + output->type); return kTfLiteError; } return kTfLiteOk; } -} // namespace add - TfLiteRegistration Register_ADD() { - return {/*init=*/add::Init, + return {/*init=*/AddInit, /*free=*/nullptr, - /*prepare=*/add::Prepare, - /*invoke=*/add::Eval, + /*prepare=*/AddPrepare, + /*invoke=*/AddEval, /*profiling_string=*/nullptr, /*builtin_code=*/0, /*custom_name=*/nullptr, /*version=*/0}; } -} // namespace micro -} // namespace ops } // namespace tflite diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/add.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/add.h new file mode 100644 index 00000000..88526153 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/add.h @@ -0,0 +1,64 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_KERNELS_ADD_H_ +#define TENSORFLOW_LITE_MICRO_KERNELS_ADD_H_ + +#include + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" + +namespace tflite { + +extern const int kAddInputTensor1; +extern const int kAddInputTensor2; +extern const int kAddOutputTensor; + +struct OpDataAdd { + bool requires_broadcast; + + // These fields are used in both the general 8-bit -> 8bit quantized path, + // and the special 16-bit -> 16bit quantized path + int input1_shift; + int input2_shift; + int32_t output_activation_min; + int32_t output_activation_max; + + // These fields are used only in the general 8-bit -> 8bit quantized path + int32_t input1_multiplier; + int32_t input2_multiplier; + int32_t output_multiplier; + int output_shift; + int left_shift; + int32_t input1_offset; + int32_t input2_offset; + int32_t output_offset; + + // Used only for float evals: + float output_activation_min_f32; + float output_activation_max_f32; +}; + +TfLiteStatus CalculateOpDataAdd(TfLiteContext* context, TfLiteAddParams* params, + const TfLiteTensor* input1, + const TfLiteTensor* input2, + TfLiteTensor* output, OpDataAdd* data); + +TfLiteStatus AddPrepare(TfLiteContext* context, TfLiteNode* node); + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_KERNELS_ADD_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/add_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/add_common.cc new file mode 100644 index 00000000..3d0c841e --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/add_common.cc @@ -0,0 +1,99 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/add.h" +#include "tensorflow/lite/kernels/internal/reference/integer_ops/add.h" +#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/kernels/op_macros.h" +#include "tensorflow/lite/micro/kernels/add.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/memory_helpers.h" + +namespace tflite { + +const int kAddInputTensor1 = 0; +const int kAddInputTensor2 = 1; +const int kAddOutputTensor = 0; + +TfLiteStatus CalculateOpDataAdd(TfLiteContext* context, TfLiteAddParams* params, + const TfLiteTensor* input1, + const TfLiteTensor* input2, + TfLiteTensor* output, OpDataAdd* data) { + data->requires_broadcast = !HaveSameShapes(input1, input2); + + if (output->type == kTfLiteInt8 || output->type == kTfLiteInt16) { + // 8bit -> 8bit general quantized path, with general rescalings + data->input1_offset = -input1->params.zero_point; + data->input2_offset = -input2->params.zero_point; + data->output_offset = output->params.zero_point; + data->left_shift = (output->type == kTfLiteInt16) ? 15 : 20; + const double twice_max_input_scale = + 2 * static_cast( + std::max(input1->params.scale, input2->params.scale)); + const double real_input1_multiplier = + static_cast(input1->params.scale) / twice_max_input_scale; + const double real_input2_multiplier = + static_cast(input2->params.scale) / twice_max_input_scale; + const double real_output_multiplier = + twice_max_input_scale / + ((1 << data->left_shift) * static_cast(output->params.scale)); + + QuantizeMultiplierSmallerThanOneExp( + real_input1_multiplier, &data->input1_multiplier, &data->input1_shift); + + QuantizeMultiplierSmallerThanOneExp( + real_input2_multiplier, &data->input2_multiplier, &data->input2_shift); + + QuantizeMultiplierSmallerThanOneExp( + real_output_multiplier, &data->output_multiplier, &data->output_shift); + + TF_LITE_ENSURE_STATUS(CalculateActivationRangeQuantized( + context, params->activation, output, &data->output_activation_min, + &data->output_activation_max)); + } else if (output->type == kTfLiteFloat32) { + CalculateActivationRange(params->activation, + &data->output_activation_min_f32, + &data->output_activation_max_f32); + } + + return kTfLiteOk; +} + +TfLiteStatus AddPrepare(TfLiteContext* context, TfLiteNode* node) { + TFLITE_DCHECK(node->user_data != nullptr); + TFLITE_DCHECK(node->builtin_data != nullptr); + + const TfLiteTensor* input1 = GetInput(context, node, kAddInputTensor1); + TF_LITE_ENSURE(context, input1 != nullptr); + const TfLiteTensor* input2 = GetInput(context, node, kAddInputTensor2); + TF_LITE_ENSURE(context, input2 != nullptr); + TfLiteTensor* output = GetOutput(context, node, kAddOutputTensor); + TF_LITE_ENSURE(context, output != nullptr); + + OpDataAdd* data = static_cast(node->user_data); + auto* params = reinterpret_cast(node->builtin_data); + + TF_LITE_ENSURE_STATUS( + CalculateOpDataAdd(context, params, input1, input2, output, data)); + + return kTfLiteOk; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/add_n.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/add_n.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/add_n.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/add_n.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/arg_min_max.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/arg_min_max.cc similarity index 97% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/arg_min_max.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/arg_min_max.cc index 12ac0019..8217a4a0 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/arg_min_max.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/arg_min_max.cc @@ -66,9 +66,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node, bool is_arg_max) { case kTfLiteFloat32: TF_LITE_ARG_MIN_MAX(float, int32_t, int32_t); break; - case kTfLiteUInt8: - TF_LITE_ARG_MIN_MAX(uint8_t, int32_t, int32_t); - break; case kTfLiteInt8: TF_LITE_ARG_MIN_MAX(int8_t, int32_t, int32_t); break; diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/assign_variable.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/assign_variable.cc new file mode 100644 index 00000000..a583a067 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/assign_variable.cc @@ -0,0 +1,114 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include + +#include + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/compatibility.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/memory_helpers.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" +#include "tensorflow/lite/micro/micro_graph.h" +#include "tensorflow/lite/micro/micro_resource_variable.h" +#include "tensorflow/lite/schema/schema_generated.h" + +namespace tflite { + +namespace { + +constexpr int kInputVariableId = 0; +constexpr int kInputValue = 1; + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + TF_LITE_ENSURE_EQ(context, NumInputs(node), 2); + TF_LITE_ENSURE_EQ(context, NumOutputs(node), 0); + + // This must be a TfLiteEvalTensor despite this being in Prepare, because + // CreateTensor allocates a temp tensor from the flatbuffer, which does not + // contain the correct ID generated within the VAR_HANDLE op. EvalTensors are + // all allocated during StartModelAllocation which happens before + // init/prepare, and VAR_HANDLE Prepare() references its own op_data in the + // TfLiteEvalTensor, so reading the ID here is valid. + const TfLiteEvalTensor* input_resource_id_tensor = + tflite::micro::GetEvalInput(context, node, kInputVariableId); + TFLITE_DCHECK(input_resource_id_tensor != nullptr); + TF_LITE_ENSURE(context, (input_resource_id_tensor->type == kTfLiteResource || + input_resource_id_tensor->type == kTfLiteInt32)); + TF_LITE_ENSURE_EQ(context, NumElements(input_resource_id_tensor->dims), 1); + + const TfLiteTensor* input_value = GetInput(context, node, kInputValue); + TFLITE_DCHECK(input_value != nullptr); + + // Casting to TfliteIntArray is required since we are re-using + // GetExecutionPlan from TfLiteContext. On TFLM this method returns a + // MicroGraph. + // TODO(b/188226309): Design a cleaner way to get a graph from kernel context. + MicroGraph* graph_info; + context->GetExecutionPlan(context, + reinterpret_cast(&graph_info)); + MicroResourceVariables* resources = graph_info->GetResourceVariables(); + TF_LITE_ENSURE_OK(context, + resources->Allocate(input_resource_id_tensor->data.i32[0], + context, input_value)); + + return kTfLiteOk; +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteEvalTensor* input_id = + tflite::micro::GetEvalInput(context, node, kInputVariableId); + TFLITE_DCHECK(input_id != nullptr); + + const TfLiteEvalTensor* input_value = + tflite::micro::GetEvalInput(context, node, kInputValue); + TFLITE_DCHECK(input_value != nullptr); + + // Casting to TfliteIntArray is required since we are re-using + // GetExecutionPlan from TfLiteContext. On TFLM this method returns a + // MicroGraph. + // TODO(b/188226309): Design a cleaner way to get a graph from kernel context. + MicroGraph* graph_info; + context->GetExecutionPlan(context, + reinterpret_cast(&graph_info)); + MicroResourceVariables* resources = graph_info->GetResourceVariables(); + if (resources == nullptr) { + MicroPrintf( + "ASSIGN_VARIABLE requires resource variables. Please create " + "ResourceVariables and pass it to the interpreter."); + return kTfLiteError; + } + TF_LITE_ENSURE_OK(context, + resources->Assign(input_id->data.i32[0], input_value)); + return kTfLiteOk; +} + +} // namespace. + +TfLiteRegistration Register_ASSIGN_VARIABLE() { + return {/*init=*/nullptr, + /*free=*/nullptr, + /*prepare=*/Prepare, + /*invoke=*/Eval, + /*profiling_string=*/nullptr, + /*builtin_code=*/0, + /*custom_name=*/nullptr, + /*version=*/0}; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/batch_to_space_nd.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/batch_to_space_nd.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/batch_to_space_nd.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/batch_to_space_nd.cc diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/call_once.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/call_once.cc new file mode 100644 index 00000000..97fded0c --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/call_once.cc @@ -0,0 +1,104 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include + +#include + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/compatibility.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/memory_helpers.h" +#include "tensorflow/lite/micro/micro_graph.h" +#include "tensorflow/lite/schema/schema_generated.h" + +namespace tflite { + +namespace { + +struct OpData { + int init_subgraph_index; + bool has_run; +}; + +void* Init(TfLiteContext* context, const char* buffer, size_t length) { + TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); + return context->AllocatePersistentBuffer(context, sizeof(OpData)); +} + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + OpData* op_data = reinterpret_cast(node->user_data); + const auto* params = + reinterpret_cast(node->builtin_data); + op_data->init_subgraph_index = params->init_subgraph_index; + op_data->has_run = false; + + TF_LITE_ENSURE(context, NumInputs(node) == 0); + TF_LITE_ENSURE(context, NumOutputs(node) == 0); + + // Casting to TfliteIntArray is required since we are re-using + // GetExecutionPlan from TfLiteContext. On TFLM this method returns a + // MicroGraph. + // TODO(b/188226309): Design a cleaner way to get a graph from kernel context. + MicroGraph* graph_info; + context->GetExecutionPlan(context, + reinterpret_cast(&graph_info)); + + TF_LITE_ENSURE(context, + op_data->init_subgraph_index < graph_info->NumSubgraphs()); + + return kTfLiteOk; +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + OpData* op_data = reinterpret_cast(node->user_data); + + // Call once only runs one time then is a no-op for every subsequent call. + if (op_data->has_run) { + return kTfLiteOk; + } + + // Casting to TfliteIntArray is required since we are re-using + // GetExecutionPlan from TfLiteContext. On TFLM this method returns a + // MicroGraph. + // TODO(b/188226309): Design a cleaner way to get a graph from kernel context. + MicroGraph* graph_info; + context->GetExecutionPlan(context, + reinterpret_cast(&graph_info)); + + TF_LITE_ENSURE_OK(context, + graph_info->InvokeSubgraph(op_data->init_subgraph_index)); + + op_data->has_run = true; + + return kTfLiteOk; +} + +} // namespace. + +TfLiteRegistration Register_CALL_ONCE() { + return {/*init=*/Init, + /*free=*/nullptr, + /*prepare=*/Prepare, + /*invoke=*/Eval, + /*profiling_string=*/nullptr, + /*builtin_code=*/0, + /*custom_name=*/nullptr, + /*version=*/0}; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/cast.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/cast.cc similarity index 80% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/cast.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/cast.cc index b0462ed6..0314e523 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/cast.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/cast.cc @@ -17,6 +17,7 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" namespace tflite { namespace { @@ -48,13 +49,19 @@ TfLiteStatus copyToTensor(TfLiteContext* context, const FromT* in, case kTfLiteInt8: copyCast(in, out->data.int8, num_elements); break; + case kTfLiteInt16: + copyCast(in, out->data.i16, num_elements); + break; + case kTfLiteInt32: + copyCast(in, out->data.i32, num_elements); + break; case kTfLiteFloat32: copyCast(in, tflite::micro::GetTensorData(out), num_elements); break; default: // Unsupported type. - TF_LITE_KERNEL_LOG(context, "Output type %s (%d) not supported.", - TfLiteTypeGetName(out->type), out->type); + MicroPrintf("Output type %s (%d) not supported.", + TfLiteTypeGetName(out->type), out->type); } return kTfLiteOk; } @@ -70,13 +77,19 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { switch (input->type) { case kTfLiteInt8: return copyToTensor(context, input->data.int8, output, num_elements); + case kTfLiteInt16: + return copyToTensor(context, tflite::micro::GetTensorData(input), + output, num_elements); + case kTfLiteInt32: + return copyToTensor(context, tflite::micro::GetTensorData(input), + output, num_elements); case kTfLiteFloat32: return copyToTensor(context, tflite::micro::GetTensorData(input), output, num_elements); default: // Unsupported type. - TF_LITE_KERNEL_LOG(context, "Input type %s (%d) not supported.", - TfLiteTypeGetName(input->type), input->type); + MicroPrintf("Input type %s (%d) not supported.", + TfLiteTypeGetName(input->type), input->type); } return kTfLiteOk; } diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/ceil.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/ceil.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/ceil.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/ceil.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/circular_buffer.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.cc similarity index 51% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/circular_buffer.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.cc index 37854c16..bda3e66a 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/circular_buffer.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.cc @@ -13,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include "tensorflow/lite/micro/kernels/circular_buffer.h" + #include "tensorflow/lite/c/builtin_op_data.h" #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/kernels/internal/compatibility.h" @@ -45,43 +47,17 @@ limitations under the License. * - Input and output quantization params must be identical. */ namespace tflite { -namespace ops { -namespace micro { -namespace circular_buffer { -namespace { - -// The CircularBuffer op has one input and one output tensor. -constexpr int kInputTensor = 0; -constexpr int kOutputTensor = 0; - -// Indices into the init flexbuffer's vector. -// The parameter's name is in the comment that follows. -// Elements in the vectors are ordered alphabetically by parameter name. -constexpr int kCyclesMaxIndex = 0; // 'cycles_max' - -// TODO(b/149795762): Add this to TfLiteStatus enum. -constexpr TfLiteStatus kTfLiteAbort = static_cast(-9); - -// These fields control the stride period of a strided streaming model. This op -// returns kTfLiteAbort until cycles_until_run-- is zero. At this time, -// cycles_until_run is reset to cycles_max. -struct OpData { - int cycles_until_run; - int cycles_max; -}; - -} // namespace - -void* Init(TfLiteContext* context, const char* buffer, size_t length) { +void* CircularBufferInit(TfLiteContext* context, const char* buffer, + size_t length) { TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); - OpData* op_data = static_cast( - context->AllocatePersistentBuffer(context, sizeof(OpData))); + OpDataCircularBuffer* op_data = static_cast( + context->AllocatePersistentBuffer(context, sizeof(OpDataCircularBuffer))); if (buffer != nullptr && length > 0) { const uint8_t* buffer_t = reinterpret_cast(buffer); tflite::FlexbufferWrapper wrapper(buffer_t, length); - op_data->cycles_max = wrapper.ElementAsInt32(kCyclesMaxIndex); + op_data->cycles_max = wrapper.ElementAsInt32(kCircularBufferCyclesMaxIndex); } else { op_data->cycles_max = 0; } @@ -89,54 +65,6 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { return op_data; } -TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { - const TfLiteTensor* input = GetInput(context, node, kInputTensor); - TfLiteTensor* output = GetOutput(context, node, kOutputTensor); - - TFLITE_DCHECK(node->user_data != nullptr); - OpData* op_data = static_cast(node->user_data); - - TF_LITE_ENSURE(context, input != nullptr); - TF_LITE_ENSURE(context, output != nullptr); - TF_LITE_ENSURE_EQ(context, input->dims->data[0], output->dims->data[0]); - TF_LITE_ENSURE_EQ(context, 1, input->dims->data[1]); - TF_LITE_ENSURE_EQ(context, input->dims->data[2], output->dims->data[2]); - TF_LITE_ENSURE_EQ(context, output->dims->data[3], input->dims->data[3]); - - TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type); - - // The circular buffer custom operator currently only supports int8. - TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteInt8); - - if (op_data->cycles_max <= 0) { - // The last circular buffer layer simply accumulates outputs, and does not - // run periodically. - // TODO(b/150001379): Move this special case logic to the tflite flatbuffer. - static int cb_prepare_count = 0; - cb_prepare_count++; - // These checks specifically work for the only two streaming models - // supported on TFLM. They use the shape of the output tensor along with the - // layer number to determine if the circular buffer period should be 1 or 2. - - // These models are outlined int the following documents: - // https://docs.google.com/document/d/1lc_G2ZFhjiKFo02UHjBaljye1xsL0EkfybkaVELEE3Q/edit?usp=sharing - // https://docs.google.com/document/d/1pGc42PuWyrk-Jy1-9qeqtggvsmHr1ifz8Lmqfpr2rKA/edit?usp=sharing - if (output->dims->data[1] == 5 || output->dims->data[1] == 13 || - output->dims->data[1] == 25 || - (cb_prepare_count == 5 && output->dims->data[2] == 2 && - output->dims->data[3] == 96)) { - op_data->cycles_max = 1; - cb_prepare_count = 0; - } else { - op_data->cycles_max = 2; - } - } - op_data->cycles_until_run = op_data->cycles_max; - node->user_data = op_data; - - return kTfLiteOk; -} - // Shifts buffer over by the output depth, and write new input to end of buffer. // num_slots is the number of samples stored in the output buffer. // depth is the size of each sample. @@ -145,14 +73,15 @@ void EvalInt8(const int8_t* input, int num_slots, int depth, int8_t* output) { memcpy(&output[(num_slots - 1) * depth], input, depth); } -TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { +TfLiteStatus CircularBufferEval(TfLiteContext* context, TfLiteNode* node) { const TfLiteEvalTensor* input = - tflite::micro::GetEvalInput(context, node, kInputTensor); + tflite::micro::GetEvalInput(context, node, kCircularBufferInputTensor); TfLiteEvalTensor* output = - tflite::micro::GetEvalOutput(context, node, kOutputTensor); + tflite::micro::GetEvalOutput(context, node, kCircularBufferOutputTensor); TFLITE_DCHECK(node->user_data != nullptr); - OpData* data = reinterpret_cast(node->user_data); + OpDataCircularBuffer* data = + reinterpret_cast(node->user_data); int num_slots = output->dims->data[1]; int depth = output->dims->data[2] * output->dims->data[3]; @@ -178,13 +107,11 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } -} // namespace circular_buffer - TfLiteRegistration* Register_CIRCULAR_BUFFER() { - static TfLiteRegistration r = {/*init=*/circular_buffer::Init, + static TfLiteRegistration r = {/*init=*/CircularBufferInit, /*free=*/nullptr, - /*prepare=*/circular_buffer::Prepare, - /*invoke=*/circular_buffer::Eval, + /*prepare=*/CircularBufferPrepare, + /*invoke=*/CircularBufferEval, /*profiling_string=*/nullptr, /*builtin_code=*/0, /*custom_name=*/nullptr, @@ -192,6 +119,4 @@ TfLiteRegistration* Register_CIRCULAR_BUFFER() { return &r; } -} // namespace micro -} // namespace ops } // namespace tflite diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.h new file mode 100644 index 00000000..51adf746 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.h @@ -0,0 +1,48 @@ +/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_KERNELS_CIRCULAR_BUFFER_H_ +#define TENSORFLOW_LITE_MICRO_KERNELS_CIRCULAR_BUFFER_H_ + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" + +namespace tflite { + +// The CircularBuffer op has one input and one output tensor. +extern const int kCircularBufferInputTensor; +extern const int kCircularBufferOutputTensor; + +// Indices into the init flexbuffer's vector. +// The parameter's name is in the comment that follows. +// Elements in the vectors are ordered alphabetically by parameter name. +extern const int kCircularBufferCyclesMaxIndex; // 'cycles_max' + +// TODO(b/149795762): Add this to TfLiteStatus enum. +extern const TfLiteStatus kTfLiteAbort; + +// These fields control the stride period of a strided streaming model. This op +// returns kTfLiteAbort until cycles_until_run-- is zero. At this time, +// cycles_until_run is reset to cycles_max. +struct OpDataCircularBuffer { + int cycles_until_run; + int cycles_max; +}; + +TfLiteStatus CircularBufferPrepare(TfLiteContext* context, TfLiteNode* node); + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_KERNELS_CIRCULAR_BUFFER_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer_common.cc new file mode 100644 index 00000000..0bb4d476 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer_common.cc @@ -0,0 +1,91 @@ +/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/compatibility.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/kernels/op_macros.h" +#include "tensorflow/lite/micro/flatbuffer_utils.h" +#include "tensorflow/lite/micro/kernels/circular_buffer.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" + +namespace tflite { + +// The CircularBuffer op has one input and one output tensor. +const int kCircularBufferInputTensor = 0; +const int kCircularBufferOutputTensor = 0; + +// Indices into the init flexbuffer's vector. +// The parameter's name is in the comment that follows. +// Elements in the vectors are ordered alphabetically by parameter name. +const int kCircularBufferCyclesMaxIndex = 0; // 'cycles_max' + +// TODO(b/149795762): Add this to TfLiteStatus enum. +const TfLiteStatus kTfLiteAbort = static_cast(-9); + +TfLiteStatus CircularBufferPrepare(TfLiteContext* context, TfLiteNode* node) { + const TfLiteTensor* input = + GetInput(context, node, kCircularBufferInputTensor); + TfLiteTensor* output = GetOutput(context, node, kCircularBufferOutputTensor); + + TFLITE_DCHECK(node->user_data != nullptr); + OpDataCircularBuffer* op_data = + static_cast(node->user_data); + + TF_LITE_ENSURE(context, input != nullptr); + TF_LITE_ENSURE(context, output != nullptr); + TF_LITE_ENSURE_EQ(context, input->dims->data[0], output->dims->data[0]); + TF_LITE_ENSURE_EQ(context, 1, input->dims->data[1]); + TF_LITE_ENSURE_EQ(context, input->dims->data[2], output->dims->data[2]); + TF_LITE_ENSURE_EQ(context, output->dims->data[3], input->dims->data[3]); + + TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type); + + // The circular buffer custom operator currently only supports int8. + TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteInt8); + + if (op_data->cycles_max <= 0) { + // The last circular buffer layer simply accumulates outputs, and does not + // run periodically. + // TODO(b/150001379): Move this special case logic to the tflite flatbuffer. + static int cb_prepare_count = 0; + cb_prepare_count++; + // These checks specifically work for the only two streaming models + // supported on TFLM. They use the shape of the output tensor along with the + // layer number to determine if the circular buffer period should be 1 or 2. + + // These models are outlined int the following documents: + // https://docs.google.com/document/d/1lc_G2ZFhjiKFo02UHjBaljye1xsL0EkfybkaVELEE3Q/edit?usp=sharing + // https://docs.google.com/document/d/1pGc42PuWyrk-Jy1-9qeqtggvsmHr1ifz8Lmqfpr2rKA/edit?usp=sharing + if (output->dims->data[1] == 5 || output->dims->data[1] == 13 || + output->dims->data[1] == 25 || + (cb_prepare_count == 5 && output->dims->data[2] == 2 && + output->dims->data[3] == 96)) { + op_data->cycles_max = 1; + cb_prepare_count = 0; + } else { + op_data->cycles_max = 2; + } + } + op_data->cycles_until_run = op_data->cycles_max; + node->user_data = op_data; + + return kTfLiteOk; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/circular_buffer_flexbuffers_generated_data.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer_flexbuffers_generated_data.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/circular_buffer_flexbuffers_generated_data.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer_flexbuffers_generated_data.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/comparisons.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/comparisons.cc similarity index 87% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/comparisons.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/comparisons.cc index 35007640..eb39d9ea 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/comparisons.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/comparisons.cc @@ -104,19 +104,6 @@ TfLiteStatus EqualEval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(input2), output_shape, output_data); break; - case kTfLiteUInt8: - requires_broadcast - ? reference_ops::Broadcast4DSlowEqualWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data) - : reference_ops::EqualWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data); - break; case kTfLiteInt8: requires_broadcast ? reference_ops::Broadcast4DSlowEqualWithScaling( @@ -209,19 +196,6 @@ TfLiteStatus NotEqualEval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(input2), output_shape, output_data); break; - case kTfLiteUInt8: - requires_broadcast - ? reference_ops::Broadcast4DSlowNotEqualWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data) - : reference_ops::NotEqualWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data); - break; case kTfLiteInt8: requires_broadcast ? reference_ops::Broadcast4DSlowNotEqualWithScaling( @@ -300,19 +274,6 @@ TfLiteStatus GreaterEval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(input2), output_shape, output_data); break; - case kTfLiteUInt8: - requires_broadcast - ? reference_ops::Broadcast4DSlowGreaterWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data) - : reference_ops::GreaterWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data); - break; case kTfLiteInt8: requires_broadcast ? reference_ops::Broadcast4DSlowGreaterWithScaling( @@ -391,19 +352,6 @@ TfLiteStatus GreaterEqualEval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(input2), output_shape, output_data); break; - case kTfLiteUInt8: - requires_broadcast - ? reference_ops::Broadcast4DSlowGreaterEqualWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data) - : reference_ops::GreaterEqualWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data); - break; case kTfLiteInt8: requires_broadcast ? reference_ops::Broadcast4DSlowGreaterEqualWithScaling( @@ -482,19 +430,6 @@ TfLiteStatus LessEval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(input2), output_shape, output_data); break; - case kTfLiteUInt8: - requires_broadcast - ? reference_ops::Broadcast4DSlowLessWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data) - : reference_ops::LessWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data); - break; case kTfLiteInt8: requires_broadcast ? reference_ops::Broadcast4DSlowLessWithScaling( @@ -573,19 +508,6 @@ TfLiteStatus LessEqualEval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(input2), output_shape, output_data); break; - case kTfLiteUInt8: - requires_broadcast - ? reference_ops::Broadcast4DSlowLessEqualWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data) - : reference_ops::LessEqualWithScaling( - data->params, input1_shape, - tflite::micro::GetTensorData(input1), input2_shape, - tflite::micro::GetTensorData(input2), output_shape, - output_data); - break; case kTfLiteInt8: requires_broadcast ? reference_ops::Broadcast4DSlowLessEqualWithScaling( @@ -623,7 +545,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { const TfLiteTensor* input2 = GetInput(context, node, kInputTensor2); TF_LITE_ENSURE(context, input2 != nullptr); - if (input1->type == kTfLiteUInt8 || input1->type == kTfLiteInt8) { + if (input1->type == kTfLiteInt8) { auto input1_offset = -input1->params.zero_point; auto input2_offset = -input2->params.zero_point; const int kLeftShift = 8; diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/concatenation.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/concatenation.cc similarity index 88% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/concatenation.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/concatenation.cc index c4196aea..8f45ac6a 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/concatenation.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/concatenation.cc @@ -104,27 +104,6 @@ void EvalUnquantized(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(output)); } -void EvalQuantizedUInt8(TfLiteContext* context, TfLiteNode* node) { - // Collect the shapes and data pointer of input tensors - RuntimeShape inputs_shape[kMaxInputNum]; - const RuntimeShape* inputs_shape_ptr[kMaxInputNum]; - const uint8_t* inputs_data[kMaxInputNum]; - GetAllInputTensorShapes(context, node, inputs_shape); - GetShapesPointers(inputs_shape, node->inputs->size, inputs_shape_ptr); - GetAllInputTensorData(context, node, inputs_data); - - TfLiteEvalTensor* output = - tflite::micro::GetEvalOutput(context, node, kOutputTensor); - - TFLITE_DCHECK(node->user_data != nullptr); - const OpData* data = static_cast(node->user_data); - - reference_ops::ConcatenationWithScaling( - data->params, inputs_shape_ptr, inputs_data, - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); -} - void* Init(TfLiteContext* context, const char* buffer, size_t length) { TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); return context->AllocatePersistentBuffer(context, sizeof(OpData)); @@ -146,9 +125,9 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { // Check activation and input type TF_LITE_ENSURE_EQ(context, params->activation, kTfLiteActNone); TF_LITE_ENSURE(context, - input_type == kTfLiteFloat32 || input_type == kTfLiteUInt8 || - input_type == kTfLiteInt8 || input_type == kTfLiteInt16 || - input_type == kTfLiteInt32 || input_type == kTfLiteInt64); + input_type == kTfLiteFloat32 || input_type == kTfLiteInt8 || + input_type == kTfLiteInt16 || input_type == kTfLiteInt32 || + input_type == kTfLiteInt64); // Output type must match input type TF_LITE_ENSURE_EQ(context, output_type, input_type); @@ -189,7 +168,6 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { data->params.inputs_count = node->inputs->size; break; } - case kTfLiteUInt8: case kTfLiteInt8: { data->params.axis = CalculatePositiveAxis(params->axis, output); data->params.inputs_count = node->inputs->size; @@ -228,7 +206,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { } TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { - const TfLiteTensor* output_tensor = GetOutput(context, node, kOutputTensor); + const TfLiteEvalTensor* output_tensor = + tflite::micro::GetEvalOutput(context, node, kOutputTensor); TF_LITE_ENSURE(context, output_tensor != nullptr); TfLiteType output_type = output_tensor->type; @@ -239,9 +218,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { case kTfLiteInt32: EvalUnquantized(context, node); break; - case kTfLiteUInt8: - EvalQuantizedUInt8(context, node); - break; case kTfLiteInt8: EvalUnquantized(context, node); break; diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/conv.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/conv.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/conv.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/conv.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/conv.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/conv.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/conv.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/conv.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/conv_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/conv_common.cc similarity index 98% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/conv_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/conv_common.cc index 48b0d0cb..6887e423 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/conv_common.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/conv_common.cc @@ -168,8 +168,6 @@ TfLiteStatus ConvPrepare(TfLiteContext* context, TfLiteNode* node) { affine_quantization->scale->size == 1 || affine_quantization->scale->size == filter->dims->data[kConvQuantizedDimension]); - TF_LITE_ENSURE_EQ(context, affine_quantization->scale->size, - affine_quantization->zero_point->size); } TF_LITE_ENSURE_STATUS(CalculateOpDataConv( diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/conv_test.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/conv_test.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/conv_test.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/conv_test.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/cumsum.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/cumsum.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/cumsum.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/cumsum.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/depth_to_space.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/depth_to_space.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/depth_to_space.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/depth_to_space.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/depthwise_conv.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/depthwise_conv.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/depthwise_conv.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/depthwise_conv.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/depthwise_conv_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv_common.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/depthwise_conv_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv_common.cc diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize.cc new file mode 100644 index 00000000..4438ea33 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize.cc @@ -0,0 +1,87 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/kernels/internal/reference/dequantize.h" + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/quantize.h" +#include "tensorflow/lite/kernels/internal/reference/requantize.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/dequantize.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" + +namespace tflite { + +void* DequantizeInit(TfLiteContext* context, const char* buffer, + size_t length) { + TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); + return context->AllocatePersistentBuffer(context, sizeof(DequantizeOpData)); +} + +TfLiteStatus DequantizeEval(TfLiteContext* context, TfLiteNode* node) { + TFLITE_DCHECK(node->user_data != nullptr); + DequantizeOpData* data = static_cast(node->user_data); + + const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0); + TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0); + + if (output->type == kTfLiteFloat32) { + switch (input->type) { + case kTfLiteInt8: + reference_ops::Dequantize(data->quantization_params, + tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + break; + case kTfLiteInt16: + reference_ops::Dequantize(data->quantization_params, + tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + break; + default: + MicroPrintf("Input %s, output %s not supported.", + TfLiteTypeGetName(input->type), + TfLiteTypeGetName(output->type)); + return kTfLiteError; + } + } else { + MicroPrintf("Input %s, output %s not supported.", + TfLiteTypeGetName(input->type), + TfLiteTypeGetName(output->type)); + return kTfLiteError; + } + + return kTfLiteOk; +} + +TfLiteRegistration Register_DEQUANTIZE() { + return {/*init=*/DequantizeInit, + /*free=*/nullptr, + /*prepare=*/DequantizePrepare, + /*invoke=*/DequantizeEval, + /*profiling_string=*/nullptr, + /*builtin_code=*/0, + /*custom_name=*/nullptr, + /*version=*/0}; +} + +} // namespace tflite diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize.h new file mode 100644 index 00000000..fe6ec169 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize.h @@ -0,0 +1,38 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_KERNELS_DEQUANTIZE_H_ +#define TENSORFLOW_LITE_MICRO_KERNELS_DEQUANTIZE_H_ + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/types.h" + +namespace tflite { + +struct DequantizeOpData { + tflite::DequantizationParams quantization_params; + // The scaling factor from input to output (aka the 'real multiplier') can + // be represented as a fixed point multiplier plus a left shift. + int32_t output_multiplier; + int output_shift; + int32_t output_zero_point; +}; + +TfLiteStatus DequantizePrepare(TfLiteContext* context, TfLiteNode* node); + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_KERNELS_DEQUANTIZE_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize_common.cc new file mode 100644 index 00000000..00b47f57 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/dequantize_common.cc @@ -0,0 +1,60 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/dequantize.h" +#include "tensorflow/lite/kernels/internal/reference/quantize.h" +#include "tensorflow/lite/kernels/internal/reference/requantize.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/dequantize.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" + +namespace tflite { + +TfLiteStatus DequantizePrepare(TfLiteContext* context, TfLiteNode* node) { + TFLITE_DCHECK(node->user_data != nullptr); + DequantizeOpData* data = static_cast(node->user_data); + + TF_LITE_ENSURE_EQ(context, NumInputs(node), 1); + TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); + + // TODO(b/140515557): Add cached dequant to improve hybrid model performance. + const TfLiteTensor* input = GetInput(context, node, 0); + TF_LITE_ENSURE(context, input != nullptr); + TfLiteTensor* output = GetOutput(context, node, 0); + TF_LITE_ENSURE(context, output != nullptr); + + TF_LITE_ENSURE(context, + input->type == kTfLiteInt8 || input->type == kTfLiteInt16); + TF_LITE_ENSURE(context, output->type == kTfLiteFloat32); + + if (output->type == kTfLiteInt32) { + const double effective_output_scale = + static_cast(input->params.scale) / + static_cast(output->params.scale); + QuantizeMultiplier(effective_output_scale, &data->output_multiplier, + &data->output_shift); + } + + data->quantization_params.zero_point = input->params.zero_point; + data->quantization_params.scale = static_cast(input->params.scale); + data->output_zero_point = output->params.zero_point; + return kTfLiteOk; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/detection_postprocess.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/detection_postprocess.cc similarity index 91% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/detection_postprocess.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/detection_postprocess.cc index 55b7d286..5ac343cf 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/detection_postprocess.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/detection_postprocess.cc @@ -1,4 +1,4 @@ -/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -233,25 +233,6 @@ class Dequantizer { float scale_; }; -void DequantizeBoxEncodings(const TfLiteEvalTensor* input_box_encodings, - int idx, float quant_zero_point, float quant_scale, - int length_box_encoding, - CenterSizeEncoding* box_centersize) { - const uint8_t* boxes = - tflite::micro::GetTensorData(input_box_encodings) + - length_box_encoding * idx; - Dequantizer dequantize(quant_zero_point, quant_scale); - // See definition of the KeyPointBoxCoder at - // https://github.com/tensorflow/models/blob/master/research/object_detection/box_coders/keypoint_box_coder.py - // The first four elements are the box coordinates, which is the same as the - // FastRnnBoxCoder at - // https://github.com/tensorflow/models/blob/master/research/object_detection/box_coders/faster_rcnn_box_coder.py - box_centersize->y = dequantize(boxes[0]); - box_centersize->x = dequantize(boxes[1]); - box_centersize->h = dequantize(boxes[2]); - box_centersize->w = dequantize(boxes[3]); -} - template T ReInterpretTensor(const TfLiteEvalTensor* tensor) { const float* tensor_base = tflite::micro::GetTensorData(tensor); @@ -281,19 +262,6 @@ TfLiteStatus DecodeCenterSizeBoxes(TfLiteContext* context, TfLiteNode* node, CenterSizeEncoding anchor; for (int idx = 0; idx < num_boxes; ++idx) { switch (input_box_encodings->type) { - // Quantized - case kTfLiteUInt8: - DequantizeBoxEncodings( - input_box_encodings, idx, - static_cast(op_data->input_box_encodings.zero_point), - static_cast(op_data->input_box_encodings.scale), - input_box_encodings->dims->data[2], &box_centersize); - DequantizeBoxEncodings( - input_anchors, idx, - static_cast(op_data->input_anchors.zero_point), - static_cast(op_data->input_anchors.scale), kNumCoordBox, - &anchor); - break; // Float case kTfLiteFloat32: { // Please see DequantizeBoxEncodings function for the support detail. @@ -350,6 +318,57 @@ void DecreasingPartialArgSort(const float* values, int num_values, [&values](const int i, const int j) { return values[i] > values[j]; }); } +template +void InsertionSort(int* start, int* end, Compare compare) { + for (int* i = start; i != end; ++i) { + std::rotate(std::upper_bound(start, i, *i, compare), i, i + 1); + } +} + +template +void TopDownMerge(int* values, int* scratch, const int half_num_values, + int num_values, Compare compare) { + int left = 0; + int right = half_num_values; + + for (int i = 0; i < num_values; i++) { + if (left >= half_num_values || + (right < num_values && compare(values[right], values[left]))) { + scratch[i] = values[right++]; + } else { + scratch[i] = values[left++]; + } + } + memcpy(values, scratch, num_values * sizeof(int)); +} + +template +void MergeSort(int* values, int* scratch, const int num_values, + Compare compare) { + constexpr int threshold = 20; + + if (num_values < threshold) { + InsertionSort(values, values + num_values, compare); + return; + } + + const int half_num_values = num_values / 2; + + MergeSort(values, scratch, half_num_values, compare); + MergeSort(values + half_num_values, scratch, num_values - half_num_values, + compare); + TopDownMerge(values, scratch, half_num_values, num_values, compare); +} + +void DecreasingArgSort(const float* values, int num_values, int* indices, + int* scratch) { + std::iota(indices, indices + num_values, 0); + + MergeSort(indices, scratch, num_values, [&values](const int i, const int j) { + return values[i] > values[j]; + }); +} + int SelectDetectionsAboveScoreThreshold(const float* values, int size, const float threshold, float* keep_values, int* keep_indices) { @@ -432,8 +451,16 @@ TfLiteStatus NonMaxSuppressionSingleClassHelper( int* sorted_indices = reinterpret_cast( context->GetScratchBuffer(context, op_data->sorted_indices_idx)); - DecreasingPartialArgSort(keep_scores, num_scores_kept, num_scores_kept, - sorted_indices); + // Reusing keep_indices for scratch buffer and write back its values + // after the sorting is done. + DecreasingArgSort(keep_scores, num_scores_kept, sorted_indices, keep_indices); + int counter = 0; + for (int i = 0; i < num_boxes; i++) { + if (scores[i] >= non_max_suppression_score_threshold) { + keep_indices[counter] = i; + counter++; + } + } const int num_boxes_kept = num_scores_kept; const int output_size = std::min(num_boxes_kept, max_detections); @@ -699,22 +726,6 @@ TfLiteStatus NonMaxSuppressionMultiClassFastHelper(TfLiteContext* context, return kTfLiteOk; } -void DequantizeClassPredictions(const TfLiteEvalTensor* input_class_predictions, - const int num_boxes, - const int num_classes_with_background, - float* scores, OpData* op_data) { - float quant_zero_point = - static_cast(op_data->input_class_predictions.zero_point); - float quant_scale = - static_cast(op_data->input_class_predictions.scale); - Dequantizer dequantize(quant_zero_point, quant_scale); - const uint8_t* scores_quant = - tflite::micro::GetTensorData(input_class_predictions); - for (int idx = 0; idx < num_boxes * num_classes_with_background; ++idx) { - scores[idx] = dequantize(scores_quant[idx]); - } -} - TfLiteStatus NonMaxSuppressionMultiClass(TfLiteContext* context, TfLiteNode* node, OpData* op_data) { // Get the input tensors @@ -736,14 +747,6 @@ TfLiteStatus NonMaxSuppressionMultiClass(TfLiteContext* context, const float* scores; switch (input_class_predictions->type) { - case kTfLiteUInt8: { - float* temporary_scores = reinterpret_cast( - context->GetScratchBuffer(context, op_data->scores_idx)); - DequantizeClassPredictions(input_class_predictions, num_boxes, - num_classes_with_background, temporary_scores, - op_data); - scores = temporary_scores; - } break; case kTfLiteFloat32: scores = tflite::micro::GetTensorData(input_class_predictions); break; diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/detection_postprocess_flexbuffers_generated_data.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/detection_postprocess_flexbuffers_generated_data.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/detection_postprocess_flexbuffers_generated_data.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/detection_postprocess_flexbuffers_generated_data.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/elementwise.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/elementwise.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/elementwise.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/elementwise.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/elu.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/elu.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/elu.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/elu.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/ethosu.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/ethosu.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/ethosu.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/ethosu.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/ethosu.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/ethosu.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/ethosu.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/ethosu.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/exp.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/exp.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/exp.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/exp.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/expand_dims.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/expand_dims.cc similarity index 68% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/expand_dims.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/expand_dims.cc index 1f105212..bea3ca7e 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/expand_dims.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/expand_dims.cc @@ -26,33 +26,10 @@ constexpr int kInputTensor = 0; constexpr int kAxisTensor = 1; constexpr int kOutputTensor = 0; -TfLiteStatus ExpandTensorDim(TfLiteContext* context, - const TfLiteEvalTensor* input, int32_t axis, - TfLiteEvalTensor* output) { - const TfLiteIntArray* input_dims = input->dims; - TfLiteIntArray* output_dims = output->dims; - if (axis < 0) { - axis = input_dims->size + 1 + axis; - } - TF_LITE_ENSURE(context, (axis <= input_dims->size)); - - output_dims->size = input_dims->size + 1; - for (int i = 0; i < output_dims->size; ++i) { - if (i < axis) { - output_dims->data[i] = input_dims->data[i]; - } else if (i == axis) { - output_dims->data[i] = 1; - } else { - output_dims->data[i] = input_dims->data[i - 1]; - } - } - return kTfLiteOk; -} - TfLiteStatus GetAxisValueFromTensor(TfLiteContext* context, - const TfLiteEvalTensor* axis, + const TfLiteTensor* axis, int32_t* axis_value) { - const int axis_dims = (tflite::micro::GetTensorShape(axis)).DimensionsCount(); + const int axis_dims = (tflite::GetTensorShape(axis)).DimensionsCount(); if (axis_dims > 1) { TF_LITE_KERNEL_LOG(context, "Axis has only one element for Expand_Dims.", axis_dims); @@ -60,7 +37,7 @@ TfLiteStatus GetAxisValueFromTensor(TfLiteContext* context, } if (kTfLiteInt32 == (axis->type)) { - const int32_t* axis_ptr = tflite::micro::GetTensorData(axis); + const int32_t* axis_ptr = tflite::GetTensorData(axis); *axis_value = axis_ptr[0]; return kTfLiteOk; } else { @@ -71,6 +48,41 @@ TfLiteStatus GetAxisValueFromTensor(TfLiteContext* context, } } +// Verifies that the output tensor's dimension shape is equivalent to inserting +// a dimension of length 1 at the dimension index axis of input's shape as +// defined in https://www.tensorflow.org/api_docs/python/tf/expand_dims. +TfLiteStatus VerifyTensorDim(TfLiteContext* context, const TfLiteTensor* input, + const TfLiteTensor* axis_tensor, + const TfLiteTensor* output) { + int32_t axis_value = 0; + TF_LITE_ENSURE_OK(context, + GetAxisValueFromTensor(context, axis_tensor, &axis_value)); + + tflite::RuntimeShape input_shape = tflite::GetTensorShape(input); + if (axis_value < 0) { + axis_value = input_shape.DimensionsCount() + 1 + axis_value; + } + TF_LITE_ENSURE(context, axis_value <= input_shape.DimensionsCount()); + + // TFLM only supports fixed dimension tensor and assumes that the output shape + // is fully specified in the model. As such, TFLM directly use the pointer to + // the dimension array in the model buffer. + tflite::RuntimeShape output_shape = tflite::GetTensorShape(output); + + TF_LITE_ENSURE(context, output_shape.DimensionsCount() == + input_shape.DimensionsCount() + 1); + for (int i = 0; i < output_shape.DimensionsCount(); ++i) { + if (i < axis_value) { + TF_LITE_ENSURE(context, output_shape.Dims(i) == input_shape.Dims(i)); + } else if (i == axis_value) { + TF_LITE_ENSURE(context, output_shape.Dims(i) == 1); + } else { + TF_LITE_ENSURE(context, output_shape.Dims(i) == input_shape.Dims(i - 1)); + } + } + return kTfLiteOk; +} + TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { TF_LITE_ENSURE_EQ(context, NumInputs(node), 2); TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); @@ -87,7 +99,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { "DynamicTensor is not yet supported by Expand_Dims."); return kTfLiteError; } - return kTfLiteOk; + return VerifyTensorDim(context, input, axis, output); } template @@ -100,23 +112,9 @@ void memCopyN(T* out, const T* in, const int num_elements) { TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, kInputTensor); - const TfLiteEvalTensor* axis = - tflite::micro::GetEvalInput(context, node, kAxisTensor); TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, kOutputTensor); const int flat_size = ElementCount(*input->dims); - const int input_dims = input->dims->size; - - int32_t axis_value; - TF_LITE_ENSURE_OK(context, - GetAxisValueFromTensor(context, axis, &axis_value)); - if ((axis_value > static_cast(input_dims)) || - (axis_value < static_cast(-(input_dims + 1)))) { - TF_LITE_KERNEL_LOG(context, "Invalid Expand_Dims axis value (%d).", - axis_value); - return kTfLiteError; - } - ExpandTensorDim(context, input, axis_value, output); switch (input->type) { case kTfLiteFloat32: { diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/fill.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/fill.cc similarity index 87% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/fill.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/fill.cc index ca3d15e1..18de3458 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/fill.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/fill.cc @@ -46,8 +46,6 @@ TfLiteStatus EnsureEq(TfLiteContext* context, const TfLiteIntArray* array, switch (tensor->type) { case kTfLiteInt8: return EnsureEqImpl(context, array, tensor); - case kTfLiteUInt8: - return EnsureEqImpl(context, array, tensor); case kTfLiteInt16: return EnsureEqImpl(context, array, tensor); case kTfLiteInt32: @@ -82,9 +80,15 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { // The value type and output type must match. TF_LITE_ENSURE_EQ(context, value->type, output->type); - // The dims tensor must match the output tensor shape. As a byproduct, - // ensures the dims tensor is of an integer type. - TF_LITE_ENSURE_OK(context, EnsureEq(context, output->dims, dims)); + // The dimension of the output tensor is known in model already. + TFLITE_DCHECK(output->dims != nullptr); + + if (dims->data.data != nullptr) { + // When the dims tensor is specified in model already (i.e. is not an + // activation tensor), the dims tensor must match the output tensor shape. + // As a byproduct, ensures the dims tensor is of an integer type. + TF_LITE_ENSURE_OK(context, EnsureEq(context, output->dims, dims)); + } return kTfLiteOk; } @@ -105,6 +109,12 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { case kTfLiteFloat32: FillImpl(value, output); break; + case kTfLiteInt32: + FillImpl(value, output); + break; + case kTfLiteInt8: + FillImpl(value, output); + break; default: TF_LITE_KERNEL_LOG( context, "Fill only currently supports float32 for input 1, got %d.", diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/floor.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/floor.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/floor.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/floor.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/floor_div.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_div.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/floor_div.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_div.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/floor_mod.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_mod.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/floor_mod.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_mod.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/fully_connected.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.cc similarity index 92% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/fully_connected.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.cc index 554aebb6..a9f35dba 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/fully_connected.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.cc @@ -1,4 +1,4 @@ -/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -82,28 +82,33 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { // Checks in Prepare ensure input, output and filter types are all the same. switch (input->type) { case kTfLiteFloat32: { + const float* bias_data = + nullptr != bias ? tflite::micro::GetTensorData(bias) : nullptr; + tflite::reference_ops::FullyConnected( FullyConnectedParamsFloat(params->activation), tflite::micro::GetTensorShape(input), tflite::micro::GetTensorData(input), tflite::micro::GetTensorShape(filter), tflite::micro::GetTensorData(filter), - tflite::micro::GetTensorShape(bias), - tflite::micro::GetTensorData(bias), + tflite::micro::GetTensorShape(bias), bias_data, tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); break; } case kTfLiteInt8: { + const int32_t* bias_data = + nullptr != bias ? tflite::micro::GetTensorData(bias) + : nullptr; + tflite::reference_integer_ops::FullyConnected( FullyConnectedParamsQuantized(data), tflite::micro::GetTensorShape(input), tflite::micro::GetTensorData(input), tflite::micro::GetTensorShape(filter), tflite::micro::GetTensorData(filter), - tflite::micro::GetTensorShape(bias), - tflite::micro::GetTensorData(bias), + tflite::micro::GetTensorShape(bias), bias_data, tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); break; diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/fully_connected.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.h similarity index 89% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/fully_connected.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.h index 0b672e43..e1215da6 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/fully_connected.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.h @@ -65,14 +65,9 @@ TfLiteStatus CalculateOpDataFullyConnected( // (reference or optimized) must define this function. TfLiteRegistration Register_FULLY_CONNECTED(); -#if defined(CMSIS_NN) -// The Arduino is a special case where we use the CMSIS kernels, but because of -// the current approach to building for Arduino, we do not support -DCMSIS_NN as -// part of the build. As a result, we use defined(ARDUINO) as proxy for the -// CMSIS kernels for this one special case. - -// Returns a TfLiteRegistration struct for cmsis_nn kernel variant that only -// supports int8. +#if defined(CMSIS_NN) || defined(HEXAGON) +// Returns a TfLiteRegistration struct for kernel variant that only supports +// int8. TfLiteRegistration Register_FULLY_CONNECTED_INT8(); #else diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/fully_connected_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected_common.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/fully_connected_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected_common.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/gather.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/gather.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/gather.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/gather.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/gather_nd.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/gather_nd.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/gather_nd.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/gather_nd.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/hard_swish.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/hard_swish.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/hard_swish.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/hard_swish.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/hard_swish.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/hard_swish.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/hard_swish.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/hard_swish.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/hard_swish_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/hard_swish_common.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/hard_swish_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/hard_swish_common.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/if.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/if.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/if.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/if.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_runner.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.cc similarity index 96% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_runner.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.cc index b6ff90d7..7debacc2 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_runner.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.cc @@ -15,6 +15,7 @@ limitations under the License. #include "tensorflow/lite/micro/kernels/kernel_runner.h" +#include "tensorflow/lite/micro/micro_arena_constants.h" #include "tensorflow/lite/micro/micro_error_reporter.h" #include "tensorflow/lite/micro/simple_memory_allocator.h" #include "tensorflow/lite/micro/test_helpers.h" @@ -22,10 +23,6 @@ limitations under the License. namespace tflite { namespace micro { -namespace { -constexpr size_t kBufferAlignment = 16; -} // namespace - // TODO(b/161841696): Consider moving away from global arena buffers: constexpr int KernelRunner::kNumScratchBuffers_; constexpr int KernelRunner::kKernelRunnerBufferSize_; @@ -112,7 +109,8 @@ void* KernelRunner::AllocatePersistentBuffer(TfLiteContext* context, KernelRunner* runner = reinterpret_cast(context->impl_); TFLITE_DCHECK(runner != nullptr); - return runner->allocator_->AllocateFromTail(bytes, kBufferAlignment); + return runner->allocator_->AllocateFromTail(bytes, + MicroArenaBufferAlignment()); } TfLiteStatus KernelRunner::RequestScratchBufferInArena(TfLiteContext* context, @@ -134,7 +132,7 @@ TfLiteStatus KernelRunner::RequestScratchBufferInArena(TfLiteContext* context, // for the lifetime of model. This means that the arena size in the tests will // be more than what we would have if the scratch buffers could share memory. runner->scratch_buffers_[runner->scratch_buffer_count_] = - runner->allocator_->AllocateFromTail(bytes, kBufferAlignment); + runner->allocator_->AllocateFromTail(bytes, MicroArenaBufferAlignment()); TFLITE_DCHECK(runner->scratch_buffers_[runner->scratch_buffer_count_] != nullptr); diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_runner.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_runner.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_util.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.cc similarity index 58% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_util.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.cc index 0cd60898..73069064 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_util.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.cc @@ -20,6 +20,51 @@ limitations under the License. namespace tflite { namespace micro { +namespace { + +int ValidateTensorIndexing(const TfLiteContext* context, int index, + int max_size, const int* tensor_indices) { + if (index >= 0 && index < max_size) { + const int tensor_index = tensor_indices[index]; + if (tensor_index != kTfLiteOptionalTensor) { + return tensor_index; + } + } + return -1; +} + +} // namespace + +// Returns a mutable tensor for a given input index. is_variable must be checked +// during prepare when the full TfLiteTensor is available. +TfLiteEvalTensor* GetMutableEvalInput(const TfLiteContext* context, + const TfLiteNode* node, int index) { + TFLITE_DCHECK(context != nullptr); + TFLITE_DCHECK(node != nullptr); + const int tensor_index = ValidateTensorIndexing( + context, index, node->inputs->size, node->inputs->data); + + if (tensor_index < 0) { + return nullptr; + } + + return context->GetEvalTensor(context, node->inputs->data[index]); +} + +// Returns the TfLiteEvalTensor struct for a given input index in a node. +const TfLiteEvalTensor* GetEvalInput(const TfLiteContext* context, + const TfLiteNode* node, int index) { + return GetMutableEvalInput(context, node, index); +} + +// Returns the TfLiteEvalTensor struct for a given output index in a node. +TfLiteEvalTensor* GetEvalOutput(const TfLiteContext* context, + const TfLiteNode* node, int index) { + TFLITE_DCHECK(context != nullptr); + TFLITE_DCHECK(node != nullptr); + return context->GetEvalTensor(context, node->outputs->data[index]); +} + bool HaveSameShapes(const TfLiteEvalTensor* input1, const TfLiteEvalTensor* input2) { TFLITE_DCHECK(input1 != nullptr); @@ -74,5 +119,14 @@ TfLiteStatus CreateWritableTensorDimsWithCopy(TfLiteContext* context, return kTfLiteOk; } +// Returns a blob of payload data. The payload is subjected to interpretation by +// the OP. This is the recommended API for an OP to get an external context. OP +// should use this instead of directly calling GetExternalContext function in +// context. +void* GetExternalContext(TfLiteContext* context) { + return reinterpret_cast( + context->GetExternalContext(context, kTfLiteMaxExternalContexts)); +} + } // namespace micro } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_util.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.h similarity index 68% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_util.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.h index 63ce653f..1bd266d1 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/kernel_util.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.h @@ -1,4 +1,4 @@ -/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,27 +28,16 @@ namespace micro { // Returns a mutable tensor for a given input index. is_variable must be checked // during prepare when the full TfLiteTensor is available. -inline TfLiteEvalTensor* GetMutableEvalInput(const TfLiteContext* context, - const TfLiteNode* node, - int index) { - TFLITE_DCHECK(context != nullptr); - TFLITE_DCHECK(node != nullptr); - return context->GetEvalTensor(context, node->inputs->data[index]); -} +TfLiteEvalTensor* GetMutableEvalInput(const TfLiteContext* context, + const TfLiteNode* node, int index); // Returns the TfLiteEvalTensor struct for a given input index in a node. -inline const TfLiteEvalTensor* GetEvalInput(const TfLiteContext* context, - const TfLiteNode* node, int index) { - return GetMutableEvalInput(context, node, index); -} +const TfLiteEvalTensor* GetEvalInput(const TfLiteContext* context, + const TfLiteNode* node, int index); // Returns the TfLiteEvalTensor struct for a given output index in a node. -inline TfLiteEvalTensor* GetEvalOutput(const TfLiteContext* context, - const TfLiteNode* node, int index) { - TFLITE_DCHECK(context != nullptr); - TFLITE_DCHECK(node != nullptr); - return context->GetEvalTensor(context, node->outputs->data[index]); -} +TfLiteEvalTensor* GetEvalOutput(const TfLiteContext* context, + const TfLiteNode* node, int index); // Returns data for a TfLiteEvalTensor struct. template @@ -80,6 +69,24 @@ TfLiteStatus CreateWritableTensorDimsWithCopy(TfLiteContext* context, TfLiteTensor* tensor, TfLiteEvalTensor* eval_tensor); +// Returns a blob of payload data. The payload is subjected to interpretation by +// the OP. This is the recommended API for an OP to get an external context. OP +// should use this instead of directly calling GetExternalContext function in +// context. Example usage: +// +// An application can set an external context through interpreter as below +// interpreter->SetMicroExternalContext(pointer_to_your_payload); +// +// Inside an OP that needs this payload, it get the payload pointer by: +// Prepare(TfliteContext * context) { +// ... +// payload_ptr = +// reinterpret_cast(GetMicroExternalContext(context)) +// ... +// } +// +void* GetMicroExternalContext(TfLiteContext* context); + } // namespace micro } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/l2_pool_2d.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/l2_pool_2d.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/l2_pool_2d.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/l2_pool_2d.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/l2norm.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/l2norm.cc similarity index 91% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/l2norm.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/l2norm.cc index 507dea20..930710d4 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/l2norm.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/l2norm.cc @@ -56,12 +56,11 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { TF_LITE_ENSURE(context, NumDimensions(input) <= 4); - TF_LITE_ENSURE(context, output->type == kTfLiteFloat32 || - output->type == kTfLiteUInt8 || - output->type == kTfLiteInt8); + TF_LITE_ENSURE(context, + output->type == kTfLiteFloat32 || output->type == kTfLiteInt8); TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type); - if (output->type == kTfLiteUInt8 || output->type == kTfLiteInt8) { + if (output->type == kTfLiteInt8) { data->input_zero_point = input->params.zero_point; } else if (output->type == kTfLiteFloat32) { data->input_zero_point = 0; @@ -109,12 +108,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output), epsilon); - } else if (output->type == kTfLiteUInt8) { - reference_ops::L2Normalization( - data, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); } else if (output->type == kTfLiteInt8) { const auto input_shape = tflite::micro::GetTensorShape(input); const auto output_shape = tflite::micro::GetTensorShape(output); diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/leaky_relu.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu.cc similarity index 62% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/leaky_relu.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu.cc index fb37f3b4..70ee3856 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/leaky_relu.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu.cc @@ -1,4 +1,4 @@ -/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,23 +21,10 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/types.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/leaky_relu.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" namespace tflite { -namespace { - -// Input/output tensor index. -constexpr int kInputTensor = 0; -constexpr int kOutputTensor = 0; - -struct LeakyReluOpData { - // quantization parameters - int32_t output_multiplier_alpha; - int32_t output_shift_alpha; - int32_t output_multiplier_identity; - int32_t output_shift_identity; - int32_t input_zero_point; - int32_t output_zero_point; -}; template void QuantizeLeakyRelu(const LeakyReluOpData& data, @@ -58,51 +45,11 @@ void QuantizeLeakyRelu(const LeakyReluOpData& data, tflite::micro::GetTensorData(output)); } -TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) { - TF_LITE_ENSURE_EQ(context, NumInputs(node), 1); - TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); - const TfLiteTensor* input; - TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kInputTensor, &input)); - TfLiteTensor* output; - TF_LITE_ENSURE_OK(context, - GetOutputSafe(context, node, kOutputTensor, &output)); - TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type); - - if (output->type == kTfLiteInt8 || output->type == kTfLiteInt16) { - LeakyReluOpData* data = static_cast(node->user_data); - const auto* params = - static_cast(node->builtin_data); - - data->input_zero_point = input->params.zero_point; - data->output_zero_point = output->params.zero_point; - - int output_shift_alpha; - double alpha_multiplier = static_cast( - input->params.scale * params->alpha / output->params.scale); - QuantizeMultiplier(alpha_multiplier, &data->output_multiplier_alpha, - &output_shift_alpha); - data->output_shift_alpha = static_cast(output_shift_alpha); - - int output_shift_identity; - double identity_multiplier = - static_cast(input->params.scale / output->params.scale); - QuantizeMultiplier(identity_multiplier, &data->output_multiplier_identity, - &output_shift_identity); - data->output_shift_identity = static_cast(output_shift_identity); - } - - return kTfLiteOk; -} - void* LeakyReluInit(TfLiteContext* context, const char* buffer, size_t length) { TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); return context->AllocatePersistentBuffer(context, sizeof(LeakyReluOpData)); } -TfLiteStatus LeakyReluPrepare(TfLiteContext* context, TfLiteNode* node) { - return CalculateOpData(context, node); -} - TfLiteStatus LeakyReluEval(TfLiteContext* context, TfLiteNode* node) { const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, kInputTensor); @@ -132,17 +79,14 @@ TfLiteStatus LeakyReluEval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } break; default: - TF_LITE_KERNEL_LOG( - context, "Only float32, int8 are supported by LEAKY_RELU, got %s.", - TfLiteTypeGetName(input->type)); + MicroPrintf("Only float32, int8 are supported by LEAKY_RELU, got %s.", + TfLiteTypeGetName(input->type)); return kTfLiteError; } return kTfLiteError; } -} // namespace - TfLiteRegistration Register_LEAKY_RELU() { return {/*init=*/LeakyReluInit, /*free=*/nullptr, diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu.h new file mode 100644 index 00000000..dfcd6e93 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu.h @@ -0,0 +1,43 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_KERNELS_LEAKY_RELU_H_ +#define TENSORFLOW_LITE_MICRO_KERNELS_LEAKY_RELU_H_ + +#include "tensorflow/lite/c/common.h" + +namespace tflite { + +// Input/output tensor index. +extern const int kInputTensor; +extern const int kOutputTensor; + +struct LeakyReluOpData { + // quantization parameters + int32_t output_multiplier_alpha; + int32_t output_shift_alpha; + int32_t output_multiplier_identity; + int32_t output_shift_identity; + int32_t input_zero_point; + int32_t output_zero_point; +}; + +TfLiteStatus CalculateOpDataLeakyRelu(TfLiteContext* context, TfLiteNode* node); + +TfLiteStatus LeakyReluPrepare(TfLiteContext* context, TfLiteNode* node); + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_KERNELS_LEAKY_RELU_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu_common.cc new file mode 100644 index 00000000..21cc99fc --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/leaky_relu_common.cc @@ -0,0 +1,72 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/leaky_relu.h" +#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" +#include "tensorflow/lite/kernels/internal/types.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/leaky_relu.h" + +namespace tflite { + +// Input/output tensor index. +const int kInputTensor = 0; +const int kOutputTensor = 0; + +TfLiteStatus CalculateOpDataLeakyRelu(TfLiteContext* context, + TfLiteNode* node) { + TF_LITE_ENSURE_EQ(context, NumInputs(node), 1); + TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); + const TfLiteTensor* input; + TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kInputTensor, &input)); + TfLiteTensor* output; + TF_LITE_ENSURE_OK(context, + GetOutputSafe(context, node, kOutputTensor, &output)); + TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type); + + if (output->type == kTfLiteInt8 || output->type == kTfLiteInt16) { + LeakyReluOpData* data = static_cast(node->user_data); + const auto* params = + static_cast(node->builtin_data); + + data->input_zero_point = input->params.zero_point; + data->output_zero_point = output->params.zero_point; + + int output_shift_alpha; + double alpha_multiplier = static_cast( + input->params.scale * params->alpha / output->params.scale); + QuantizeMultiplier(alpha_multiplier, &data->output_multiplier_alpha, + &output_shift_alpha); + data->output_shift_alpha = static_cast(output_shift_alpha); + + int output_shift_identity; + double identity_multiplier = + static_cast(input->params.scale / output->params.scale); + QuantizeMultiplier(identity_multiplier, &data->output_multiplier_identity, + &output_shift_identity); + data->output_shift_identity = static_cast(output_shift_identity); + } + + return kTfLiteOk; +} + +TfLiteStatus LeakyReluPrepare(TfLiteContext* context, TfLiteNode* node) { + return CalculateOpDataLeakyRelu(context, node); +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/log_softmax.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/log_softmax.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/log_softmax.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/log_softmax.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/logical.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/logical.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/logical.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/logical.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/logical.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/logical.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/logical.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/logical.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/logical_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/logical_common.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/logical_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/logical_common.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/logistic.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/logistic.cc similarity index 75% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/logistic.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/logistic.cc index c483d3fc..77f94ec0 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/logistic.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/logistic.cc @@ -25,6 +25,7 @@ limitations under the License. #include "tensorflow/lite/kernels/op_macros.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" #include "tensorflow/lite/micro/kernels/logistic.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" namespace tflite { namespace { @@ -53,9 +54,25 @@ TfLiteStatus LogisticEval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } default: - TF_LITE_KERNEL_LOG(context, "Input %s, output %s not supported.", - TfLiteTypeGetName(input->type), - TfLiteTypeGetName(output->type)); + MicroPrintf("Input %s, output %s not supported.", + TfLiteTypeGetName(input->type), + TfLiteTypeGetName(output->type)); + return kTfLiteError; + } + } else if (input->type == kTfLiteInt16) { + switch (output->type) { + case kTfLiteInt16: { + reference_integer_ops::Logistic( + data->input_multiplier, data->input_left_shift, + NumElements(input->dims), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorData(output)); + return kTfLiteOk; + } + default: + MicroPrintf("Input %s, output %s not supported.", + TfLiteTypeGetName(input->type), + TfLiteTypeGetName(output->type)); return kTfLiteError; } } else if (input->type == kTfLiteInt8) { @@ -70,17 +87,17 @@ TfLiteStatus LogisticEval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } default: - TF_LITE_KERNEL_LOG(context, "Input %s, output %s not supported.", - TfLiteTypeGetName(input->type), - TfLiteTypeGetName(output->type)); + MicroPrintf("Input %s, output %s not supported.", + TfLiteTypeGetName(input->type), + TfLiteTypeGetName(output->type)); return kTfLiteError; } } else { // TODO(b/141211002): Also support other data types once we have supported // temporary tensors in TFLM. - TF_LITE_KERNEL_LOG(context, "Input %s, output %s not supported.", - TfLiteTypeGetName(input->type), - TfLiteTypeGetName(output->type)); + MicroPrintf("Input %s, output %s not supported.", + TfLiteTypeGetName(input->type), + TfLiteTypeGetName(output->type)); return kTfLiteError; } return kTfLiteOk; diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/logistic.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/logistic.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/logistic.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/logistic.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/logistic_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/logistic_common.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/logistic_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/logistic_common.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/maximum_minimum.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/maximum_minimum.cc similarity index 97% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/maximum_minimum.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/maximum_minimum.cc index a7c343bf..a6d358fb 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/maximum_minimum.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/maximum_minimum.cc @@ -88,9 +88,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { case kTfLiteFloat32: TFLiteOperation(context, node, op_context); break; - case kTfLiteUInt8: - TFLiteOperation(context, node, op_context); - break; case kTfLiteInt8: TFLiteOperation(context, node, op_context); break; diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/micro_ops.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_ops.h similarity index 94% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/micro_ops.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_ops.h index 6c27827c..0fac51b7 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/micro_ops.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_ops.h @@ -31,13 +31,19 @@ namespace tflite { // (https://abseil.io/tips/130). Any new ops (or cleanup of existing ops should // have their Register function declarations in the tflite namespace. +TfLiteRegistration Register_ADD(); TfLiteRegistration Register_ADD_N(); +TfLiteRegistration Register_ASSIGN_VARIABLE(); TfLiteRegistration Register_AVERAGE_POOL_2D(); TfLiteRegistration Register_BATCH_TO_SPACE_ND(); +TfLiteRegistration Register_CALL_ONCE(); TfLiteRegistration Register_CAST(); +// TODO(b/160234179): Change custom OPs to also return by value. +TfLiteRegistration* Register_CIRCULAR_BUFFER(); TfLiteRegistration Register_CUMSUM(); TfLiteRegistration Register_DEPTH_TO_SPACE(); TfLiteRegistration Register_DEPTHWISE_CONV_2D(); +TfLiteRegistration Register_DEQUANTIZE(); TfLiteRegistration Register_DIV(); TfLiteRegistration Register_ELU(); TfLiteRegistration Register_EXP(); @@ -56,32 +62,34 @@ TfLiteRegistration Register_LOGICAL_AND(); TfLiteRegistration Register_LOGICAL_OR(); TfLiteRegistration Register_LOGISTIC(); TfLiteRegistration Register_MAX_POOL_2D(); +TfLiteRegistration Register_PRELU(); +TfLiteRegistration Register_MUL(); TfLiteRegistration Register_QUANTIZE(); +TfLiteRegistration Register_READ_VARIABLE(); TfLiteRegistration Register_RELU(); TfLiteRegistration Register_RELU6(); TfLiteRegistration Register_RESIZE_BILINEAR(); TfLiteRegistration Register_SHAPE(); +TfLiteRegistration Register_SLICE(); TfLiteRegistration Register_SPACE_TO_BATCH_ND(); TfLiteRegistration Register_SPACE_TO_DEPTH(); TfLiteRegistration Register_SQUEEZE(); +TfLiteRegistration Register_SUB(); TfLiteRegistration Register_SVDF(); TfLiteRegistration Register_TRANSPOSE(); TfLiteRegistration Register_TRANSPOSE_CONV(); +TfLiteRegistration Register_VAR_HANDLE(); TfLiteRegistration Register_ZEROS_LIKE(); namespace ops { namespace micro { TfLiteRegistration Register_ABS(); -TfLiteRegistration Register_ADD(); TfLiteRegistration Register_ARG_MAX(); TfLiteRegistration Register_ARG_MIN(); TfLiteRegistration Register_CEIL(); -// TODO(b/160234179): Change custom OPs to also return by value. -TfLiteRegistration* Register_CIRCULAR_BUFFER(); TfLiteRegistration Register_CONCATENATION(); TfLiteRegistration Register_COS(); -TfLiteRegistration Register_DEQUANTIZE(); TfLiteRegistration Register_EQUAL(); TfLiteRegistration Register_FLOOR(); TfLiteRegistration Register_GREATER(); @@ -93,13 +101,11 @@ TfLiteRegistration Register_LOGICAL_NOT(); TfLiteRegistration Register_MAXIMUM(); TfLiteRegistration Register_MEAN(); TfLiteRegistration Register_MINIMUM(); -TfLiteRegistration Register_MUL(); TfLiteRegistration Register_NEG(); TfLiteRegistration Register_NOT_EQUAL(); TfLiteRegistration Register_PACK(); TfLiteRegistration Register_PAD(); TfLiteRegistration Register_PADV2(); -TfLiteRegistration Register_PRELU(); TfLiteRegistration Register_REDUCE_MAX(); TfLiteRegistration Register_RESHAPE(); TfLiteRegistration Register_RESIZE_NEAREST_NEIGHBOR(); @@ -111,7 +117,7 @@ TfLiteRegistration Register_SPLIT_V(); TfLiteRegistration Register_SQRT(); TfLiteRegistration Register_SQUARE(); TfLiteRegistration Register_STRIDED_SLICE(); -TfLiteRegistration Register_SUB(); +TfLiteRegistration Register_UNIDIRECTIONAL_SEQUENCE_LSTM(); TfLiteRegistration Register_UNPACK(); TfLiteRegistration Register_L2_NORMALIZATION(); TfLiteRegistration Register_TANH(); diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/micro_utils.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_utils.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/micro_utils.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_utils.h diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.cc new file mode 100644 index 00000000..e8295197 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.cc @@ -0,0 +1,74 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/micro/kernels/mul.h" + +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/integer_ops/mul.h" +#include "tensorflow/lite/kernels/internal/reference/mul.h" +#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/memory_helpers.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" + +namespace tflite { + +TfLiteStatus MulEval(TfLiteContext* context, TfLiteNode* node) { + TFLITE_DCHECK(node->builtin_data != nullptr); + auto* params = reinterpret_cast(node->builtin_data); + + TFLITE_DCHECK(node->user_data != nullptr); + const OpDataMul* data = static_cast(node->user_data); + + const TfLiteEvalTensor* input1 = + tflite::micro::GetEvalInput(context, node, kMulInput1Tensor); + const TfLiteEvalTensor* input2 = + tflite::micro::GetEvalInput(context, node, kMulInput2Tensor); + TfLiteEvalTensor* output = + tflite::micro::GetEvalOutput(context, node, kMulOutputTensor); + + switch (input1->type) { + case kTfLiteInt8: + case kTfLiteInt32: + EvalMulQuantizedReference(context, node, data, input1, input2, output); + break; + case kTfLiteFloat32: + EvalMulFloatReference(context, node, params, data, input1, input2, + output); + break; + default: + MicroPrintf("Type %s (%d) not supported.", + TfLiteTypeGetName(input1->type), input1->type); + return kTfLiteError; + } + + return kTfLiteOk; +} + +TfLiteRegistration Register_MUL() { + return {/*init=*/MulInit, + /*free=*/nullptr, + /*prepare=*/MulPrepare, + /*invoke=*/MulEval, + /*profiling_string=*/nullptr, + /*builtin_code=*/0, + /*custom_name=*/nullptr, + /*version=*/0}; +} + +} // namespace tflite diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.h new file mode 100644 index 00000000..e6d1a9b1 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.h @@ -0,0 +1,65 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_KERNELS_MUL_H_ +#define TENSORFLOW_LITE_MICRO_KERNELS_MUL_H_ + +#include + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" + +namespace tflite { + +extern const int kMulInput1Tensor; +extern const int kMulInput2Tensor; +extern const int kMulOutputTensor; + +struct OpDataMul { + int32_t input1_zero_point; + int32_t input2_zero_point; + + int32_t output_activation_min; + int32_t output_activation_max; + int32_t output_zero_point; + int32_t output_multiplier; + int output_shift; + + float output_activation_min_f32; + float output_activation_max_f32; +}; + +void* MulInit(TfLiteContext* context, const char* buffer, size_t length); + +TfLiteStatus CalculateOpDataMul(TfLiteContext* context, TfLiteNode* node, + TfLiteMulParams* params, OpDataMul* data); + +TfLiteStatus MulPrepare(TfLiteContext* context, TfLiteNode* node); + +void EvalMulQuantizedReference(TfLiteContext* context, TfLiteNode* node, + const OpDataMul* data, + const TfLiteEvalTensor* input1, + const TfLiteEvalTensor* input2, + TfLiteEvalTensor* output); + +void EvalMulFloatReference(TfLiteContext* context, TfLiteNode* node, + TfLiteMulParams* params, const OpDataMul* data, + const TfLiteEvalTensor* input1, + const TfLiteEvalTensor* input2, + TfLiteEvalTensor* output); + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_KERNELS_MUL_H_ diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/mul.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/mul_common.cc similarity index 54% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/mul.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/mul_common.cc index 1eaee5e0..86ab90aa 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/mul.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/mul_common.cc @@ -1,4 +1,4 @@ -/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,48 +13,35 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/lite/kernels/internal/reference/mul.h" - #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/kernels/internal/quantization_util.h" #include "tensorflow/lite/kernels/internal/reference/integer_ops/mul.h" +#include "tensorflow/lite/kernels/internal/reference/mul.h" #include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/mul.h" #include "tensorflow/lite/micro/memory_helpers.h" namespace tflite { -namespace ops { -namespace micro { -namespace mul { -namespace { -constexpr int kInput1Tensor = 0; -constexpr int kInput2Tensor = 1; -constexpr int kOutputTensor = 0; +const int kMulInput1Tensor = 0; +const int kMulInput2Tensor = 1; +const int kMulOutputTensor = 0; -struct OpData { - int32_t input1_zero_point; - int32_t input2_zero_point; +void* MulInit(TfLiteContext* context, const char* buffer, size_t length) { + TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); + return context->AllocatePersistentBuffer(context, sizeof(OpDataMul)); +} - int32_t output_activation_min; - int32_t output_activation_max; - int32_t output_zero_point; - int32_t output_multiplier; - int output_shift; - - float output_activation_min_f32; - float output_activation_max_f32; -}; - -TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node, - TfLiteMulParams* params, OpData* data) { - const TfLiteTensor* input1 = GetInput(context, node, kInput1Tensor); +TfLiteStatus CalculateOpDataMul(TfLiteContext* context, TfLiteNode* node, + TfLiteMulParams* params, OpDataMul* data) { + const TfLiteTensor* input1 = GetInput(context, node, kMulInput1Tensor); TF_LITE_ENSURE(context, input1 != nullptr); - const TfLiteTensor* input2 = GetInput(context, node, kInput2Tensor); + const TfLiteTensor* input2 = GetInput(context, node, kMulInput2Tensor); TF_LITE_ENSURE(context, input2 != nullptr); - TfLiteTensor* output = GetOutput(context, node, kOutputTensor); + TfLiteTensor* output = GetOutput(context, node, kMulOutputTensor); TF_LITE_ENSURE(context, output != nullptr); TF_LITE_ENSURE_EQ(context, NumInputs(node), 2); @@ -76,6 +63,9 @@ TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node, data->input1_zero_point = input1->params.zero_point; data->input2_zero_point = input2->params.zero_point; data->output_zero_point = output->params.zero_point; + } else if (output->type == kTfLiteInt32) { + CalculateActivationRange(params->activation, &data->output_activation_min, + &data->output_activation_max); } else { CalculateActivationRange(params->activation, &data->output_activation_min_f32, @@ -85,11 +75,21 @@ TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node, return kTfLiteOk; } -} // namespace +TfLiteStatus MulPrepare(TfLiteContext* context, TfLiteNode* node) { + TFLITE_DCHECK(node->builtin_data != nullptr); + auto* params = reinterpret_cast(node->builtin_data); -void EvalQuantized(TfLiteContext* context, TfLiteNode* node, const OpData* data, - const TfLiteEvalTensor* input1, - const TfLiteEvalTensor* input2, TfLiteEvalTensor* output) { + TFLITE_DCHECK(node->user_data != nullptr); + OpDataMul* data = static_cast(node->user_data); + + return CalculateOpDataMul(context, node, params, data); +} + +void EvalMulQuantizedReference(TfLiteContext* context, TfLiteNode* node, + const OpDataMul* data, + const TfLiteEvalTensor* input1, + const TfLiteEvalTensor* input2, + TfLiteEvalTensor* output) { tflite::ArithmeticParams op_params = {}; op_params.quantized_activation_min = data->output_activation_min; op_params.quantized_activation_max = data->output_activation_max; @@ -104,28 +104,49 @@ void EvalQuantized(TfLiteContext* context, TfLiteNode* node, const OpData* data, tflite::micro::GetTensorShape(input1), tflite::micro::GetTensorShape(input2), &op_params); - if (need_broadcast) { - reference_integer_ops::BroadcastMul4DSlow( - op_params, tflite::micro::GetTensorShape(input1), - tflite::micro::GetTensorData(input1), - tflite::micro::GetTensorShape(input2), - tflite::micro::GetTensorData(input2), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - } else { - reference_integer_ops::Mul(op_params, tflite::micro::GetTensorShape(input1), - tflite::micro::GetTensorData(input1), - tflite::micro::GetTensorShape(input2), - tflite::micro::GetTensorData(input2), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); + if (input1->type == kTfLiteInt8) { + if (need_broadcast) { + reference_integer_ops::BroadcastMul4DSlow( + op_params, tflite::micro::GetTensorShape(input1), + tflite::micro::GetTensorData(input1), + tflite::micro::GetTensorShape(input2), + tflite::micro::GetTensorData(input2), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + } else { + reference_integer_ops::Mul(op_params, + tflite::micro::GetTensorShape(input1), + tflite::micro::GetTensorData(input1), + tflite::micro::GetTensorShape(input2), + tflite::micro::GetTensorData(input2), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + } + } else if (input1->type == kTfLiteInt32) { + if (need_broadcast) { + reference_ops::BroadcastMul4DSlow( + op_params, tflite::micro::GetTensorShape(input1), + tflite::micro::GetTensorData(input1), + tflite::micro::GetTensorShape(input2), + tflite::micro::GetTensorData(input2), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + } else { + reference_ops::Mul(op_params, tflite::micro::GetTensorShape(input1), + tflite::micro::GetTensorData(input1), + tflite::micro::GetTensorShape(input2), + tflite::micro::GetTensorData(input2), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + } } } -void EvalFloat(TfLiteContext* context, TfLiteNode* node, - TfLiteMulParams* params, const OpData* data, - const TfLiteEvalTensor* input1, const TfLiteEvalTensor* input2, - TfLiteEvalTensor* output) { +void EvalMulFloatReference(TfLiteContext* context, TfLiteNode* node, + TfLiteMulParams* params, const OpDataMul* data, + const TfLiteEvalTensor* input1, + const TfLiteEvalTensor* input2, + TfLiteEvalTensor* output) { tflite::ArithmeticParams op_params = {}; op_params.float_activation_min = data->output_activation_min_f32; op_params.float_activation_max = data->output_activation_max_f32; @@ -152,63 +173,4 @@ void EvalFloat(TfLiteContext* context, TfLiteNode* node, } } -void* Init(TfLiteContext* context, const char* buffer, size_t length) { - TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); - return context->AllocatePersistentBuffer(context, sizeof(OpData)); -} - -TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { - TFLITE_DCHECK(node->builtin_data != nullptr); - auto* params = reinterpret_cast(node->builtin_data); - - TFLITE_DCHECK(node->user_data != nullptr); - OpData* data = static_cast(node->user_data); - - return CalculateOpData(context, node, params, data); -} - -TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { - TFLITE_DCHECK(node->builtin_data != nullptr); - auto* params = reinterpret_cast(node->builtin_data); - - TFLITE_DCHECK(node->user_data != nullptr); - const OpData* data = static_cast(node->user_data); - - const TfLiteEvalTensor* input1 = - tflite::micro::GetEvalInput(context, node, kInput1Tensor); - const TfLiteEvalTensor* input2 = - tflite::micro::GetEvalInput(context, node, kInput2Tensor); - TfLiteEvalTensor* output = - tflite::micro::GetEvalOutput(context, node, kOutputTensor); - - switch (input1->type) { - case kTfLiteInt8: - EvalQuantized(context, node, data, input1, input2, output); - break; - case kTfLiteFloat32: - EvalFloat(context, node, params, data, input1, input2, output); - break; - default: - TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.", - TfLiteTypeGetName(input1->type), input1->type); - return kTfLiteError; - } - - return kTfLiteOk; -} -} // namespace mul - -TfLiteRegistration Register_MUL() { - return {/*init=*/mul::Init, - /*free=*/nullptr, - /*prepare=*/mul::Prepare, - /*invoke=*/mul::Eval, - /*profiling_string=*/nullptr, - /*builtin_code=*/0, - /*custom_name=*/nullptr, - /*version=*/0}; -} - -} // namespace micro -} // namespace ops } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/neg.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/neg.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/neg.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/neg.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/pack.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/pack.cc similarity index 96% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/pack.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/pack.cc index d332fc63..098a0482 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/pack.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/pack.cc @@ -82,10 +82,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { return PackImpl(context, node, output, data->values_count, data->axis); } - case kTfLiteUInt8: { - return PackImpl(context, node, output, data->values_count, - data->axis); - } case kTfLiteInt8: { return PackImpl(context, node, output, data->values_count, data->axis); diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/pad.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/pad.cc similarity index 84% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/pad.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/pad.cc index 5d9d4364..e038de0b 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/pad.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/pad.cc @@ -103,21 +103,14 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { data->params.right_padding[idx] = paddings_data[idx * 2 + 1]; } - if (input->type == kTfLiteInt8 || input->type == kTfLiteUInt8) { + if (input->type == kTfLiteInt8) { if (constant_values == nullptr) { // Quantized Pad requires that 0 is represented in the quantized // range. - if (input->type == kTfLiteUInt8) { - TF_LITE_ENSURE(context, output->params.zero_point >= - std::numeric_limits::min()); - TF_LITE_ENSURE(context, output->params.zero_point <= - std::numeric_limits::max()); - } else { - TF_LITE_ENSURE(context, output->params.zero_point >= - std::numeric_limits::min()); - TF_LITE_ENSURE(context, output->params.zero_point <= - std::numeric_limits::max()); - } + TF_LITE_ENSURE(context, output->params.zero_point >= + std::numeric_limits::min()); + TF_LITE_ENSURE(context, output->params.zero_point <= + std::numeric_limits::max()); } else { // Quantized Pad requires that 'constant_values' is represented in the // same quantized range as the input and output tensors. @@ -164,26 +157,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(output)); } } break; - case kTfLiteUInt8: { - uint8_t pad_value; - if (constant_values == nullptr) { - pad_value = static_cast(data->output_zero_point); - } else { - pad_value = *tflite::micro::GetTensorData(constant_values); - } - if (data->params.resizing_category == ResizingCategory::kImageStyle) { - reference_ops::PadImageStyle( - data->params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), &pad_value, - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - } else { - reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - &pad_value, tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - } - } break; case kTfLiteInt8: { int8_t pad_value; if (constant_values == nullptr) { @@ -204,6 +177,16 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(output)); } } break; + case kTfLiteInt16: { + int16_t pad_value = + constant_values == nullptr + ? 0 + : *tflite::micro::GetTensorData(constant_values); + reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + &pad_value, tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + } break; case kTfLiteInt32: { int32_t pad_value = constant_values == nullptr @@ -220,7 +203,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { TfLiteTypeGetName(input->type)); return kTfLiteError; } -#undef TF_LITE_PAD return kTfLiteOk; } diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/pooling.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/pooling.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/pooling.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/pooling.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/pooling_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling_common.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/pooling_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling_common.cc diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.cc new file mode 100644 index 00000000..dc0c32c0 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.cc @@ -0,0 +1,82 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/kernels/internal/reference/prelu.h" + +#include + +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/prelu.h" + +namespace tflite { + +void* PreluInit(TfLiteContext* context, const char* buffer, size_t length) { + TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); + return context->AllocatePersistentBuffer(context, sizeof(PreluParams)); +} + +TfLiteStatus PreluEval(TfLiteContext* context, TfLiteNode* node) { + TFLITE_DCHECK(node->user_data != nullptr); + const PreluParams& params = + *(static_cast(node->user_data)); + + const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0); + const TfLiteEvalTensor* alpha = tflite::micro::GetEvalInput(context, node, 1); + TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0); + + switch (input->type) { + case kTfLiteFloat32: { + BroadcastPrelu4DSlowFloat(tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(alpha), + tflite::micro::GetTensorData(alpha), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + return kTfLiteOk; + } break; + case kTfLiteInt8: { + reference_ops::BroadcastPrelu4DSlow( + params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(alpha), + tflite::micro::GetTensorData(alpha), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + return kTfLiteOk; + } break; + default: + TF_LITE_KERNEL_LOG( + context, "Only float32 and uint8_t are supported currently, got %d.", + TfLiteTypeGetName(input->type)); + return kTfLiteError; + } +} + +TfLiteRegistration Register_PRELU() { + return {/*init=*/PreluInit, + /*free=*/nullptr, + /*prepare=*/PreluPrepare, + /*invoke=*/PreluEval, + /*profiling_string=*/nullptr, + /*builtin_code=*/0, + /*custom_name=*/nullptr, + /*version=*/0}; +} + +} // namespace tflite diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.h new file mode 100644 index 00000000..571d1e88 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.h @@ -0,0 +1,39 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_KERNELS_PRELU_H_ +#define TENSORFLOW_LITE_MICRO_KERNELS_PRELU_H_ + +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/types.h" + +namespace tflite { + +TfLiteStatus CalculatePreluParams(const TfLiteTensor* input, + const TfLiteTensor* alpha, + TfLiteTensor* output, PreluParams* params); + +void BroadcastPrelu4DSlowFloat(const RuntimeShape& unextended_input1_shape, + const float* input1_data, + const RuntimeShape& unextended_input2_shape, + const float* input2_data, + const RuntimeShape& unextended_output_shape, + float* output_data); + +TfLiteStatus PreluPrepare(TfLiteContext* context, TfLiteNode* node); + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_KERNELS_PRELU_H_ diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/prelu.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu_common.cc similarity index 53% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/prelu.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu_common.cc index b48491d6..8b840fcb 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/prelu.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu_common.cc @@ -1,4 +1,4 @@ -/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,27 +13,22 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/lite/kernels/internal/reference/prelu.h" - #include #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/prelu.h" #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/prelu.h" namespace tflite { -namespace ops { -namespace micro { -namespace activations { -namespace { TfLiteStatus CalculatePreluParams(const TfLiteTensor* input, const TfLiteTensor* alpha, TfLiteTensor* output, PreluParams* params) { - if (output->type == kTfLiteInt8 || output->type == kTfLiteUInt8 || - output->type == kTfLiteInt16) { + if (output->type == kTfLiteInt8 || output->type == kTfLiteInt16) { double real_multiplier_1 = static_cast(input->params.scale) / static_cast(output->params.scale); double real_multiplier_2 = static_cast(input->params.scale) * @@ -52,12 +47,12 @@ TfLiteStatus CalculatePreluParams(const TfLiteTensor* input, return kTfLiteOk; } -} // namespace - -inline void BroadcastPrelu4DSlowFloat( - const RuntimeShape& unextended_input1_shape, const float* input1_data, - const RuntimeShape& unextended_input2_shape, const float* input2_data, - const RuntimeShape& unextended_output_shape, float* output_data) { +void BroadcastPrelu4DSlowFloat(const RuntimeShape& unextended_input1_shape, + const float* input1_data, + const RuntimeShape& unextended_input2_shape, + const float* input2_data, + const RuntimeShape& unextended_output_shape, + float* output_data) { TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4); TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4); TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); @@ -85,11 +80,6 @@ inline void BroadcastPrelu4DSlowFloat( } } -void* PreluInit(TfLiteContext* context, const char* buffer, size_t length) { - TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); - return context->AllocatePersistentBuffer(context, sizeof(PreluParams)); -} - TfLiteStatus PreluPrepare(TfLiteContext* context, TfLiteNode* node) { TFLITE_DCHECK(node->user_data != nullptr); PreluParams* params = static_cast(node->user_data); @@ -104,66 +94,4 @@ TfLiteStatus PreluPrepare(TfLiteContext* context, TfLiteNode* node) { return CalculatePreluParams(input, alpha, output, params); } -TfLiteStatus PreluEval(TfLiteContext* context, TfLiteNode* node) { - TFLITE_DCHECK(node->user_data != nullptr); - const PreluParams& params = - *(static_cast(node->user_data)); - - const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0); - const TfLiteEvalTensor* alpha = tflite::micro::GetEvalInput(context, node, 1); - TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0); - - switch (input->type) { - case kTfLiteFloat32: { - BroadcastPrelu4DSlowFloat(tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(alpha), - tflite::micro::GetTensorData(alpha), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - return kTfLiteOk; - } break; - case kTfLiteUInt8: { - reference_ops::BroadcastPrelu4DSlow( - params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(alpha), - tflite::micro::GetTensorData(alpha), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - return kTfLiteOk; - } break; - case kTfLiteInt8: { - reference_ops::BroadcastPrelu4DSlow( - params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(alpha), - tflite::micro::GetTensorData(alpha), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - return kTfLiteOk; - } break; - default: - TF_LITE_KERNEL_LOG( - context, "Only float32 and uint8_t are supported currently, got %d.", - TfLiteTypeGetName(input->type)); - return kTfLiteError; - } -} - -} // namespace activations - -TfLiteRegistration Register_PRELU() { - return {/*init=*/activations::PreluInit, - /*free=*/nullptr, - /*prepare=*/activations::PreluPrepare, - /*invoke=*/activations::PreluEval, - /*profiling_string=*/nullptr, - /*builtin_code=*/0, - /*custom_name=*/nullptr, - /*version=*/0}; -} - -} // namespace micro -} // namespace ops } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/quantize.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/quantize.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/quantize.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/quantize.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/quantize.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/quantize.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/quantize.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/quantize.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/quantize_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/quantize_common.cc similarity index 84% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/quantize_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/quantize_common.cc index 39fdd76a..459b0966 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/quantize_common.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/quantize_common.cc @@ -13,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include + #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/kernels/internal/quantization_util.h" #include "tensorflow/lite/kernels/internal/reference/quantize.h" @@ -21,6 +23,7 @@ limitations under the License. #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" #include "tensorflow/lite/micro/kernels/quantize.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" #include "tensorflow/lite/micro/micro_utils.h" namespace tflite { @@ -48,9 +51,9 @@ TfLiteStatus PrepareQuantizeReference(TfLiteContext* context, TF_LITE_ENSURE(context, affine_quantization->scale); TF_LITE_ENSURE(context, affine_quantization->scale->size == 1); - TF_LITE_ENSURE(context, input->type == kTfLiteFloat32 || - input->type == kTfLiteInt16 || - input->type == kTfLiteInt8); + TF_LITE_ENSURE(context, + input->type == kTfLiteFloat32 || input->type == kTfLiteInt32 || + input->type == kTfLiteInt16 || input->type == kTfLiteInt8); TF_LITE_ENSURE(context, output->type == kTfLiteInt8 || output->type == kTfLiteInt16 || output->type == kTfLiteInt32); @@ -60,7 +63,9 @@ TfLiteStatus PrepareQuantizeReference(TfLiteContext* context, (input->type == kTfLiteInt8 && output->type == kTfLiteInt16) || (input->type == kTfLiteInt8 && output->type == kTfLiteInt32) || (input->type == kTfLiteInt16 && output->type == kTfLiteInt16) || - (input->type == kTfLiteInt16 && output->type == kTfLiteInt32)) { + (input->type == kTfLiteInt16 && output->type == kTfLiteInt32) || + (input->type == kTfLiteInt32 && output->type == kTfLiteInt8) || + (input->type == kTfLiteInt32 && output->type == kTfLiteInt16)) { double effective_scale = static_cast(input->params.scale) / static_cast(output->params.scale); @@ -104,6 +109,29 @@ TfLiteStatus EvalQuantizeReference(TfLiteContext* context, TfLiteNode* node) { TfLiteTypeGetName(output->type)); return kTfLiteError; } + } else if (input->type == kTfLiteInt32) { + size_t size = ElementCount(*input->dims); + switch (output->type) { + case kTfLiteInt8: + reference_ops::Requantize( + tflite::micro::GetTensorData(input), size, + data->requantize_output_multiplier, data->requantize_output_shift, + data->input_zero_point, data->quantization_params.zero_point, + tflite::micro::GetTensorData(output)); + break; + case kTfLiteInt16: + reference_ops::Requantize( + tflite::micro::GetTensorData(input), size, + data->requantize_output_multiplier, data->requantize_output_shift, + data->input_zero_point, data->quantization_params.zero_point, + tflite::micro::GetTensorData(output)); + break; + default: + TF_LITE_KERNEL_LOG(context, "Input %s, output %s not supported.", + TfLiteTypeGetName(input->type), + TfLiteTypeGetName(output->type)); + return kTfLiteError; + } } else if (input->type == kTfLiteInt16) { size_t size = ElementCount(*input->dims); switch (output->type) { diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/read_variable.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/read_variable.cc new file mode 100644 index 00000000..024b511a --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/read_variable.cc @@ -0,0 +1,94 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include + +#include + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/compatibility.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/memory_helpers.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" +#include "tensorflow/lite/micro/micro_graph.h" +#include "tensorflow/lite/micro/micro_resource_variable.h" +#include "tensorflow/lite/schema/schema_generated.h" + +namespace tflite { + +namespace { + +constexpr int kInputVariableId = 0; +constexpr int kOutputValue = 0; + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + TFLITE_DCHECK(NumInputs(node) == 1); + TFLITE_DCHECK(NumOutputs(node) == 1); + + const TfLiteTensor* input_resource_id_tensor = + GetInput(context, node, kInputVariableId); + + TFLITE_DCHECK(input_resource_id_tensor != nullptr); + TFLITE_DCHECK(input_resource_id_tensor->type == kTfLiteResource); + TFLITE_DCHECK(NumElements(input_resource_id_tensor) == 1); + + return kTfLiteOk; +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteEvalTensor* input_resource_id_tensor = + tflite::micro::GetEvalInput(context, node, kInputVariableId); + TFLITE_DCHECK(input_resource_id_tensor != nullptr); + + TfLiteEvalTensor* output_value = + tflite::micro::GetEvalOutput(context, node, kOutputValue); + TFLITE_DCHECK(output_value != nullptr); + + // Casting to TfliteIntArray is required since we are re-using + // GetExecutionPlan from TfLiteContext. On TFLM this method returns a + // MicroGraph. + // TODO(b/188226309): Design a cleaner way to get a graph from kernel context. + MicroGraph* graph_info; + context->GetExecutionPlan(context, + reinterpret_cast(&graph_info)); + MicroResourceVariables* resources = graph_info->GetResourceVariables(); + if (resources == nullptr) { + MicroPrintf( + "READ_VARIABLE requires resource variables. Please create " + "ResourceVariables and pass it to the interpreter."); + return kTfLiteError; + } + TF_LITE_ENSURE_OK( + context, + resources->Read(input_resource_id_tensor->data.i32[0], output_value)); + return kTfLiteOk; +} + +} // namespace. + +TfLiteRegistration Register_READ_VARIABLE() { + return {/*init=*/nullptr, + /*free=*/nullptr, + /*prepare=*/Prepare, + /*invoke=*/Eval, + /*profiling_string=*/nullptr, + /*builtin_code=*/0, + /*custom_name=*/nullptr, + /*version=*/0}; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/reduce.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/reduce.cc similarity index 86% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/reduce.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/reduce.cc index 665320bd..6339c98b 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/reduce.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/reduce.cc @@ -110,8 +110,7 @@ TfLiteStatus PrepareMeanOrSum(TfLiteContext* context, TfLiteNode* node) { } int output_size = NumElements(output); - if (input->type == kTfLiteInt8 || input->type == kTfLiteUInt8 || - input->type == kTfLiteInt16) { + if (input->type == kTfLiteInt8 || input->type == kTfLiteInt16) { context->RequestScratchBufferInArena(context, output_size * sizeof(int32_t), &op_data->temp_buffer_idx); op_data->input_zp = input->params.zero_point; @@ -251,43 +250,6 @@ TfLiteStatus EvalMean(TfLiteContext* context, TfLiteNode* node) { temp_buffer, false)); } } break; - case kTfLiteUInt8: { - // Defer to specialized implementation for 4D Mean across axes 1 & 2. - if (params->keep_dims && special_case_4d_axes_1_and_2) { - reference_ops::Mean(op_params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - op_data->input_zp, op_data->input_scale, - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output), - op_data->output_zp, op_data->output_scale); - } else if (op_data->input_zp == op_data->output_zp && - op_data->input_scale == op_data->output_scale) { - uint32_t* temp_buffer = static_cast( - context->GetScratchBuffer(context, op_data->temp_buffer_idx)); - TF_LITE_ENSURE( - context, - reference_ops::Mean(tflite::micro::GetTensorData(input), - input->dims->data, input->dims->size, - tflite::micro::GetTensorData(output), - output->dims->data, output->dims->size, - tflite::micro::GetTensorData(axis), - num_axis, params->keep_dims, temp_index, - resolved_axis, temp_buffer)); - } else { - uint32_t* temp_buffer = static_cast( - context->GetScratchBuffer(context, op_data->temp_buffer_idx)); - TF_LITE_ENSURE( - context, - reference_ops::QuantizedMeanOrSum( - tflite::micro::GetTensorData(input), op_data->input_zp, - op_data->input_scale, input->dims->data, input->dims->size, - tflite::micro::GetTensorData(output), - op_data->output_zp, op_data->output_scale, output->dims->data, - output->dims->size, tflite::micro::GetTensorData(axis), - num_axis, params->keep_dims, temp_index, resolved_axis, - temp_buffer, false)); - } - } break; default: TF_LITE_ENSURE_MSG(context, false, "Currently, only float32, int8 or uint8 input type " diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/reshape.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/reshape.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/reshape.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/reshape.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/resize_bilinear.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_bilinear.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/resize_bilinear.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_bilinear.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc similarity index 92% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc index 971de83c..63c302bb 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc @@ -21,6 +21,7 @@ limitations under the License. #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/kernels/op_macros.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" namespace tflite { namespace ops { @@ -78,14 +79,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(size), tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); - } else if (output->type == kTfLiteUInt8) { - reference_ops::ResizeNearestNeighbor( - op_params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(size), - tflite::micro::GetTensorData(size), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); } else if (output->type == kTfLiteInt8) { reference_ops::ResizeNearestNeighbor( op_params, tflite::micro::GetTensorShape(input), @@ -94,10 +87,18 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(size), tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); + } else if (output->type == kTfLiteInt16) { + reference_ops::ResizeNearestNeighbor( + op_params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(size), + tflite::micro::GetTensorData(size), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); } else { - TF_LITE_KERNEL_LOG(context, - "Output type is %d, requires float, uint8_t or int8_t.", - output->type); + MicroPrintf("Output tensor type %s (%d) not supported.", + TfLiteTypeGetName(output->type), output->type); + return kTfLiteError; } diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/round.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/round.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/round.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/round.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/shape.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/shape.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/shape.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/shape.cc diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/slice.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/slice.cc new file mode 100644 index 00000000..51ee70de --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/slice.cc @@ -0,0 +1,152 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/kernels/internal/reference/slice.h" + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" + +namespace tflite { + +namespace { + +constexpr int kInputTensor = 0; +constexpr int kBeginTensor = 1; +constexpr int kSizeTensor = 2; +constexpr int kOutputTensor = 0; + +const int kMaxDim = 5; + +template +void GetBeginAndSizeVectors(int dimensions, const TfLiteEvalTensor* begin, + const TfLiteEvalTensor* size, int32_t* begins, + int32_t* sizes) { + int offset = kMaxDim - dimensions; + for (int idx = 0; idx < dimensions; ++idx) { + begins[offset + idx] = tflite::micro::GetTensorData(begin)[idx]; + sizes[offset + idx] = tflite::micro::GetTensorData(size)[idx]; + } +} + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + TF_LITE_ENSURE_EQ(context, NumInputs(node), 3); + TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); + + const TfLiteTensor* input = GetInput(context, node, kInputTensor); + TFLITE_DCHECK(input != nullptr); + const TfLiteTensor* begin = GetInput(context, node, kBeginTensor); + TFLITE_DCHECK(begin != nullptr); + const TfLiteTensor* size = GetInput(context, node, kSizeTensor); + TFLITE_DCHECK(size != nullptr); + TfLiteTensor* output = GetOutput(context, node, kOutputTensor); + TFLITE_DCHECK(output != nullptr); + + // Ensure validity of input tensor and its dimension. + TFLITE_DCHECK(input->type == output->type); + TFLITE_DCHECK(begin->type == size->type); + TFLITE_DCHECK(begin->type == kTfLiteInt32 || begin->type == kTfLiteInt64); + TFLITE_DCHECK(size->type == kTfLiteInt32 || size->type == kTfLiteInt64); + TFLITE_DCHECK(NumDimensions(begin) == 1); + TFLITE_DCHECK(NumDimensions(size) == 1); + TFLITE_DCHECK(NumElements(begin) == NumElements(size)); + TFLITE_DCHECK(NumDimensions(input) <= kMaxDim); + return kTfLiteOk; +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteEvalTensor* input = + tflite::micro::GetEvalInput(context, node, kInputTensor); + const TfLiteEvalTensor* begin = + tflite::micro::GetEvalInput(context, node, kBeginTensor); + const TfLiteEvalTensor* size = + tflite::micro::GetEvalInput(context, node, kSizeTensor); + TfLiteEvalTensor* output = + tflite::micro::GetEvalOutput(context, node, kOutputTensor); + + tflite::SliceParams op_params; + op_params.begin_count = kMaxDim; + op_params.size_count = kMaxDim; + for (int i = 0; i < kMaxDim; ++i) { + op_params.begin[i] = 0; + op_params.size[i] = 1; + } + + if (begin->type == kTfLiteInt32) { + GetBeginAndSizeVectors(input->dims->size, begin, size, + op_params.begin, op_params.size); + } else if (begin->type == kTfLiteInt64) { + GetBeginAndSizeVectors(input->dims->size, begin, size, + op_params.begin, op_params.size); + } else { + TF_LITE_KERNEL_LOG(context, "Begin tensor type %s (%d) not supported.", + TfLiteTypeGetName(input->type), input->type); + return kTfLiteError; + } + + switch (input->type) { + case kTfLiteFloat32: + reference_ops::Slice(op_params, + tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + break; + case kTfLiteInt32: + reference_ops::Slice( + op_params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + break; + case kTfLiteInt8: + reference_ops::Slice( + op_params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + break; + case kTfLiteInt16: + reference_ops::Slice( + op_params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + break; + default: + MicroPrintf("Input tensor type %s (%d) not supported.", + TfLiteTypeGetName(input->type), input->type); + return kTfLiteError; + } + return kTfLiteOk; +} + +} // namespace + +TfLiteRegistration Register_SLICE() { + return {/*init=*/nullptr, + /*free=*/nullptr, + /*prepare=*/Prepare, + /*invoke=*/Eval, + /*profiling_string=*/nullptr, + /*builtin_code=*/0, + /*custom_name=*/nullptr, + /*version=*/0}; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/softmax.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/softmax.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/softmax.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/softmax.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/softmax_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax_common.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/softmax_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax_common.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/space_to_batch_nd.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/space_to_batch_nd.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/space_to_batch_nd.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/space_to_batch_nd.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/space_to_depth.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/space_to_depth.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/space_to_depth.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/space_to_depth.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/split.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/split.cc similarity index 94% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/split.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/split.cc index a1236d71..82f17228 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/split.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/split.cc @@ -18,6 +18,7 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" namespace tflite { namespace ops { @@ -95,9 +96,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { case kTfLiteFloat32: { return SplitImpl(context, node, input, axis_value); } - case kTfLiteUInt8: { - return SplitImpl(context, node, input, axis_value); - } case kTfLiteInt8: { return SplitImpl(context, node, input, axis_value); } @@ -108,11 +106,10 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { return SplitImpl(context, node, input, axis_value); } default: - TF_LITE_KERNEL_LOG(context, "Type %s currently not supported.", - TfLiteTypeGetName(input->type)); + MicroPrintf("Type %s currently not supported.", + TfLiteTypeGetName(input->type)); return kTfLiteError; } -#undef TF_LITE_SPLIT return kTfLiteOk; } diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/split_v.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/split_v.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/split_v.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/split_v.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/squeeze.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/squeeze.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/squeeze.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/squeeze.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/strided_slice.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/strided_slice.cc similarity index 98% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/strided_slice.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/strided_slice.cc index 6985a4e2..58582bbf 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/strided_slice.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/strided_slice.cc @@ -153,13 +153,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); break; - case kTfLiteUInt8: - reference_ops::StridedSlice( - op_params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - break; case kTfLiteInt8: reference_ops::StridedSlice(op_params, tflite::micro::GetTensorShape(input), @@ -174,6 +167,13 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); break; + case kTfLiteInt32: + reference_ops::StridedSlice( + op_params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + break; default: TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.", TfLiteTypeGetName(input->type), input->type); diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/sub.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/sub.cc similarity index 51% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/sub.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/sub.cc index 9c75bf21..de99149f 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/sub.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/sub.cc @@ -1,4 +1,4 @@ -/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/lite/kernels/internal/reference/sub.h" +#include "tensorflow/lite/micro/kernels/sub.h" #include "tensorflow/lite/c/builtin_op_data.h" #include "tensorflow/lite/c/common.h" @@ -21,115 +21,23 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/quantization_util.h" #include "tensorflow/lite/kernels/internal/reference/add.h" #include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" +#include "tensorflow/lite/kernels/internal/reference/sub.h" #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" #include "tensorflow/lite/kernels/internal/types.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/kernels/op_macros.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" namespace tflite { -namespace ops { -namespace micro { -namespace sub { -constexpr int kInputTensor1 = 0; -constexpr int kInputTensor2 = 1; -constexpr int kOutputTensor = 0; - -struct OpData { - bool requires_broadcast; - - // These fields are used in both the general 8-bit -> 8bit quantized path, - // and the special 16-bit -> 16bit quantized path - int input1_shift; - int input2_shift; - int32_t output_activation_min; - int32_t output_activation_max; - - // These fields are used only in the general 8-bit -> 8bit quantized path - int32_t input1_multiplier; - int32_t input2_multiplier; - int32_t output_multiplier; - int output_shift; - int left_shift; - int32_t input1_offset; - int32_t input2_offset; - int32_t output_offset; -}; - -TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteSubParams* params, - const TfLiteTensor* input1, - const TfLiteTensor* input2, TfLiteTensor* output, - OpData* data) { - data->requires_broadcast = !HaveSameShapes(input1, input2); - - if (output->type == kTfLiteUInt8 || output->type == kTfLiteInt8 || - output->type == kTfLiteInt16) { - // 8bit -> 8bit general quantized path, with general rescalings - data->input1_offset = -input1->params.zero_point; - data->input2_offset = -input2->params.zero_point; - data->output_offset = output->params.zero_point; - - // The shift is set to 15 in case of 16-bit and 20 in case of 8-bit, - // accordingly. In case of 16-bit we have 65535 << 15 which is less than 1 - // << 31, therefore the addition will still fit in a 32 bit accumulator. - data->left_shift = output->type == kTfLiteInt16 ? 15 : 20; - const float twice_max_input_scale = - 2 * std::max(input1->params.scale, input2->params.scale); - const double real_input1_multiplier = - static_cast(input1->params.scale / twice_max_input_scale); - const double real_input2_multiplier = - static_cast(input2->params.scale / twice_max_input_scale); - const double real_output_multiplier = - static_cast(twice_max_input_scale / - ((1 << data->left_shift) * output->params.scale)); - - QuantizeMultiplierSmallerThanOneExp( - real_input1_multiplier, &data->input1_multiplier, &data->input1_shift); - - QuantizeMultiplierSmallerThanOneExp( - real_input2_multiplier, &data->input2_multiplier, &data->input2_shift); - - // Use add kernel for 16-bit sub, since it supports output requantization. - // This matches behavior in TFLite. - data->input2_multiplier *= (output->type == kTfLiteInt16) ? -1 : 1; - QuantizeMultiplierSmallerThanOneExp( - real_output_multiplier, &data->output_multiplier, &data->output_shift); - - TF_LITE_ENSURE_STATUS(CalculateActivationRangeQuantized( - context, params->activation, output, &data->output_activation_min, - &data->output_activation_max)); - } - - return kTfLiteOk; -} - -void* Init(TfLiteContext* context, const char* buffer, size_t length) { +void* SubInit(TfLiteContext* context, const char* buffer, size_t length) { TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); - return context->AllocatePersistentBuffer(context, sizeof(OpData)); -} - -TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { - TFLITE_DCHECK(node->user_data != nullptr); - TFLITE_DCHECK(node->builtin_data != nullptr); - - OpData* data = static_cast(node->user_data); - auto* params = reinterpret_cast(node->builtin_data); - - const TfLiteTensor* input1 = GetInput(context, node, kInputTensor1); - TF_LITE_ENSURE(context, input1 != nullptr); - const TfLiteTensor* input2 = GetInput(context, node, kInputTensor2); - TF_LITE_ENSURE(context, input2 != nullptr); - TfLiteTensor* output = GetOutput(context, node, kOutputTensor); - TF_LITE_ENSURE(context, output != nullptr); - - TF_LITE_ENSURE_STATUS( - CalculateOpData(context, params, input1, input2, output, data)); - return kTfLiteOk; + return context->AllocatePersistentBuffer(context, sizeof(OpDataSub)); } void EvalSub(TfLiteContext* context, TfLiteNode* node, TfLiteSubParams* params, - const OpData* data, const TfLiteEvalTensor* input1, + const OpDataSub* data, const TfLiteEvalTensor* input1, const TfLiteEvalTensor* input2, TfLiteEvalTensor* output) { float output_activation_min, output_activation_max; CalculateActivationRange(params->activation, &output_activation_min, @@ -156,7 +64,7 @@ void EvalSub(TfLiteContext* context, TfLiteNode* node, TfLiteSubParams* params, } TfLiteStatus EvalSubQuantized(TfLiteContext* context, TfLiteNode* node, - TfLiteSubParams* params, const OpData* data, + TfLiteSubParams* params, const OpDataSub* data, const TfLiteEvalTensor* input1, const TfLiteEvalTensor* input2, TfLiteEvalTensor* output) { @@ -180,7 +88,7 @@ TfLiteStatus EvalSubQuantized(TfLiteContext* context, TfLiteNode* node, switch (output->type) { case kTfLiteInt8: { if (need_broadcast) { - tflite::reference_ops::BroadcastSubSlow( + tflite::reference_ops::BroadcastQuantSubSlow( op_params, tflite::micro::GetTensorShape(input1), tflite::micro::GetTensorData(input1), tflite::micro::GetTensorShape(input2), @@ -200,7 +108,7 @@ TfLiteStatus EvalSubQuantized(TfLiteContext* context, TfLiteNode* node, } case kTfLiteInt16: { if (need_broadcast) { - tflite::reference_ops::BroadcastAdd4DSlow( + tflite::reference_ops::BroadcastQuantSubSlow( op_params, tflite::micro::GetTensorShape(input1), tflite::micro::GetTensorData(input1), tflite::micro::GetTensorShape(input2), @@ -208,85 +116,60 @@ TfLiteStatus EvalSubQuantized(TfLiteContext* context, TfLiteNode* node, tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); } else { - tflite::reference_ops::Add( + tflite::reference_ops::Sub( op_params, tflite::micro::GetTensorShape(input1), tflite::micro::GetTensorData(input1), tflite::micro::GetTensorShape(input2), tflite::micro::GetTensorData(input2), tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output), false); - } - break; - } - case kTfLiteUInt8: { - if (need_broadcast) { - tflite::reference_ops::BroadcastSubSlow( - op_params, tflite::micro::GetTensorShape(input1), - tflite::micro::GetTensorData(input1), - tflite::micro::GetTensorShape(input2), - tflite::micro::GetTensorData(input2), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - } else { - tflite::reference_ops::Sub( - op_params, tflite::micro::GetTensorShape(input1), - tflite::micro::GetTensorData(input1), - tflite::micro::GetTensorShape(input2), - tflite::micro::GetTensorData(input2), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); + tflite::micro::GetTensorData(output)); } break; } default: - TF_LITE_KERNEL_LOG(context, "Quantized type %s not currently supported.", - TfLiteTypeGetName(output->type)); + MicroPrintf("Quantized type %s not currently supported.", + TfLiteTypeGetName(output->type)); return kTfLiteError; } return kTfLiteOk; } -TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { +TfLiteStatus SubEval(TfLiteContext* context, TfLiteNode* node) { auto* params = reinterpret_cast(node->builtin_data); const TfLiteEvalTensor* input1 = - tflite::micro::GetEvalInput(context, node, kInputTensor1); + tflite::micro::GetEvalInput(context, node, kSubInputTensor1); const TfLiteEvalTensor* input2 = - tflite::micro::GetEvalInput(context, node, kInputTensor2); + tflite::micro::GetEvalInput(context, node, kSubInputTensor2); TfLiteEvalTensor* output = - tflite::micro::GetEvalOutput(context, node, kOutputTensor); + tflite::micro::GetEvalOutput(context, node, kSubOutputTensor); TFLITE_DCHECK(node->user_data != nullptr); - const OpData& data = *(static_cast(node->user_data)); + const OpDataSub& data = *(static_cast(node->user_data)); if (output->type == kTfLiteFloat32) { EvalSub(context, node, params, &data, input1, input2, output); - } else if (output->type == kTfLiteUInt8 || output->type == kTfLiteInt8 || - output->type == kTfLiteInt16) { + } else if (output->type == kTfLiteInt8 || output->type == kTfLiteInt16) { TF_LITE_ENSURE_OK(context, EvalSubQuantized(context, node, params, &data, input1, input2, output)); } else { - TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.", - TfLiteTypeGetName(output->type), output->type); + MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(output->type), + output->type); return kTfLiteError; } return kTfLiteOk; } -} // namespace sub - TfLiteRegistration Register_SUB() { - return {/*init=*/sub::Init, + return {/*init=*/SubInit, /*free=*/nullptr, - /*prepare=*/sub::Prepare, - /*invoke=*/sub::Eval, + /*prepare=*/SubPrepare, + /*invoke=*/SubEval, /*profiling_string=*/nullptr, /*builtin_code=*/0, /*custom_name=*/nullptr, /*version=*/0}; } -} // namespace micro -} // namespace ops } // namespace tflite diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/sub.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/sub.h new file mode 100644 index 00000000..29900221 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/sub.h @@ -0,0 +1,60 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_KERNELS_SUB_H_ +#define TENSORFLOW_LITE_MICRO_KERNELS_SUB_H_ + +#include + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" + +namespace tflite { + +extern const int kSubInputTensor1; +extern const int kSubInputTensor2; +extern const int kSubOutputTensor; + +struct OpDataSub { + bool requires_broadcast; + + // These fields are used in both the general 8-bit -> 8bit quantized path, + // and the special 16-bit -> 16bit quantized path + int input1_shift; + int input2_shift; + int32_t output_activation_min; + int32_t output_activation_max; + + // These fields are used only in the general 8-bit -> 8bit quantized path + int32_t input1_multiplier; + int32_t input2_multiplier; + int32_t output_multiplier; + int output_shift; + int left_shift; + int32_t input1_offset; + int32_t input2_offset; + int32_t output_offset; +}; + +TfLiteStatus CalculateOpDataSub(TfLiteContext* context, TfLiteSubParams* params, + const TfLiteTensor* input1, + const TfLiteTensor* input2, + TfLiteTensor* output, OpDataSub* data); + +TfLiteStatus SubPrepare(TfLiteContext* context, TfLiteNode* node); + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_KERNELS_SUB_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/sub_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/sub_common.cc new file mode 100644 index 00000000..bb30780b --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/sub_common.cc @@ -0,0 +1,98 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/add.h" +#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" +#include "tensorflow/lite/kernels/internal/reference/sub.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/internal/types.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/kernels/op_macros.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/sub.h" + +namespace tflite { + +const int kSubInputTensor1 = 0; +const int kSubInputTensor2 = 1; +const int kSubOutputTensor = 0; + +TfLiteStatus CalculateOpDataSub(TfLiteContext* context, TfLiteSubParams* params, + const TfLiteTensor* input1, + const TfLiteTensor* input2, + TfLiteTensor* output, OpDataSub* data) { + data->requires_broadcast = !HaveSameShapes(input1, input2); + + if (output->type == kTfLiteInt8 || output->type == kTfLiteInt16) { + // 8bit -> 8bit general quantized path, with general rescalings + data->input1_offset = -input1->params.zero_point; + data->input2_offset = -input2->params.zero_point; + data->output_offset = output->params.zero_point; + + // The shift is set to 15 in case of 16-bit and 20 in case of 8-bit, + // accordingly. In case of 16-bit we have 65535 << 15 which is less than 1 + // << 31, therefore the addition will still fit in a 32 bit accumulator. + data->left_shift = output->type == kTfLiteInt16 ? 15 : 20; + const float twice_max_input_scale = + 2 * std::max(input1->params.scale, input2->params.scale); + const double real_input1_multiplier = + static_cast(input1->params.scale / twice_max_input_scale); + const double real_input2_multiplier = + static_cast(input2->params.scale / twice_max_input_scale); + const double real_output_multiplier = + static_cast(twice_max_input_scale / + ((1 << data->left_shift) * output->params.scale)); + + QuantizeMultiplierSmallerThanOneExp( + real_input1_multiplier, &data->input1_multiplier, &data->input1_shift); + + QuantizeMultiplierSmallerThanOneExp( + real_input2_multiplier, &data->input2_multiplier, &data->input2_shift); + + QuantizeMultiplierSmallerThanOneExp( + real_output_multiplier, &data->output_multiplier, &data->output_shift); + + TF_LITE_ENSURE_STATUS(CalculateActivationRangeQuantized( + context, params->activation, output, &data->output_activation_min, + &data->output_activation_max)); + } + + return kTfLiteOk; +} + +TfLiteStatus SubPrepare(TfLiteContext* context, TfLiteNode* node) { + TFLITE_DCHECK(node->user_data != nullptr); + TFLITE_DCHECK(node->builtin_data != nullptr); + + OpDataSub* data = static_cast(node->user_data); + auto* params = reinterpret_cast(node->builtin_data); + + const TfLiteTensor* input1 = GetInput(context, node, kSubInputTensor1); + TF_LITE_ENSURE(context, input1 != nullptr); + const TfLiteTensor* input2 = GetInput(context, node, kSubInputTensor2); + TF_LITE_ENSURE(context, input2 != nullptr); + TfLiteTensor* output = GetOutput(context, node, kSubOutputTensor); + TF_LITE_ENSURE(context, output != nullptr); + + TF_LITE_ENSURE_STATUS( + CalculateOpDataSub(context, params, input1, input2, output, data)); + return kTfLiteOk; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/svdf.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf.cc similarity index 75% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/svdf.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf.cc index cd22e31b..f8a2bed2 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/svdf.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf.cc @@ -26,6 +26,7 @@ limitations under the License. #include "tensorflow/lite/kernels/op_macros.h" #include "tensorflow/lite/micro/kernels/activation_utils.h" #include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" #include "tensorflow/lite/micro/micro_utils.h" namespace tflite { @@ -33,13 +34,13 @@ namespace { void* Init(TfLiteContext* context, const char* buffer, size_t length) { TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); - return context->AllocatePersistentBuffer(context, sizeof(OpData)); + return context->AllocatePersistentBuffer(context, sizeof(OpDataSvdf)); } TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { auto* params = reinterpret_cast(node->builtin_data); TFLITE_DCHECK(node->user_data != nullptr); - const OpData& data = *(static_cast(node->user_data)); + const OpDataSvdf& data = *(static_cast(node->user_data)); const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, kSvdfInputTensor); @@ -66,16 +67,31 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { } case kTfLiteInt8: { - EvalIntegerSvdfReference(context, node, input, weights_feature, - weights_time, bias, params, activation_state, - output, data); - return kTfLiteOk; - break; + switch (weights_time->type) { + case kTfLiteInt16: { + EvalInt16SvdfReference(context, node, input, weights_feature, + weights_time, bias, params, activation_state, + output, data); + return kTfLiteOk; + break; + } + case kTfLiteInt8: { + EvalInt8SvdfReference(context, node, input, weights_feature, + weights_time, bias, params, activation_state, + output, data); + return kTfLiteOk; + break; + } + default: + MicroPrintf("Type %s not currently supported.", + TfLiteTypeGetName(weights_time->type)); + return kTfLiteError; + } } default: - TF_LITE_KERNEL_LOG(context, "Type %s not currently supported.", - TfLiteTypeGetName(weights_feature->type)); + MicroPrintf("Type %s not currently supported.", + TfLiteTypeGetName(weights_feature->type)); return kTfLiteError; } return kTfLiteOk; diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/svdf.h b/code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf.h similarity index 51% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/svdf.h rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf.h index d04787be..7a04a5c6 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/svdf.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf.h @@ -20,7 +20,7 @@ limitations under the License. namespace tflite { -struct OpData { +struct OpDataSvdf { int32_t effective_scale_1_a; int32_t effective_scale_2_a; // b versions of each scale are kept at int since the numbers are just the @@ -33,6 +33,7 @@ struct OpData { // Cached tensor zero point values for quantized operations. int input_zero_point; int output_zero_point; + int activation_state_zero_point; }; // Input tensors. @@ -46,16 +47,26 @@ extern const int kSvdfInputActivationStateTensor; // Output tensor. extern const int kSvdfOutputTensor; -// TensorflowLite Micro-specific reference implementation for Integer SVDF. -void EvalIntegerSvdfReference(TfLiteContext* context, TfLiteNode* node, - const TfLiteEvalTensor* input_tensor, - const TfLiteEvalTensor* weights_feature_tensor, - const TfLiteEvalTensor* weights_time_tensor, - const TfLiteEvalTensor* bias_tensor, - const TfLiteSVDFParams* params, - TfLiteEvalTensor* activation_state_tensor, - TfLiteEvalTensor* output_tensor, - const OpData& data); +void EvalInt8SvdfReference(TfLiteContext* context, TfLiteNode* node, + const TfLiteEvalTensor* input_tensor, + const TfLiteEvalTensor* weights_feature_tensor, + const TfLiteEvalTensor* weights_time_tensor, + const TfLiteEvalTensor* bias_tensor, + const TfLiteSVDFParams* params, + TfLiteEvalTensor* activation_state_tensor, + TfLiteEvalTensor* output_tensor, + const OpDataSvdf& data); + +// TODO(#523): remove 16-bit code when no longer needed. +void EvalInt16SvdfReference(TfLiteContext* context, TfLiteNode* node, + const TfLiteEvalTensor* input_tensor, + const TfLiteEvalTensor* weights_feature_tensor, + const TfLiteEvalTensor* weights_time_tensor, + const TfLiteEvalTensor* bias_tensor, + const TfLiteSVDFParams* params, + TfLiteEvalTensor* activation_state_tensor, + TfLiteEvalTensor* output_tensor, + const OpDataSvdf& data); void EvalFloatSvdfReference( TfLiteContext* context, TfLiteNode* node, const TfLiteEvalTensor* input, @@ -66,6 +77,23 @@ void EvalFloatSvdfReference( TfLiteStatus PrepareSvdf(TfLiteContext* context, TfLiteNode* node); +// This is the most generic TfLiteRegistration. The actual supported types may +// still be target dependent. The only requirement is that every implementation +// (reference or optimized) must define this function. +TfLiteRegistration Register_SVDF(); + +#if defined(HEXAGON) +TfLiteRegistration Register_SVDF_INT8(); + +#else +// Note that while this block gets used for both reference and optimized kernels +// that do not have any specialized implementations, the only goal here is to +// define fallback implementation that allow reference kernels to still be used +// from applications that call a more specific kernel variant. + +inline TfLiteRegistration Register_SVDF_INT8() { return Register_SVDF(); } + +#endif } // namespace tflite #endif // TENSORFLOW_LITE_MICRO_KERNELS_SVDF_H_ diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/svdf_common.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf_common.cc similarity index 82% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/svdf_common.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf_common.cc index 12e697b1..d1cbd26e 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/svdf_common.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf_common.cc @@ -48,6 +48,7 @@ const int kSvdfInputActivationStateTensor = 4; // This is a variable tensor, and will be modified by this op. const int kSvdfOutputTensor = 0; +template void EvalIntegerSvdfReference(TfLiteContext* context, TfLiteNode* node, const TfLiteEvalTensor* input_tensor, const TfLiteEvalTensor* weights_feature_tensor, @@ -56,7 +57,7 @@ void EvalIntegerSvdfReference(TfLiteContext* context, TfLiteNode* node, const TfLiteSVDFParams* params, TfLiteEvalTensor* activation_state_tensor, TfLiteEvalTensor* output_tensor, - const OpData& data) { + const OpDataSvdf& data) { const int n_rank = params->rank; const int n_batch = input_tensor->dims->data[0]; const int n_input = input_tensor->dims->data[1]; @@ -73,14 +74,13 @@ void EvalIntegerSvdfReference(TfLiteContext* context, TfLiteNode* node, context->GetScratchBuffer(context, data.scratch_output_tensor_index)); // Shift states. - int16_t* const state_ptr = - tflite::micro::GetTensorData(activation_state_tensor); + T* const state_ptr = tflite::micro::GetTensorData(activation_state_tensor); // Left shift the activation_state. { - int16_t* new_state_start = state_ptr; - const int16_t* old_state_start = state_ptr + 1; - const int16_t* old_state_end = state_ptr + n_batch * n_filter * n_memory; + T* new_state_start = state_ptr; + const T* old_state_start = state_ptr + 1; + const T* old_state_end = state_ptr + n_batch * n_filter * n_memory; while (old_state_start != old_state_end) { *new_state_start++ = *old_state_start++; } @@ -90,14 +90,13 @@ void EvalIntegerSvdfReference(TfLiteContext* context, TfLiteNode* node, // Feature matmul. { - int16_t* state = - tflite::micro::GetTensorData(activation_state_tensor); + T* state = tflite::micro::GetTensorData(activation_state_tensor); const int8_t* input = tflite::micro::GetTensorData(input_tensor); const int8_t* weight_feature = tflite::micro::GetTensorData(weights_feature_tensor); - const int32_t output_max = std::numeric_limits::max(); - const int32_t output_min = std::numeric_limits::min(); - int16_t* result_in_batch = state + (n_memory - 1); + const int32_t output_max = std::numeric_limits::max(); + const int32_t output_min = std::numeric_limits::min(); + T* result_in_batch = state + (n_memory - 1); for (int b = 0; b < n_batch; b++) { const int8_t* matrix_ptr = weight_feature; for (int r = 0; r < n_filter; r++) { @@ -110,13 +109,10 @@ void EvalIntegerSvdfReference(TfLiteContext* context, TfLiteNode* node, dot_prod = MultiplyByQuantizedMultiplier( dot_prod, data.effective_scale_1_a, data.effective_scale_1_b); dot_prod = std::min(std::max(output_min, dot_prod), output_max); - // This assumes state is symmetrically quantized. Otherwise last bit of - // state should be initialized to its zero point and accumulate the - // dot_prod. - // Equivalent as the following: - // result_in_batch = zero point, which happens to be zero. - // result_in_batch += dot_prod_56. - *result_in_batch = dot_prod; + // The int16 version of the op assumes a zero_point of 0. This + // code accounts for the potentially non-zero zero_point for the int8 + // version of the op. + *result_in_batch = data.activation_state_zero_point + dot_prod; result_in_batch += n_memory; } } @@ -128,16 +124,18 @@ void EvalIntegerSvdfReference(TfLiteContext* context, TfLiteNode* node, int32_t* scratch_ptr_batch = scratch_tensor + b * n_filter; // Perform batched vector dot product: - const int16_t* vector1_ptr = - tflite::micro::GetTensorData(weights_time_tensor); - const int16_t* vector2_ptr = - tflite::micro::GetTensorData(activation_state_tensor) + + const T* vector1_ptr = + tflite::micro::GetTensorData(weights_time_tensor); + const T* vector2_ptr = + tflite::micro::GetTensorData(activation_state_tensor) + b * n_memory * n_filter; for (int i = 0; i < n_filter; i++) { *scratch_ptr_batch = 0; for (int j = 0; j < n_memory; j++) { - *scratch_ptr_batch += *vector1_ptr++ * *vector2_ptr++; + *scratch_ptr_batch += + *vector1_ptr++ * + (*vector2_ptr++ - data.activation_state_zero_point); } scratch_ptr_batch++; } @@ -192,12 +190,46 @@ void EvalIntegerSvdfReference(TfLiteContext* context, TfLiteNode* node, } } } + +/** + * Generate two versions of the integer code. One with int16_t type for the + * time weights and the activation state, and another one with int8_t for the + * same. + */ + +void EvalInt16SvdfReference(TfLiteContext* context, TfLiteNode* node, + const TfLiteEvalTensor* input_tensor, + const TfLiteEvalTensor* weights_feature_tensor, + const TfLiteEvalTensor* weights_time_tensor, + const TfLiteEvalTensor* bias_tensor, + const TfLiteSVDFParams* params, + TfLiteEvalTensor* activation_state_tensor, + TfLiteEvalTensor* output_tensor, + const OpDataSvdf& data) { + EvalIntegerSvdfReference( + context, node, input_tensor, weights_feature_tensor, weights_time_tensor, + bias_tensor, params, activation_state_tensor, output_tensor, data); +} + +void EvalInt8SvdfReference(TfLiteContext* context, TfLiteNode* node, + const TfLiteEvalTensor* input_tensor, + const TfLiteEvalTensor* weights_feature_tensor, + const TfLiteEvalTensor* weights_time_tensor, + const TfLiteEvalTensor* bias_tensor, + const TfLiteSVDFParams* params, + TfLiteEvalTensor* activation_state_tensor, + TfLiteEvalTensor* output_tensor, + const OpDataSvdf& data) { + EvalIntegerSvdfReference( + context, node, input_tensor, weights_feature_tensor, weights_time_tensor, + bias_tensor, params, activation_state_tensor, output_tensor, data); +} + static inline void ApplyTimeWeightsBiasAndActivation( int batch_size, int memory_size, int num_filters, int num_units, int rank, - const float* const __restrict__ weights_time_ptr, - const float* const __restrict__ bias_ptr, TfLiteFusedActivation activation, - float* const __restrict__ state_ptr, float* const __restrict__ scratch_ptr, - float* const __restrict__ output_ptr) { + const float* const weights_time_ptr, const float* const bias_ptr, + TfLiteFusedActivation activation, float* const state_ptr, + float* const scratch_ptr, float* const output_ptr) { // Compute matmul(activation_state, weights_time). for (int b = 0; b < batch_size; ++b) { // Perform batched vector dot product: @@ -401,12 +433,14 @@ TfLiteStatus PrepareSvdf(TfLiteContext* context, TfLiteNode* node) { TF_LITE_ENSURE_EQ(context, node->inputs->size, 5); TFLITE_DCHECK(node->user_data != nullptr); - OpData* data = static_cast(node->user_data); + OpDataSvdf* data = static_cast(node->user_data); if (input->type == kTfLiteInt8) { TF_LITE_ENSURE_EQ(context, weights_feature->type, kTfLiteInt8); - TF_LITE_ENSURE_EQ(context, weights_time->type, kTfLiteInt16); - TF_LITE_ENSURE_EQ(context, activation_state->type, kTfLiteInt16); + TF_LITE_ENSURE(context, (weights_time->type == kTfLiteInt16) || + (weights_time->type == kTfLiteInt8)); + TF_LITE_ENSURE(context, (activation_state->type == kTfLiteInt16) || + (activation_state->type == kTfLiteInt8)); if (bias != nullptr) { TF_LITE_ENSURE_EQ(context, bias->type, kTfLiteInt32); } @@ -434,6 +468,7 @@ TfLiteStatus PrepareSvdf(TfLiteContext* context, TfLiteNode* node) { data->input_zero_point = input->params.zero_point; data->output_zero_point = output->params.zero_point; + data->activation_state_zero_point = activation_state->params.zero_point; TFLITE_DCHECK(context->RequestScratchBufferInArena != nullptr); diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/tanh.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/tanh.cc similarity index 83% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/tanh.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/tanh.cc index 7743a87f..a9ede9eb 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/tanh.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/tanh.cc @@ -57,7 +57,7 @@ TfLiteStatus CalculateArithmeticOpData(TfLiteContext* context, TfLiteNode* node, TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type); - if (input->type == kTfLiteUInt8 || input->type == kTfLiteInt8) { + if (input->type == kTfLiteInt8) { static constexpr int kInputIntegerBits = 4; const double input_real_multiplier = static_cast(input->params.scale) * @@ -103,25 +103,12 @@ TfLiteStatus TanhEval(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } break; case kTfLiteInt16: { - TanhParams params; - params.input_left_shift = data.input_left_shift; - reference_ops::Tanh(params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - return kTfLiteOk; - } break; - case kTfLiteUInt8: { - TanhParams params; - params.input_zero_point = data.input_zero_point; - params.input_range_radius = data.input_range_radius; - params.input_multiplier = data.input_multiplier; - params.input_left_shift = data.input_left_shift; - reference_ops::Tanh(params, tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - + reference_integer_ops::Tanh( + data.input_multiplier, data.input_left_shift, + tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); return kTfLiteOk; } break; case kTfLiteInt8: { diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/transpose.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/transpose.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/transpose.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/transpose.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/transpose_conv.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/transpose_conv.cc similarity index 89% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/transpose_conv.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/transpose_conv.cc index aacb4880..cbd964a0 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/transpose_conv.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/transpose_conv.cc @@ -1,4 +1,4 @@ -/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -69,9 +69,8 @@ inline PaddingType RuntimePaddingType(TfLitePadding padding) { } TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node, - const TfLiteConvParams* params, int width, + const TfLiteTransposeConvParams* params, int width, int height, int filter_width, int filter_height, - int out_width, int out_height, const TfLiteType data_type, OpData* data) { bool has_bias = node->inputs->size == 4; // Check number of inputs/outputs @@ -80,10 +79,13 @@ TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node, // Matching GetWindowedOutputSize in TensorFlow. auto padding = params->padding; + int unused_output_width; + int unused_output_height; TfLitePaddingValues padding_values = ComputePaddingHeightWidth( - params->stride_height, params->stride_width, - params->dilation_height_factor, params->dilation_width_factor, height, - width, filter_height, filter_width, padding, &out_height, &out_width); + params->stride_height, params->stride_width, 1, + 1, // Dilation height and width are always 1 for transpose_conv. + height, width, filter_height, filter_width, padding, + &unused_output_height, &unused_output_width); data->params.padding_type = RuntimePaddingType(padding); data->params.padding_values.width = padding_values.width; @@ -103,7 +105,7 @@ TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node, int output_channels = filter->dims->data[kConvQuantizedDimension]; TF_LITE_ENSURE_STATUS(tflite::PopulateConvolutionQuantizationParams( - context, input, filter, bias, output, params->activation, + context, input, filter, bias, output, kTfLiteActNone, &data->params.output_multiplier, &data->params.output_shift, &data->params.quantized_activation_min, &data->params.quantized_activation_max, @@ -136,7 +138,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { TFLITE_DCHECK(node->builtin_data != nullptr); OpData* data = static_cast(node->user_data); - const auto params = static_cast(node->builtin_data); + const auto params = + static_cast(node->builtin_data); TfLiteTensor* output = GetOutput(context, node, kOutputTensor); TF_LITE_ENSURE(context, output != nullptr); @@ -145,12 +148,11 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { const TfLiteTensor* filter = GetInput(context, node, kFilterTensor); TF_LITE_ENSURE(context, filter != nullptr); - int input_width = input->dims->data[2]; - int input_height = input->dims->data[1]; - int filter_width = filter->dims->data[2]; - int filter_height = filter->dims->data[1]; - int output_width = output->dims->data[2]; - int output_height = output->dims->data[1]; + // Get height and width of the output. + const int width = SizeOfDimension(output, 2); + const int height = SizeOfDimension(output, 1); + const int filter_width = SizeOfDimension(filter, 2); + const int filter_height = SizeOfDimension(filter, 1); // Dynamically allocate per-channel quantization parameters. const int num_channels = filter->dims->data[kConvQuantizedDimension]; @@ -162,7 +164,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { context, num_channels * sizeof(int32_t))); // Quantized kernels use an int32 scratch buffer. - if (input->type == kTfLiteUInt8 || input->type == kTfLiteInt8) { + if (input->type == kTfLiteInt8) { TFLITE_DCHECK(context->RequestScratchBufferInArena != nullptr); TFLITE_DCHECK(context->RequestScratchBufferInArena( context, @@ -198,28 +200,20 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { affine_quantization->zero_point->size); } - TF_LITE_ENSURE_STATUS(CalculateOpData( - context, node, params, input_width, input_height, filter_width, - filter_height, output_width, output_height, input->type, data)); + TF_LITE_ENSURE_STATUS(CalculateOpData(context, node, params, width, height, + filter_width, filter_height, + input->type, data)); // Offsets (zero points) data->params.input_offset = -input->params.zero_point; data->params.weights_offset = -filter->params.zero_point; data->params.output_offset = output->params.zero_point; - // Stride + dilation + // Stride data->params.stride_width = params->stride_width; data->params.stride_height = params->stride_height; - data->params.dilation_width_factor = params->dilation_width_factor; - data->params.dilation_height_factor = params->dilation_height_factor; - - float output_activation_min, output_activation_max; - CalculateActivationRange(params->activation, &output_activation_min, - &output_activation_max); - data->params.float_activation_min = output_activation_min; - data->params.float_activation_max = output_activation_max; return kTfLiteOk; -} // namespace conv +} TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { const TfLiteEvalTensor* input = diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/unpack.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/unpack.cc similarity index 97% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/unpack.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/unpack.cc index 557cc57a..13bb7dcf 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/unpack.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/unpack.cc @@ -87,9 +87,6 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { case kTfLiteInt32: { return UnpackImpl(context, node, input, data->num, data->axis); } - case kTfLiteUInt8: { - return UnpackImpl(context, node, input, data->num, data->axis); - } case kTfLiteInt8: { return UnpackImpl(context, node, input, data->num, data->axis); } diff --git a/code/components/tflite-lib/tensorflow/lite/micro/kernels/var_handle.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/var_handle.cc new file mode 100644 index 00000000..2efffdb6 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/kernels/var_handle.cc @@ -0,0 +1,104 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include + +#include + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/compatibility.h" +#include "tensorflow/lite/kernels/kernel_util.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" +#include "tensorflow/lite/micro/memory_helpers.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" +#include "tensorflow/lite/micro/micro_graph.h" +#include "tensorflow/lite/micro/micro_resource_variable.h" +#include "tensorflow/lite/schema/schema_generated.h" + +namespace tflite { + +namespace { + +struct OpData { + int32_t resource_id; +}; + +void* Init(TfLiteContext* context, const char* buffer, size_t length) { + TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); + return context->AllocatePersistentBuffer(context, sizeof(OpData)); +} + +TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { + OpData* op_data = reinterpret_cast(node->user_data); + const auto* params = + reinterpret_cast(node->builtin_data); + + // Casting to TfliteIntArray is required since we are re-using + // GetExecutionPlan from TfLiteContext. On TFLM this method returns a + // MicroGraph. + // TODO(b/188226309): Design a cleaner way to get a graph from kernel context. + MicroGraph* graph_info; + context->GetExecutionPlan(context, + reinterpret_cast(&graph_info)); + MicroResourceVariables* resources = graph_info->GetResourceVariables(); + if (resources == nullptr) { + MicroPrintf( + "VAR_HANDLE requires resource variables. Please create " + "ResourceVariables and pass it to the interpreter."); + return kTfLiteError; + } + op_data->resource_id = + resources->CreateIdIfNoneFound(params->container, params->shared_name); + if (op_data->resource_id < 0) { + return kTfLiteError; + } + + TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0); + TFLITE_DCHECK(output != nullptr); + + // Assign saved resource_id so this output tensor will always return the + // correct resource id. + output->data.i32 = &op_data->resource_id; + + return kTfLiteOk; +} + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + OpData* op_data = reinterpret_cast(node->user_data); + + TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0); + TFLITE_DCHECK(output != nullptr); + + // Assign saved resource_id so this output tensor will always return the + // correct resource id. + output->data.i32 = &op_data->resource_id; + return kTfLiteOk; +} + +} // namespace. + +TfLiteRegistration Register_VAR_HANDLE() { + return {/*init=*/Init, + /*free=*/nullptr, + /*prepare=*/Prepare, + /*invoke=*/Eval, + /*profiling_string=*/nullptr, + /*builtin_code=*/0, + /*custom_name=*/nullptr, + /*version=*/0}; +} + +} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/zeros_like.cc b/code/components/tflite-lib/tensorflow/lite/micro/kernels/zeros_like.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/kernels/zeros_like.cc rename to code/components/tflite-lib/tensorflow/lite/micro/kernels/zeros_like.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/memory_helpers.cc b/code/components/tflite-lib/tensorflow/lite/micro/memory_helpers.cc similarity index 98% rename from code/components/tfmicro/tensorflow/lite/micro/memory_helpers.cc rename to code/components/tflite-lib/tensorflow/lite/micro/memory_helpers.cc index 2d8f7597..930202de 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/memory_helpers.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/memory_helpers.cc @@ -81,6 +81,9 @@ TfLiteStatus TfLiteTypeSizeOf(TfLiteType type, size_t* size) { case kTfLiteBool: *size = sizeof(bool); break; + case kTfLiteResource: + *size = sizeof(int32_t); + break; case kTfLiteComplex64: *size = sizeof(float) * 2; break; diff --git a/code/components/tfmicro/tensorflow/lite/micro/memory_helpers.h b/code/components/tflite-lib/tensorflow/lite/micro/memory_helpers.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/memory_helpers.h rename to code/components/tflite-lib/tensorflow/lite/micro/memory_helpers.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc similarity index 98% rename from code/components/tfmicro/tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc rename to code/components/tflite-lib/tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc index ade0a3c4..bdfc8304 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc @@ -58,9 +58,14 @@ void ReverseSortInPlace(int* values, int* ids, int size) { } while (any_swapped); } -GreedyMemoryPlanner::GreedyMemoryPlanner(unsigned char* scratch_buffer, - int scratch_buffer_size) - : buffer_count_(0), need_to_calculate_offsets_(true) { +GreedyMemoryPlanner::GreedyMemoryPlanner() {} + +TfLiteStatus GreedyMemoryPlanner::Init(unsigned char* scratch_buffer, + int scratch_buffer_size) { + // Reset internal states + buffer_count_ = 0; + need_to_calculate_offsets_ = true; + // Allocate the arrays we need within the scratch buffer arena. max_buffer_count_ = scratch_buffer_size / per_buffer_size(); @@ -78,6 +83,7 @@ GreedyMemoryPlanner::GreedyMemoryPlanner(unsigned char* scratch_buffer, next_free += sizeof(ListEntry) * max_buffer_count_; buffer_offsets_ = reinterpret_cast(next_free); + return kTfLiteOk; } GreedyMemoryPlanner::~GreedyMemoryPlanner() { diff --git a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/greedy_memory_planner.h b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/greedy_memory_planner.h similarity index 85% rename from code/components/tfmicro/tensorflow/lite/micro/memory_planner/greedy_memory_planner.h rename to code/components/tflite-lib/tensorflow/lite/micro/memory_planner/greedy_memory_planner.h index 0064714d..a34f3c59 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/greedy_memory_planner.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/greedy_memory_planner.h @@ -17,7 +17,7 @@ limitations under the License. #define TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_GREEDY_MEMORY_PLANNER_H_ #include "tensorflow/lite/micro/compatibility.h" -#include "tensorflow/lite/micro/memory_planner/memory_planner.h" +#include "tensorflow/lite/micro/memory_planner/micro_memory_planner.h" namespace tflite { @@ -43,20 +43,24 @@ constexpr int kOnlinePlannedBuffer = -1; // // This is not guaranteed to produce the best placement, since that's an // NP-Complete problem, but in practice it should produce one that's decent. -class GreedyMemoryPlanner : public MemoryPlanner { +class GreedyMemoryPlanner : public MicroMemoryPlanner { public: - // You need to pass in an area of memory to be used for planning. This memory - // needs to have a lifetime as long as the planner, but isn't owned by this - // object, so management should be handled by the client. This is so it can be - // stack or globally allocated if necessary on devices without dynamic memory - // allocation. How many buffers can be planned for will depend on the size of - // this scratch memory, so you should enlarge it if you see an error when - // calling AddBuffer(). The memory can be reused once you're done with the - // planner, as long as you copy the calculated offsets to another location. - // Each buffer requires about 36 bytes of scratch. - GreedyMemoryPlanner(unsigned char* scratch_buffer, int scratch_buffer_size); + GreedyMemoryPlanner(); ~GreedyMemoryPlanner() override; + // You need to pass in an area of memory to be used for planning. The client + // should ensure the validity of the memory when it needs to use this object. + // This memory isn't owned by this object, so management should be handled by + // the client. This is so it can be stack or globally allocated if necessary + // on devices without dynamic memory allocation. How many buffers can be + // planned for will depend on the size of this scratch memory, so you should + // enlarge it if you see an error when calling AddBuffer(). The memory can be + // reused once you're done with the planner, as long as you copy the + // calculated offsets to another location. Each buffer requires about 36 bytes + // of scratch. + TfLiteStatus Init(unsigned char* scratch_buffer, + int scratch_buffer_size) override; + // Record details of a buffer we want to place. TfLiteStatus AddBuffer(ErrorReporter* error_reporter, int size, int first_time_used, int last_time_used) override; @@ -65,7 +69,7 @@ class GreedyMemoryPlanner : public MemoryPlanner { // offline_offset is the buffer offset from the start of the arena. TfLiteStatus AddBuffer(ErrorReporter* error_reporter, int size, int first_time_used, int last_time_used, - int offline_offset); + int offline_offset) override; // Returns the high-water mark of used memory. This is the minimum size of a // memory arena you'd need to allocate to hold these buffers. @@ -81,7 +85,7 @@ class GreedyMemoryPlanner : public MemoryPlanner { int buffer_index, int* offset) override; // Prints an ascii-art diagram of the buffer layout plan. - void PrintMemoryPlan(); + void PrintMemoryPlan() override; // Debug method to check whether any buffer allocations are overlapping. This // is an O(N^2) complexity operation, so only use for testing. diff --git a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/linear_memory_planner.cc b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/linear_memory_planner.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/memory_planner/linear_memory_planner.cc rename to code/components/tflite-lib/tensorflow/lite/micro/memory_planner/linear_memory_planner.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/linear_memory_planner.h b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/linear_memory_planner.h similarity index 93% rename from code/components/tfmicro/tensorflow/lite/micro/memory_planner/linear_memory_planner.h rename to code/components/tflite-lib/tensorflow/lite/micro/memory_planner/linear_memory_planner.h index 4d77e778..128ef808 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/linear_memory_planner.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/linear_memory_planner.h @@ -17,13 +17,13 @@ limitations under the License. #define TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_LINEAR_MEMORY_PLANNER_H_ #include "tensorflow/lite/micro/compatibility.h" -#include "tensorflow/lite/micro/memory_planner/memory_planner.h" +#include "tensorflow/lite/micro/memory_planner/micro_memory_planner.h" namespace tflite { // The simplest possible memory planner that just lays out all buffers at // increasing offsets without trying to reuse memory. -class LinearMemoryPlanner : public MemoryPlanner { +class LinearMemoryPlanner : public MicroMemoryPlanner { public: LinearMemoryPlanner(); ~LinearMemoryPlanner() override; diff --git a/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/memory_plan_struct.h b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/memory_plan_struct.h new file mode 100644 index 00000000..c8c431cc --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/memory_plan_struct.h @@ -0,0 +1,73 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_MEMORY_PLAN_STRUCT_H_ +#define TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_MEMORY_PLAN_STRUCT_H_ + +#include +#include + +#include "tensorflow/lite/micro/micro_utils.h" + +namespace tflite { + +// This is an experimental feature and subjected to change. +// More description is available at +// tensorflow/lite/micro/docs/offline_memory_plan.md. + +// Describes a buffer's layout inside an arena. This struct should be kept as +// small as possible for memory footprint sensitive applications and should use +// only primitive fields, making it easy to adjust offline. +struct BufferDescriptor { + // Starting offset inside an arena for this buffer. + // Offset is the minimum information needed for the buffer. The user knows + // the model and the size of each buffer in order to lay out a valid buffer + // plan. + int32_t offset; +}; + +// A structure describing the lay out of buffers inside an arena. +struct BufferPlan { + // Number of buffers described in this plan. + int32_t buffer_count; + + // Each element describes one buffer. + // Buffer index is implicit by the order of AddBuffer() call. + // Specifically, indices of activation tensors are 0 … N-1 where N is the + // number of activation tensors. + // The rest are based on the order of OP requests. + // + // This is a flexible array member and should ideally be + // arena_entries[]; However, in order to support a variety + // of compilers (and without needing to add ifdef's), we + // are implementing the flexible array member with an array of + // length 1 as the last member of the struct. When the size of a BufferPlan + // is needed, use the provided SizeOfBufferPlan(buffer_count) that + // accounts for this implemenatation caveat. + BufferDescriptor buffer_plan_entries[1]; +}; + +// Returns size of a BufferPlan given a buffer count. This size is compile time +// known if buffer_count is a compile time constant. +constexpr size_t SizeOfBufferPlan(int32_t buffer_count) { + // Minus 1 because a BufferPlan struct have a BufferDescriptor already. + // Max to provide a lower bound for the corner case of buffer_count = 0. + return sizeof(BufferPlan) + + sizeof(BufferDescriptor) * Max(buffer_count - 1, 0); +} + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_MEMORY_PLAN_STRUCT_H_ diff --git a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/memory_planner.h b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/micro_memory_planner.h similarity index 68% rename from code/components/tfmicro/tensorflow/lite/micro/memory_planner/memory_planner.h rename to code/components/tflite-lib/tensorflow/lite/micro/memory_planner/micro_memory_planner.h index 2c39fbe0..cec56335 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/memory_planner/memory_planner.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/micro_memory_planner.h @@ -13,8 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#ifndef TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_MEMORY_PLANNER_H_ -#define TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_MEMORY_PLANNER_H_ +#ifndef TENSORFLOW_LITE_MICRO_MICRO_MEMORY_PLANNER_MEMORY_PLANNER_H_ +#define TENSORFLOW_LITE_MICRO_MICRO_MEMORY_PLANNER_MEMORY_PLANNER_H_ #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/core/api/error_reporter.h" @@ -43,10 +43,10 @@ namespace tflite { // The goal is for applications to be able to experiment with different layout // strategies without changing their client code, by swapping out classes that // implement this interface.= -class MemoryPlanner { +class MicroMemoryPlanner { public: - MemoryPlanner() {} - virtual ~MemoryPlanner() {} + MicroMemoryPlanner() {} + virtual ~MicroMemoryPlanner() {} // Pass information about a buffer's size and lifetime to the layout // algorithm. The order this is called implicitly assigns an index to the @@ -57,6 +57,16 @@ class MemoryPlanner { int size, int first_time_used, int last_time_used) = 0; + // Record details of an offline planned buffer offset we want to place. + // offline_offset is the buffer offset from the start of the arena. + // This is to support offline memory planning from the flatbuffer metadata. + // By default, it returns an error. + virtual TfLiteStatus AddBuffer(ErrorReporter* error_reporter, int size, + int first_time_used, int last_time_used, + int offline_offset) { + return kTfLiteError; + } + // The largest contiguous block of memory that's needed to hold the layout. virtual size_t GetMaximumMemorySize() = 0; // How many buffers have been added to the planner. @@ -64,8 +74,22 @@ class MemoryPlanner { // Calculated layout offset for the N-th buffer added to the planner. virtual TfLiteStatus GetOffsetForBuffer(tflite::ErrorReporter* error_reporter, int buffer_index, int* offset) = 0; + + // Provides the scratch buffer in case that the memory planner needs it. + // The lifetime of scratch buffers lifetime lasts until the static memory plan + // is committed. + // The default implementation is for the memory planner that does not need + // scratch buffer and simply returns ok. + virtual TfLiteStatus Init(unsigned char* scratch_buffer, + int scratch_buffer_size) { + return kTfLiteOk; + } + + virtual void PrintMemoryPlan() { + // Default does nothing. + } }; } // namespace tflite -#endif // TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_MEMORY_PLANNER_H_ +#endif // TENSORFLOW_LITE_MICRO_MICRO_MEMORY_PLANNER_MEMORY_PLANNER_H_ diff --git a/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/non_persistent_buffer_planner_shim.cc b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/non_persistent_buffer_planner_shim.cc new file mode 100644 index 00000000..700627a5 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/non_persistent_buffer_planner_shim.cc @@ -0,0 +1,66 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/micro/memory_planner/non_persistent_buffer_planner_shim.h" + +#include "tensorflow/lite/micro/micro_error_reporter.h" + +namespace tflite { + +NonPersistentMemoryPlannerShim::NonPersistentMemoryPlannerShim( + const BufferPlan* buffer_plan) + : buffer_plan_(buffer_plan), buffer_request_count_(0) {} + +NonPersistentMemoryPlannerShim::~NonPersistentMemoryPlannerShim() {} + +TfLiteStatus NonPersistentMemoryPlannerShim::AddBuffer( + tflite::ErrorReporter* error_reporter, int size, int first_time_used, + int last_time_used) { + buffer_request_count_++; + if (buffer_request_count_ > buffer_plan_->buffer_count) { + MicroPrintf( + "Attempting to add buffer %d, but only %d buffers in given buffer " + "plan.", + buffer_request_count_, buffer_plan_->buffer_count); + return kTfLiteError; + } + return kTfLiteOk; +} + +size_t NonPersistentMemoryPlannerShim::GetMaximumMemorySize() { + // Simply return 0 to let the framework accept this memory plan + // because the client ensure validity of the memory plan. + return 0; +} + +// How many buffers are in the given memory plan. +int NonPersistentMemoryPlannerShim::GetBufferCount() { + return buffer_plan_->buffer_count; +} + +TfLiteStatus NonPersistentMemoryPlannerShim::GetOffsetForBuffer( + ErrorReporter* error_reporter, int buffer_request_index, int* offset) { + if (buffer_request_index >= buffer_plan_->buffer_count) { + MicroPrintf( + "Attempting to get offset for buffer %d, but only %d buffers in given " + "buffer plan.", + buffer_request_index, buffer_plan_->buffer_count); + return kTfLiteError; + } + *offset = buffer_plan_->buffer_plan_entries[buffer_request_index].offset; + return kTfLiteOk; +} + +} // namespace tflite diff --git a/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/non_persistent_buffer_planner_shim.h b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/non_persistent_buffer_planner_shim.h new file mode 100644 index 00000000..945ac123 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/memory_planner/non_persistent_buffer_planner_shim.h @@ -0,0 +1,130 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_NON_PERSISTENT_MEMORY_PLANNER_SHIM_H__ +#define TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_NON_PERSISTENT_MEMORY_PLANNER_SHIM_H__ + +#include "tensorflow/lite/micro/compatibility.h" +#include "tensorflow/lite/micro/memory_planner/memory_plan_struct.h" +#include "tensorflow/lite/micro/memory_planner/micro_memory_planner.h" + +namespace tflite { + +/* This is an experimental feature and subjected to change. + * +The NonPersistentMemoryPlannerShim enables TFLM to work with an external tooling +that can plan the offset of each non persistent buffer for the Model within the +TFLM arena. + +If the NonPersistentMemoryPlannerShim is used, then the final binary does not +have any of the symbols associated with the GreedyMemoryPlanner which results in +a reduced memory footprint. + +Additionally, the offline planning of the non-persistent buffers can be used to +have a more efficient utilization compared to the GreedyMemoryPlanner. + +For example, consider the following hypothetical model: + +A1(400) A2(401) +──┬─────────┐ ┌─────────── + │ │ │ + │ │ │ + │ ▼ ▼ + │ ┌────────┐ + │ │ OP1 │ + │ └───┬────┘ A4(201) + │ A3(10) │ │ + │ │ │ + │ │ │ + │ ┌───┴────┐ │ + │ │ OP2 │◄────────┤ + │ └───┬────┘ │ + │ A5(11) │ A6(202) │ + │ │ │ │ + │ ▼ │ │ + │ ┌────────┐ │ │ + │ │ OP3 │◄─┘ │ + │ └───┬────┘ │ + │ │ A8(200) │ + │ A7(12) │ │ │ + │ │ │ │ + │ ┌───┴────┐◄──┘ │ + └──────►│ OP4 │ │ + └───┬────┘◄────────┘ + │ + A9(13) │ + ▼ + +The GreedyMemoryPlanner will give the following memory layout that requires 1012 +bytes of scratch arena size: + +┌─────────────────────────────────────────┬──────────────────────────┬────────┬───────┐ +│ A2(401) │ A1(400) │ A4(201)│ +A3(10)│ +└─────────────────────────────────────────┴──────────────────────────┴────────┴───────┘ + +┌───────────┬──────┬──────┐ +│ A6(202) │A5(11)│A7(12)│ +└───────────┴──────┴──────┘ + +┌──────────┬───────┐ +│ A8(200) │A9(13) │ +└──────────┴───────┘ + +But a more efficient offline memory plan that requires only 826 bytes of scratch +arena size can be + +┌──────────────────────────────────────┬─────────────────────────────┬───────┬──────┐ +│ A1(400) │ A2(401) │ +A3(10)│A5(11)│ +└──────────────────────────────────────┴─────────────────────────────┴───────┴──────┘ + + ┌────────────────┬────────────┬────────┬───────┐ + │A4(201) │ A8(200) │A9(13) +│A7(12) │ └────────────────┴────────────┴────────┴───────┘ + + ┌─────────────┐ + │ A6(202) │ + └─────────────┘ + +*/ +class NonPersistentMemoryPlannerShim : public MicroMemoryPlanner { + public: + // Does not take ownership of buffer_plan, which must refer to a valid + // BufferPlan that outlives this object. + explicit NonPersistentMemoryPlannerShim(const BufferPlan* buffer_plan); + ~NonPersistentMemoryPlannerShim() override; + + TfLiteStatus GetOffsetForBuffer(ErrorReporter* error_reporter, + int buffer_request_index, + int* offset) override; + + TfLiteStatus AddBuffer(tflite::ErrorReporter* error_reporter, int size, + int first_time_used, int last_time_used) override; + size_t GetMaximumMemorySize() override; + int GetBufferCount() override; + + private: + const BufferPlan* buffer_plan_; // not owned, can't be null + + // The number of buffers requested so far. Used for error checking. + int buffer_request_count_; + + TF_LITE_REMOVE_VIRTUAL_DELETE +}; + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_MEMORY_PLANNER_NON_PERSISTENT_MEMORY_PLANNER_SHIM_H__ diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_allocator.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_allocator.cc similarity index 82% rename from code/components/tfmicro/tensorflow/lite/micro/micro_allocator.cc rename to code/components/tflite-lib/tensorflow/lite/micro/micro_allocator.cc index c90ac3ea..ec203b9f 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_allocator.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_allocator.cc @@ -26,9 +26,11 @@ limitations under the License. #include "tensorflow/lite/core/api/tensor_utils.h" #include "tensorflow/lite/kernels/internal/compatibility.h" #include "tensorflow/lite/micro/compatibility.h" +#include "tensorflow/lite/micro/flatbuffer_utils.h" #include "tensorflow/lite/micro/memory_helpers.h" #include "tensorflow/lite/micro/memory_planner/greedy_memory_planner.h" -#include "tensorflow/lite/micro/memory_planner/memory_planner.h" +#include "tensorflow/lite/micro/memory_planner/micro_memory_planner.h" +#include "tensorflow/lite/micro/micro_arena_constants.h" #include "tensorflow/lite/micro/micro_error_reporter.h" #include "tensorflow/lite/micro/simple_memory_allocator.h" #include "tensorflow/lite/schema/schema_generated.h" @@ -56,9 +58,6 @@ struct AllocationInfo { bool needs_allocating; }; -// We align tensor buffers to 16-byte boundaries, since this is a common -// requirement for SIMD extensions. -constexpr int kBufferAlignment = 16; constexpr char kOfflineMemAllocMetadata[] = "OfflineMemoryAllocation"; const TfLiteIntArray kZeroLengthIntArray = {}; @@ -75,79 +74,12 @@ class MicroBuiltinDataAllocator : public BuiltinDataAllocator { // of the model. } + TF_LITE_REMOVE_VIRTUAL_DELETE + private: SimpleMemoryAllocator* memory_allocator_; - - TF_LITE_REMOVE_VIRTUAL_DELETE }; -#if !defined(__clang__) -// Helper function to check flatbuffer metadata correctness. This function is -// not called by default. Hence it's not linked in to the final binary code. -TfLiteStatus CheckOfflinePlannedOffsets(const Model* model, - ErrorReporter* error_reporter) { - // Suppress compile warning for unused function - (void)CheckOfflinePlannedOffsets; - - if (model->metadata()) { - for (size_t i = 0; i < model->metadata()->size(); ++i) { - auto metadata = model->metadata()->Get(i); - if (strncmp(metadata->name()->c_str(), kOfflineMemAllocMetadata, - strlen(kOfflineMemAllocMetadata)) == 0) { - auto* subgraphs = model->subgraphs(); - const SubGraph* subgraph = (*subgraphs)[0]; - const flatbuffers::Vector>* tensors = - subgraph->tensors(); - const flatbuffers::Vector>* buffers = - model->buffers(); - int nbr_tflite_tensors = tensors->size(); - auto* buffer = (*buffers)[metadata->buffer()]; - auto* array = buffer->data(); - const uint32_t* metadata_buffer = (uint32_t*)array->data(); - int version = metadata_buffer[0]; - int subgraph_idx = metadata_buffer[1]; - const int nbr_offline_offsets = metadata_buffer[2]; -#ifndef TF_LITE_STRIP_ERROR_STRINGS - int* offline_planner_offsets = (int*)&metadata_buffer[3]; -#endif - - TF_LITE_REPORT_ERROR(error_reporter, "==== Model metadata info: ====="); - TF_LITE_REPORT_ERROR(error_reporter, - "Offline planner metadata found, version %d, " - "subgraph %d, nbr offline offsets %d", - version, subgraph_idx, nbr_offline_offsets); - for (int j = 0; j < nbr_offline_offsets; ++j) { - TF_LITE_REPORT_ERROR( - error_reporter, - "Offline planner tensor index %d, offline offset: %d", j, - offline_planner_offsets[j]); - } - - if (version != 1) { - TF_LITE_REPORT_ERROR(error_reporter, "Version not supported! (%d)\n", - version); - return kTfLiteError; - } - if (subgraph_idx != 0) { - TF_LITE_REPORT_ERROR(error_reporter, - "Only 1 subgraph supported! Subgraph idx (%d)\n", - subgraph_idx); - return kTfLiteError; - } - if (nbr_tflite_tensors != nbr_offline_offsets) { - TF_LITE_REPORT_ERROR(error_reporter, - "Nbr of offline buffer offsets (%d) in metadata " - "not equal nbr tensors (%d)\n", - nbr_offline_offsets, nbr_tflite_tensors); - return kTfLiteError; - } - } - } - } - return kTfLiteOk; -} -#endif - // A helper class to construct AllocationInfo array. This array contains the // lifetime of tensors / scratch_buffer and will be used to calculate the memory // plan. Methods need to be called in order from `Init`, `Add*`, to `Finish`. @@ -213,14 +145,16 @@ TfLiteStatus AllocationInfoBuilder::AddTensors(const SubGraph* subgraph, uint32_t operators_size = NumSubgraphOperators(subgraph); - for (size_t i = 0; i < subgraph->inputs()->size(); ++i) { + for (size_t i = 0; + subgraph->inputs() != nullptr && i < subgraph->inputs()->size(); ++i) { const int tensor_index = subgraph->inputs()->Get(i); AllocationInfo* current = &info_[tensor_index]; current->first_created = 0; } // Mark all outputs as persistent to the end of the invocation. - for (size_t i = 0; i < subgraph->outputs()->size(); ++i) { + for (size_t i = 0; + subgraph->outputs() != nullptr && i < subgraph->outputs()->size(); ++i) { const int tensor_index = subgraph->outputs()->Get(i); AllocationInfo* current = &info_[tensor_index]; current->last_used = operators_size - 1; @@ -229,19 +163,25 @@ TfLiteStatus AllocationInfoBuilder::AddTensors(const SubGraph* subgraph, // Figure out when the first and last use of each tensor is. for (int i = (operators_size - 1); i >= 0; --i) { const auto* op = subgraph->operators()->Get(i); - for (size_t n = 0; n < op->inputs()->size(); ++n) { + for (size_t n = 0; op->inputs() != nullptr && n < op->inputs()->size(); + ++n) { const int tensor_index = op->inputs()->Get(n); AllocationInfo* current = &info_[tensor_index]; if (((current->last_used == -1) || (current->last_used < i))) { current->last_used = i; } } - for (size_t n = 0; n < op->outputs()->size(); ++n) { + for (size_t n = 0; op->outputs() != nullptr && n < op->outputs()->size(); + ++n) { const int tensor_index = op->outputs()->Get(n); AllocationInfo* current = &info_[tensor_index]; if ((current->first_created == -1) || (current->first_created > i)) { current->first_created = i; } + // Since operator outputs are written to, they must be marked as used. + if ((current->last_used == -1) || (current->last_used < i)) { + current->last_used = i; + } } } return kTfLiteOk; @@ -301,7 +241,7 @@ TfLiteStatus AllocationInfoBuilder::AddScratchBuffers( } TfLiteStatus CreatePlan(ErrorReporter* error_reporter, - GreedyMemoryPlanner* planner, + MicroMemoryPlanner* planner, const AllocationInfo* allocation_info, size_t allocation_info_size) { // Add the tensors to our allocation plan. @@ -309,7 +249,7 @@ TfLiteStatus CreatePlan(ErrorReporter* error_reporter, const AllocationInfo* current = &allocation_info[i]; if (current->needs_allocating) { size_t aligned_bytes_required = - AlignSizeUp(current->bytes, kBufferAlignment); + AlignSizeUp(current->bytes, MicroArenaBufferAlignment()); if (current->offline_offset == kOnlinePlannedBuffer) { TF_LITE_ENSURE_STATUS( planner->AddBuffer(error_reporter, aligned_bytes_required, @@ -324,8 +264,8 @@ TfLiteStatus CreatePlan(ErrorReporter* error_reporter, return kTfLiteOk; } -TfLiteStatus CommitPlan(ErrorReporter* error_reporter, MemoryPlanner* planner, - uint8_t* starting_point, +TfLiteStatus CommitPlan(ErrorReporter* error_reporter, + MicroMemoryPlanner* planner, uint8_t* starting_point, const AllocationInfo* allocation_info, size_t allocation_info_size) { // Figure out the actual memory addresses for each buffer, based on the plan. @@ -346,50 +286,6 @@ TfLiteStatus CommitPlan(ErrorReporter* error_reporter, MemoryPlanner* planner, namespace internal { -// Handles architecture safe mapping of flatbuffer vectors to a TfLite*Array -// struct. Matching types are required (e.g. float and TfLiteFloatArray). -// Big-endian systems will always allocate dimension array data in the tail -// (persistent) section. -template -TfLiteStatus FlatBufferVectorToTfLiteTypeArray( - SimpleMemoryAllocator* allocator, ErrorReporter* error_reporter, - const flatbuffers::Vector* flatbuffer_array, - kTfLiteArrayType** result) { - TFLITE_DCHECK(error_reporter != nullptr); - TFLITE_DCHECK(flatbuffer_array != nullptr); - // TODO(b/159668691): Consider adding type assertion or breaking this function - // into multiple functions for each type. std::is_same is c++11 and has a - // special updated constructor in c++17 that requires a string argument. - if (FLATBUFFERS_LITTLEENDIAN) { - // On little-endian machines, TfLite*Array happens to have the same memory - // layout as flatbuffers:Vector, so we can - // reinterpret_cast the flatbuffer vector and avoid a copy and malloc. - *result = const_cast( - reinterpret_cast(flatbuffer_array)); - } else { - // Big-endian architecture can not use the same memory layout as - // flatbuffers::Vector. Allocate from the tail and - // copy values from the flatbuffer into the newly allocated chunk. - kTfLiteArrayType* array = reinterpret_cast( - allocator->SimpleMemoryAllocator::AllocateFromTail( - TfLiteIntArrayGetSizeInBytes(flatbuffer_array->size()), - alignof(kTfLiteArrayType))); - if (array == nullptr) { - TF_LITE_REPORT_ERROR( - error_reporter, - "Failed to allocate %d bytes of memory to copy an array.", - TfLiteIntArrayGetSizeInBytes(flatbuffer_array->size())); - return kTfLiteError; - } - array->size = flatbuffer_array->size(); - for (int i = 0; i < array->size; ++i) { - array->data[i] = flatbuffer_array->Get(i); - } - *result = array; - } - return kTfLiteOk; -} - // Returns a pointer to any buffer associated with the flatbuffer tensor. Can // return nullptr if no buffer is found. void* GetFlatbufferTensorBuffer( @@ -459,14 +355,14 @@ TfLiteStatus InitializeTfLiteTensorFromFlatbuffer( if (flatbuffer_tensor.shape() == nullptr) { // flatbuffer_tensor.shape() can return a nullptr in the case of a scalar // tensor. + // TODO(b/188459715): figure out why const_cast is required here. result->dims = const_cast(&kZeroLengthIntArray); } else { // TFLM doesn't allow reshaping the tensor which requires dynamic memory // allocation so it is safe to drop the const qualifier. In the future, if // we really want to update the tensor shape, we can always pass in a new // TfLiteIntArray - especially we have to do so if the dimension is - TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray( - allocator, error_reporter, flatbuffer_tensor.shape(), &(result->dims))); + result->dims = FlatBufferVectorToTfLiteTypeArray(flatbuffer_tensor.shape()); } // Copy the quantization information from the serialized data. @@ -518,14 +414,18 @@ TfLiteStatus InitializeTfLiteTensorFromFlatbuffer( return kTfLiteError; } - TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray( - allocator, error_reporter, src_quantization->scale(), - &quantization->scale)); + quantization->scale = + FlatBufferVectorToTfLiteTypeArray(src_quantization->scale()); quantization->zero_point->size = channels; int* zero_point_data = quantization->zero_point->data; for (int i = 0; i < channels; i++) { - zero_point_data[i] = src_quantization->zero_point()->Get(i); + // As a space-saving optimization, zero point arrays for weights can be + // reduced to a single value, since all zero points for weights are 0. + zero_point_data[i] = src_quantization->zero_point()->size() == + src_quantization->scale()->size() + ? src_quantization->zero_point()->Get(i) + : src_quantization->zero_point()->Get(0); } // TODO(rocky): Need to add a micro_allocator test case that fails when // this is not copied: @@ -553,40 +453,81 @@ TfLiteStatus InitializeTfLiteEvalTensorFromFlatbuffer( // tensor. result->dims = const_cast(&kZeroLengthIntArray); } else { - TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray( - allocator, error_reporter, flatbuffer_tensor.shape(), &(result->dims))); + result->dims = FlatBufferVectorToTfLiteTypeArray(flatbuffer_tensor.shape()); } return kTfLiteOk; } } // namespace internal +size_t MicroAllocator::GetDefaultTailUsage(bool is_memory_planner_given) { + // TODO(b/208703041): a template version of AlignSizeUp to make expression + // shorter. + size_t total_size = + AlignSizeUp(sizeof(SimpleMemoryAllocator), + alignof(SimpleMemoryAllocator)) + + AlignSizeUp(sizeof(MicroAllocator), alignof(MicroAllocator)) + + AlignSizeUp(sizeof(MicroBuiltinDataAllocator), + alignof(MicroBuiltinDataAllocator)) + + AlignSizeUp(sizeof(SubgraphAllocations), alignof(SubgraphAllocations)); + if (!is_memory_planner_given) { + total_size += + AlignSizeUp(sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner)); + } + return total_size; +} + MicroAllocator::MicroAllocator(SimpleMemoryAllocator* memory_allocator, + MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter) : memory_allocator_(memory_allocator), + memory_planner_(memory_planner), error_reporter_(error_reporter), model_is_allocating_(false) {} MicroAllocator::~MicroAllocator() {} MicroAllocator* MicroAllocator::Create(uint8_t* tensor_arena, size_t arena_size, + MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter) { - uint8_t* aligned_arena = AlignPointerUp(tensor_arena, kBufferAlignment); + uint8_t* aligned_arena = + AlignPointerUp(tensor_arena, MicroArenaBufferAlignment()); size_t aligned_arena_size = tensor_arena + arena_size - aligned_arena; - return Create(SimpleMemoryAllocator::Create(error_reporter, aligned_arena, - aligned_arena_size), - error_reporter); + SimpleMemoryAllocator* memory_allocator = SimpleMemoryAllocator::Create( + error_reporter, aligned_arena, aligned_arena_size); + + return Create(memory_allocator, memory_planner, error_reporter); +} + +MicroAllocator* MicroAllocator::Create(uint8_t* tensor_arena, size_t arena_size, + ErrorReporter* error_reporter) { + uint8_t* aligned_arena = + AlignPointerUp(tensor_arena, MicroArenaBufferAlignment()); + size_t aligned_arena_size = tensor_arena + arena_size - aligned_arena; + SimpleMemoryAllocator* memory_allocator = SimpleMemoryAllocator::Create( + error_reporter, aligned_arena, aligned_arena_size); + + // By default create GreedyMemoryPlanner. + // If a different MemoryPlanner is needed, use the other api. + uint8_t* memory_planner_buffer = memory_allocator->AllocateFromTail( + sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner)); + GreedyMemoryPlanner* memory_planner = + new (memory_planner_buffer) GreedyMemoryPlanner(); + + return Create(memory_allocator, memory_planner, error_reporter); } MicroAllocator* MicroAllocator::Create(SimpleMemoryAllocator* memory_allocator, + MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter) { TFLITE_DCHECK(memory_allocator != nullptr); TFLITE_DCHECK(error_reporter != nullptr); + TFLITE_DCHECK(memory_planner != nullptr); uint8_t* allocator_buffer = memory_allocator->AllocateFromTail( sizeof(MicroAllocator), alignof(MicroAllocator)); - MicroAllocator* allocator = - new (allocator_buffer) MicroAllocator(memory_allocator, error_reporter); + MicroAllocator* allocator = new (allocator_buffer) + MicroAllocator(memory_allocator, memory_planner, error_reporter); return allocator; } @@ -657,7 +598,8 @@ TfLiteStatus MicroAllocator::FinishModelAllocation( } void* MicroAllocator::AllocatePersistentBuffer(size_t bytes) { - return memory_allocator_->AllocateFromTail(bytes, kBufferAlignment); + return memory_allocator_->AllocateFromTail(bytes, + MicroArenaBufferAlignment()); } TfLiteStatus MicroAllocator::RequestScratchBufferInArena(size_t bytes, @@ -887,8 +829,8 @@ TfLiteStatus MicroAllocator::AllocateVariables(const SubGraph* subgraph, TF_LITE_ENSURE_STATUS( TfLiteEvalTensorByteLength(&eval_tensors[i], &buffer_size)); - eval_tensors[i].data.data = - memory_allocator_->AllocateFromTail(buffer_size, kBufferAlignment); + eval_tensors[i].data.data = memory_allocator_->AllocateFromTail( + buffer_size, MicroArenaBufferAlignment()); if (eval_tensors[i].data.data == nullptr) { TF_LITE_REPORT_ERROR(error_reporter_, @@ -972,37 +914,37 @@ TfLiteStatus MicroAllocator::CommitStaticMemoryPlan( // Remaining arena size that memory planner can use for calculating offsets. size_t remaining_arena_size = - memory_allocator_->GetAvailableMemory(kBufferAlignment); - uint8_t* planner_arena = - memory_allocator_->AllocateTemp(remaining_arena_size, kBufferAlignment); + memory_allocator_->GetAvailableMemory(MicroArenaBufferAlignment()); + uint8_t* planner_arena = memory_allocator_->AllocateTemp( + remaining_arena_size, MicroArenaBufferAlignment()); TF_LITE_ENSURE(error_reporter_, planner_arena != nullptr); - GreedyMemoryPlanner planner(planner_arena, remaining_arena_size); - TF_LITE_ENSURE_STATUS(CreatePlan(error_reporter_, &planner, allocation_info, - allocation_info_count)); + memory_planner_->Init(planner_arena, remaining_arena_size); + TF_LITE_ENSURE_STATUS(CreatePlan(error_reporter_, memory_planner_, + allocation_info, allocation_info_count)); // Reset all temp allocations used above: memory_allocator_->ResetTempAllocations(); size_t actual_available_arena_size = - memory_allocator_->GetAvailableMemory(kBufferAlignment); + memory_allocator_->GetAvailableMemory(MicroArenaBufferAlignment()); // Make sure we have enough arena size. - if (planner.GetMaximumMemorySize() > actual_available_arena_size) { + if (memory_planner_->GetMaximumMemorySize() > actual_available_arena_size) { TF_LITE_REPORT_ERROR( error_reporter_, "Arena size is too small for all buffers. Needed %u but only " "%u was available.", - planner.GetMaximumMemorySize(), actual_available_arena_size); + memory_planner_->GetMaximumMemorySize(), actual_available_arena_size); return kTfLiteError; } // Commit the plan. - TF_LITE_ENSURE_STATUS(CommitPlan(error_reporter_, &planner, + TF_LITE_ENSURE_STATUS(CommitPlan(error_reporter_, memory_planner_, memory_allocator_->GetHeadBuffer(), allocation_info, allocation_info_count)); #ifdef TF_LITE_SHOW_MEMORY_USE - planner.PrintMemoryPlan(); + memory_planner_->PrintMemoryPlan(); #endif - head_usage = planner.GetMaximumMemorySize(); + head_usage = memory_planner_->GetMaximumMemorySize(); // The head is used to store memory plans for one model at a time during the // model preparation stage, and is re-purposed to store scratch buffer handles @@ -1017,7 +959,7 @@ TfLiteStatus MicroAllocator::CommitStaticMemoryPlan( // memory plan in this function. Ensure that the head is set to the largest // memory plan sent through the allocator: TF_LITE_ENSURE_STATUS(memory_allocator_->SetHeadBufferSize( - max_head_buffer_usage_, kBufferAlignment)); + max_head_buffer_usage_, MicroArenaBufferAlignment())); return kTfLiteOk; } @@ -1061,13 +1003,6 @@ internal::ScratchBufferRequest* MicroAllocator::GetScratchBufferRequests() { alignof(internal::ScratchBufferRequest))); } -TfLiteStatus MicroAllocator::FlatBufferVectorToTfLiteTypeArray( - const flatbuffers::Vector* flatbuffer_array, - TfLiteIntArray** result) { - return internal::FlatBufferVectorToTfLiteTypeArray( - memory_allocator_, error_reporter_, flatbuffer_array, result); -} - BuiltinDataAllocator* MicroAllocator::GetBuiltinDataAllocator() { return builtin_data_allocator_; } diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_allocator.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_allocator.h similarity index 90% rename from code/components/tfmicro/tensorflow/lite/micro/micro_allocator.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_allocator.h index 385213c5..f76fe29f 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_allocator.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_allocator.h @@ -23,11 +23,14 @@ limitations under the License. #include "tensorflow/lite/core/api/flatbuffer_conversions.h" #include "tensorflow/lite/micro/compatibility.h" #include "tensorflow/lite/micro/flatbuffer_utils.h" +#include "tensorflow/lite/micro/memory_planner/micro_memory_planner.h" #include "tensorflow/lite/micro/simple_memory_allocator.h" #include "tensorflow/lite/schema/schema_generated.h" namespace tflite { +// TODO(b/199402574): rename to tflite_internal or just remove internal +// namespace. namespace internal { // Sets up all of the data structure members for a TfLiteTensor based on the @@ -108,19 +111,32 @@ typedef struct { class MicroAllocator { public: // Creates a MicroAllocator instance from a given tensor arena. This arena - // will be managed by the created instance. - // Note: Please use __declspec(align(16)) to make sure tensor_arena is 16 + // will be managed by the created instance. The GreedyMemoryPlanner will + // by default be used and created on the arena. + // Note: Please use alignas(16) to make sure tensor_arena is 16 // bytes aligned, otherwise some head room will be wasted. // TODO(b/157615197): Cleanup constructor + factory usage. static MicroAllocator* Create(uint8_t* tensor_arena, size_t arena_size, ErrorReporter* error_reporter); - // Creates a MicroAllocator instance using the provided SimpleMemoryAllocator - // intance. This allocator instance will use the SimpleMemoryAllocator - // instance to manage allocations internally. - static MicroAllocator* Create(SimpleMemoryAllocator* memory_allocator, + // Creates a MicroAllocator instance from a given tensor arena and a given + // MemoryPlanner. This arena will be managed by the created instance. Note: + // Please use alignas(16) to make sure tensor_arena is 16 bytes + // aligned, otherwise some head room will be wasted. + static MicroAllocator* Create(uint8_t* tensor_arena, size_t arena_size, + MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter); + // Creates a MicroAllocator instance using the provided SimpleMemoryAllocator + // instance and the MemoryPlanner. This allocator instance will use the + // SimpleMemoryAllocator instance to manage allocations internally. + static MicroAllocator* Create(SimpleMemoryAllocator* memory_allocator, + MicroMemoryPlanner* memory_planner, + ErrorReporter* error_reporter); + + // Returns the fixed amount of memory overhead of MicroAllocator. + static size_t GetDefaultTailUsage(bool is_memory_planner_given); + // Allocates internal resources required for model inference for each subgraph // from the arena. // @@ -196,16 +212,11 @@ class MicroAllocator { // `FinishModelAllocation`. Otherwise, it will return 0. size_t used_bytes() const; - // Converts a flatbuffer int32_t array to a TfLiteIntArray, accounting for - // endiannes. - TfLiteStatus FlatBufferVectorToTfLiteTypeArray( - const flatbuffers::Vector* flatbuffer_array, - TfLiteIntArray** result); - BuiltinDataAllocator* GetBuiltinDataAllocator(); protected: MicroAllocator(SimpleMemoryAllocator* memory_allocator, + MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter); virtual ~MicroAllocator(); @@ -272,6 +283,9 @@ class MicroAllocator { // Allocator used to allocate persistent builtin data. BuiltinDataAllocator* builtin_data_allocator_; + // Activation buffer memory planner. + MicroMemoryPlanner* memory_planner_; + ErrorReporter* error_reporter_; bool model_is_allocating_; diff --git a/code/components/tfmicro/tensorflow/lite/micro/testing/test_conv_model.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_arena_constants.h similarity index 55% rename from code/components/tfmicro/tensorflow/lite/micro/testing/test_conv_model.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_arena_constants.h index 2103196e..82828176 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/testing/test_conv_model.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_arena_constants.h @@ -1,4 +1,4 @@ -/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,11 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#ifndef TENSORFLOW_LITE_MICRO_TESTING_TEST_CONV_MODEL_H_ -#define TENSORFLOW_LITE_MICRO_TESTING_TEST_CONV_MODEL_H_ +#ifndef TENSORFLOW_LITE_MICRO_MICRO_ARENA_CONSTANTS_H_ +#define TENSORFLOW_LITE_MICRO_MICRO_ARENA_CONSTANTS_H_ -// See generate_test_models.py for updating the contents of this model: -extern const unsigned char kTestConvModelData[]; -extern const unsigned int kTestConvModelDataSize; +namespace tflite { -#endif // TENSORFLOW_LITE_MICRO_TESTING_TEST_CONV_MODEL_H_ +// The default buffer alignment requirement. +// We align tensor buffers to 16-byte boundaries, since this is a common +// requirement for SIMD extensions. +constexpr int MicroArenaBufferAlignment() { return 16; } + +} // namespace tflite + +#endif // TENSORFLOW_LITE_MICRO_MICRO_ARENA_CONSTANTS_H_ diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_error_reporter.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_error_reporter.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/micro_error_reporter.cc rename to code/components/tflite-lib/tensorflow/lite/micro/micro_error_reporter.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_error_reporter.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_error_reporter.h similarity index 87% rename from code/components/tfmicro/tensorflow/lite/micro/micro_error_reporter.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_error_reporter.h index ac45224a..0e3b0c38 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_error_reporter.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_error_reporter.h @@ -27,11 +27,18 @@ void MicroPrintf(const char* format, ...); #else // We use a #define to ensure that the strings are completely stripped, to // prevent an unnecessary increase in the binary size. -#define MicroPrintf(format, ...) +#define MicroPrintf(...) tflite::Unused(__VA_ARGS__) #endif namespace tflite { +// From +// https://stackoverflow.com/questions/23235910/variadic-unused-function-macro +template +void Unused(Args&&... args) { + (void)(sizeof...(args)); +} + // Get a pointer to a singleton global error reporter. ErrorReporter* GetMicroErrorReporter(); diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_graph.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_graph.cc similarity index 97% rename from code/components/tfmicro/tensorflow/lite/micro/micro_graph.cc rename to code/components/tflite-lib/tensorflow/lite/micro/micro_graph.cc index ac39f72e..0abe0173 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_graph.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_graph.cc @@ -27,7 +27,6 @@ limitations under the License. namespace tflite { namespace { -#ifndef TF_LITE_STRIP_ERROR_STRINGS const char* OpNameFromRegistration(const TfLiteRegistration* registration) { if (registration->builtin_code == BuiltinOperator_CUSTOM) { return registration->custom_name; @@ -35,16 +34,17 @@ const char* OpNameFromRegistration(const TfLiteRegistration* registration) { return EnumNameBuiltinOperator(BuiltinOperator(registration->builtin_code)); } } -#endif // !defined(TF_LITE_STRIP_ERROR_STRINGS) } // namespace MicroGraph::MicroGraph(TfLiteContext* context, const Model* model, - MicroAllocator* allocator) + MicroAllocator* allocator, + MicroResourceVariables* resource_variables) : context_(context), model_(model), allocator_(allocator), - current_subgraph_index_(0) { + current_subgraph_index_(0), + resource_variables_(resource_variables) { if (model != nullptr) { subgraphs_ = model->subgraphs(); } diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_graph.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_graph.h similarity index 84% rename from code/components/tfmicro/tensorflow/lite/micro/micro_graph.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_graph.h index bc45ee49..942082ac 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_graph.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_graph.h @@ -18,6 +18,7 @@ limitations under the License. #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/micro/micro_allocator.h" +#include "tensorflow/lite/micro/micro_resource_variable.h" #include "tensorflow/lite/schema/schema_generated.h" namespace tflite { @@ -28,11 +29,13 @@ namespace tflite { // subgraph in the tflite::Graph. class MicroGraph { public: - // The lifetime of the context, model, and allocator must be at least as long - // as that of the graph object, since the this class may need to access them - // at any time. + // The lifetime of the context, model, allocator and resource_variables must + // be at least as long as that of the graph object, since the this class may + // need to access them at any time. If resource_variables is a nullptr, + // GetResourceVariables will return a nullptr. MicroGraph(TfLiteContext* context, const Model* model, - MicroAllocator* allocator); + MicroAllocator* allocator, + MicroResourceVariables* resource_variables); virtual ~MicroGraph(); // Sets up builtin data and calls TfLiteRegistration->Init for every operator @@ -81,12 +84,16 @@ class MicroGraph { // for all per-subgraph allocation data. SubgraphAllocations* GetAllocations() { return subgraph_allocations_; } + // Get the resource variables for this TFLM graph. + MicroResourceVariables* GetResourceVariables() { return resource_variables_; } + private: TfLiteContext* context_; const Model* model_; MicroAllocator* allocator_; SubgraphAllocations* subgraph_allocations_ = nullptr; int current_subgraph_index_; + MicroResourceVariables* resource_variables_; const flatbuffers::Vector>* subgraphs_; TF_LITE_REMOVE_VIRTUAL_DELETE diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_interpreter.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_interpreter.cc similarity index 86% rename from code/components/tfmicro/tensorflow/lite/micro/micro_interpreter.cc rename to code/components/tflite-lib/tensorflow/lite/micro/micro_interpreter.cc index 9efb0532..3f8c44ad 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_interpreter.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_interpreter.cc @@ -19,6 +19,7 @@ limitations under the License. #include #include "flatbuffers/flatbuffers.h" // from @flatbuffers +#include "tensorflow/lite/c/c_api_types.h" #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/core/api/error_reporter.h" #include "tensorflow/lite/core/api/tensor_utils.h" @@ -38,6 +39,7 @@ MicroInterpreter::MicroInterpreter(const Model* model, uint8_t* tensor_arena, size_t tensor_arena_size, ErrorReporter* error_reporter, + MicroResourceVariables* resource_variables, MicroProfiler* profiler) : model_(model), op_resolver_(op_resolver), @@ -45,7 +47,7 @@ MicroInterpreter::MicroInterpreter(const Model* model, allocator_(*MicroAllocator::Create(tensor_arena, tensor_arena_size, error_reporter)), - graph_(&context_, model, &allocator_), + graph_(&context_, model, &allocator_, resource_variables), tensors_allocated_(false), initialization_status_(kTfLiteError), input_tensors_(nullptr), @@ -57,12 +59,13 @@ MicroInterpreter::MicroInterpreter(const Model* model, const MicroOpResolver& op_resolver, MicroAllocator* allocator, ErrorReporter* error_reporter, + MicroResourceVariables* resource_variables, MicroProfiler* profiler) : model_(model), op_resolver_(op_resolver), error_reporter_(error_reporter), allocator_(*allocator), - graph_(&context_, model, allocator), + graph_(&context_, model, allocator, resource_variables), tensors_allocated_(false), initialization_status_(kTfLiteError), input_tensors_(nullptr), @@ -159,13 +162,10 @@ TfLiteStatus MicroInterpreter::PrepareNodeAndRegistrationDataFromFlatbuffer() { (void**)(&builtin_data))); } - TfLiteIntArray* inputs_array; - TF_LITE_ENSURE_STATUS(allocator_.FlatBufferVectorToTfLiteTypeArray( - op->inputs(), &inputs_array)); - - TfLiteIntArray* outputs_array; - TF_LITE_ENSURE_STATUS(allocator_.FlatBufferVectorToTfLiteTypeArray( - op->outputs(), &outputs_array)); + TfLiteIntArray* inputs_array = + FlatBufferVectorToTfLiteTypeArray(op->inputs()); + TfLiteIntArray* outputs_array = + FlatBufferVectorToTfLiteTypeArray(op->outputs()); TfLiteNode* node = &( graph_.GetAllocations()[subgraph_idx].node_and_registrations[i].node); @@ -175,6 +175,11 @@ TfLiteStatus MicroInterpreter::PrepareNodeAndRegistrationDataFromFlatbuffer() { node->builtin_data = reinterpret_cast(builtin_data); node->custom_initial_data = custom_data; node->custom_initial_data_size = custom_data_size; + + if (op->intermediates() && (op->intermediates()->size() > 0)) { + node->intermediates = + FlatBufferVectorToTfLiteTypeArray(op->intermediates()); + } } } return kTfLiteOk; @@ -199,12 +204,16 @@ TfLiteStatus MicroInterpreter::AllocateTensors() { context_.RequestScratchBufferInArena = nullptr; context_.GetScratchBuffer = nullptr; context_.GetExecutionPlan = GetGraph; - graph_.InitSubgraphs(); + context_.GetExternalContext = nullptr; + TF_LITE_ENSURE_STATUS(graph_.InitSubgraphs()); // Both AllocatePersistentBuffer and RequestScratchBufferInArena is // available in Prepare stage. context_.RequestScratchBufferInArena = RequestScratchBufferInArena; - graph_.PrepareSubgraphs(); + // GetExternalContext become available in Prepare stage. + context_.GetExternalContext = GetExternalContext; + + TF_LITE_ENSURE_STATUS(graph_.PrepareSubgraphs()); // Prepare is done, we're ready for Invoke. Memory allocation is no longer // allowed. Kernels can only fetch scratch buffers via GetScratchBuffer. @@ -372,4 +381,36 @@ TfLiteStatus MicroInterpreter::GetGraph(struct TfLiteContext* context, return kTfLiteOk; } +TfLiteStatus MicroInterpreter::SetMicroExternalContext( + void* external_context_payload) { + if (external_context_payload == nullptr || + external_context_payload_ != nullptr) { + MicroPrintf( + "Attempting to set external context to %x but it was %x already", + external_context_payload, external_context_payload_); + return kTfLiteError; + } + + external_context_payload_ = external_context_payload; + return kTfLiteOk; +} + +void* MicroInterpreter::GetMicroExternalContext() { + return external_context_payload_; +} + +// This callback is an implementation for TfLiteContext::GetExternalContext +// interface. +TfLiteExternalContext* MicroInterpreter::GetExternalContext( + TfLiteContext* context, TfLiteExternalContextType unused) { + // TODO(b/205754757): TfLiteExternalContextType is unused in TFLM. This + // function is only called by the framework as a way to conform to existing + // interface. Users should use GetMicroExternalContext api in kernel_util.h to + // get context and shall not directly use this function. + MicroInterpreter* interpreter = + reinterpret_cast(context->impl_); + return reinterpret_cast( + interpreter->GetMicroExternalContext()); +} + } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_interpreter.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_interpreter.h similarity index 82% rename from code/components/tfmicro/tensorflow/lite/micro/micro_interpreter.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_interpreter.h index 20819c67..33123af6 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_interpreter.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_interpreter.h @@ -19,6 +19,7 @@ limitations under the License. #include #include "flatbuffers/flatbuffers.h" // from @flatbuffers +#include "tensorflow/lite/c/c_api_types.h" #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/core/api/error_reporter.h" #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" @@ -37,16 +38,18 @@ namespace tflite { class MicroInterpreter { public: - // The lifetime of the model, op resolver, tensor arena, error reporter and - // profiler must be at least as long as that of the interpreter object, since - // the interpreter may need to access them at any time. This means that you - // should usually create them with the same scope as each other, for example - // having them all allocated on the stack as local variables through a - // top-level function. The interpreter doesn't do any deallocation of any of - // the pointed-to objects, ownership remains with the caller. + // The lifetime of the model, op resolver, tensor arena, error reporter, + // resource variables, and profiler must be at least as long as that of the + // interpreter object, since the interpreter may need to access them at any + // time. This means that you should usually create them with the same scope as + // each other, for example having them all allocated on the stack as local + // variables through a top-level function. The interpreter doesn't do any + // deallocation of any of the pointed-to objects, ownership remains with the + // caller. MicroInterpreter(const Model* model, const MicroOpResolver& op_resolver, uint8_t* tensor_arena, size_t tensor_arena_size, ErrorReporter* error_reporter, + MicroResourceVariables* resource_variables = nullptr, MicroProfiler* profiler = nullptr); // Create an interpreter instance using an existing MicroAllocator instance. @@ -56,6 +59,7 @@ class MicroInterpreter { // as long as that of the interpreter object. MicroInterpreter(const Model* model, const MicroOpResolver& op_resolver, MicroAllocator* allocator, ErrorReporter* error_reporter, + MicroResourceVariables* resource_variables = nullptr, MicroProfiler* profiler = nullptr); ~MicroInterpreter(); @@ -69,6 +73,16 @@ class MicroInterpreter { // TODO(b/149795762): Add this to the TfLiteStatus enum. TfLiteStatus Invoke(); + // This is the recommended API for an application to pass an external payload + // pointer as an external context to kernels. The life time of the payload + // pointer should be at least as long as this interpreter. TFLM supports only + // one external context. + TfLiteStatus SetMicroExternalContext(void* external_context_payload); + + // This function is used by the TfLiteContext::GetExternalContext() to get the + // external context. + void* GetMicroExternalContext(); + TfLiteTensor* input(size_t index); size_t inputs_size() const { return model_->subgraphs()->Get(0)->inputs()->size(); @@ -151,6 +165,11 @@ class MicroInterpreter { static TfLiteStatus GetGraph(struct TfLiteContext* context, TfLiteIntArray** args); + // This callback is an implementation for TfLiteContext::GetExternalContext + // interface. + static TfLiteExternalContext* GetExternalContext( + TfLiteContext* context, TfLiteExternalContextType unused); + const Model* model_; const MicroOpResolver& op_resolver_; ErrorReporter* error_reporter_; @@ -162,6 +181,7 @@ class MicroInterpreter { TfLiteStatus initialization_status_; ScratchBufferHandle* scratch_buffer_handles_ = nullptr; + void* external_context_payload_ = nullptr; // TODO(b/162311891): Clean these pointers up when this class supports buffers // from TfLiteEvalTensor. diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_mutable_op_resolver.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_mutable_op_resolver.h similarity index 91% rename from code/components/tfmicro/tensorflow/lite/micro/micro_mutable_op_resolver.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_mutable_op_resolver.h index e5524d15..8784f29a 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_mutable_op_resolver.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_mutable_op_resolver.h @@ -120,8 +120,7 @@ class MicroMutableOpResolver : public MicroOpResolver { } TfLiteStatus AddAdd() { - return AddBuiltin(BuiltinOperator_ADD, tflite::ops::micro::Register_ADD(), - ParseAdd); + return AddBuiltin(BuiltinOperator_ADD, tflite::Register_ADD(), ParseAdd); } TfLiteStatus AddAddN() { @@ -139,6 +138,11 @@ class MicroMutableOpResolver : public MicroOpResolver { tflite::ops::micro::Register_ARG_MIN(), ParseArgMin); } + TfLiteStatus AddAssignVariable() { + return AddBuiltin(BuiltinOperator_ASSIGN_VARIABLE, + tflite::Register_ASSIGN_VARIABLE(), ParseAssignVariable); + } + TfLiteStatus AddAveragePool2D() { return AddBuiltin(BuiltinOperator_AVERAGE_POOL_2D, tflite::Register_AVERAGE_POOL_2D(), ParsePool); @@ -149,6 +153,11 @@ class MicroMutableOpResolver : public MicroOpResolver { Register_BATCH_TO_SPACE_ND(), ParseBatchToSpaceNd); } + TfLiteStatus AddCallOnce() { + return AddBuiltin(BuiltinOperator_CALL_ONCE, Register_CALL_ONCE(), + ParseCallOnce); + } + TfLiteStatus AddCast() { return AddBuiltin(BuiltinOperator_CAST, Register_CAST(), ParseCast); } @@ -159,8 +168,7 @@ class MicroMutableOpResolver : public MicroOpResolver { } TfLiteStatus AddCircularBuffer() { - return AddCustom("CIRCULAR_BUFFER", - tflite::ops::micro::Register_CIRCULAR_BUFFER()); + return AddCustom("CIRCULAR_BUFFER", tflite::Register_CIRCULAR_BUFFER()); } TfLiteStatus AddConcatenation() { @@ -195,8 +203,7 @@ class MicroMutableOpResolver : public MicroOpResolver { } TfLiteStatus AddDequantize() { - return AddBuiltin(BuiltinOperator_DEQUANTIZE, - tflite::ops::micro::Register_DEQUANTIZE(), + return AddBuiltin(BuiltinOperator_DEQUANTIZE, tflite::Register_DEQUANTIZE(), ParseDequantize); } @@ -360,8 +367,7 @@ class MicroMutableOpResolver : public MicroOpResolver { } TfLiteStatus AddMul() { - return AddBuiltin(BuiltinOperator_MUL, tflite::ops::micro::Register_MUL(), - ParseMul); + return AddBuiltin(BuiltinOperator_MUL, tflite::Register_MUL(), ParseMul); } TfLiteStatus AddNeg() { @@ -390,8 +396,8 @@ class MicroMutableOpResolver : public MicroOpResolver { } TfLiteStatus AddPrelu() { - return AddBuiltin(BuiltinOperator_PRELU, - tflite::ops::micro::Register_PRELU(), ParsePrelu); + return AddBuiltin(BuiltinOperator_PRELU, tflite::Register_PRELU(), + ParsePrelu); } TfLiteStatus AddQuantize() { @@ -399,6 +405,11 @@ class MicroMutableOpResolver : public MicroOpResolver { ParseQuantize); } + TfLiteStatus AddReadVariable() { + return AddBuiltin(BuiltinOperator_READ_VARIABLE, + tflite::Register_READ_VARIABLE(), ParseReadVariable); + } + TfLiteStatus AddReduceMax() { return AddBuiltin(BuiltinOperator_REDUCE_MAX, tflite::ops::micro::Register_REDUCE_MAX(), ParseReducer); @@ -448,6 +459,10 @@ class MicroMutableOpResolver : public MicroOpResolver { ParseSin); } + TfLiteStatus AddSlice() { + return AddBuiltin(BuiltinOperator_SLICE, Register_SLICE(), ParseSlice); + } + TfLiteStatus AddSoftmax( const TfLiteRegistration& registration = Register_SOFTMAX()) { return AddBuiltin(BuiltinOperator_SOFTMAX, registration, ParseSoftmax); @@ -495,12 +510,12 @@ class MicroMutableOpResolver : public MicroOpResolver { } TfLiteStatus AddSub() { - return AddBuiltin(BuiltinOperator_SUB, tflite::ops::micro::Register_SUB(), - ParseSub); + return AddBuiltin(BuiltinOperator_SUB, tflite::Register_SUB(), ParseSub); } - TfLiteStatus AddSvdf() { - return AddBuiltin(BuiltinOperator_SVDF, Register_SVDF(), ParseSvdf); + TfLiteStatus AddSvdf( + const TfLiteRegistration& registration = Register_SVDF()) { + return AddBuiltin(BuiltinOperator_SVDF, registration, ParseSvdf); } TfLiteStatus AddTanh() { @@ -523,6 +538,18 @@ class MicroMutableOpResolver : public MicroOpResolver { tflite::ops::micro::Register_UNPACK(), ParseUnpack); } + TfLiteStatus AddUnidirectionalSequenceLSTM() { + return AddBuiltin( + BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, + tflite::ops::micro::Register_UNIDIRECTIONAL_SEQUENCE_LSTM(), + ParseUnidirectionalSequenceLSTM); + } + + TfLiteStatus AddVarHandle() { + return AddBuiltin(BuiltinOperator_VAR_HANDLE, Register_VAR_HANDLE(), + ParseVarHandle); + } + TfLiteStatus AddZerosLike() { return AddBuiltin(BuiltinOperator_ZEROS_LIKE, Register_ZEROS_LIKE(), ParseZerosLike); diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_op_resolver.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_op_resolver.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/micro_op_resolver.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_op_resolver.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_profiler.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_profiler.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/micro_profiler.cc rename to code/components/tflite-lib/tensorflow/lite/micro/micro_profiler.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_profiler.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_profiler.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/micro_profiler.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_profiler.h diff --git a/code/components/tflite-lib/tensorflow/lite/micro/micro_resource_variable.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_resource_variable.cc new file mode 100644 index 00000000..e7232f47 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_resource_variable.cc @@ -0,0 +1,140 @@ +/* Copyright 2020 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/micro/micro_resource_variable.h" + +#include + +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/compatibility.h" +#include "tensorflow/lite/micro/memory_helpers.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" +#include "tensorflow/lite/micro/micro_utils.h" + +namespace tflite { + +namespace {} // namespace + +MicroResourceVariables* MicroResourceVariables::Create( + MicroAllocator* allocator, int max_num_variables) { + TFLITE_DCHECK(allocator != nullptr); + + uint8_t* allocator_buffer = static_cast( + allocator->AllocatePersistentBuffer(sizeof(MicroResourceVariables))); + MicroResourceVariable* variable_array = + static_cast(allocator->AllocatePersistentBuffer( + sizeof(MicroResourceVariable) * max_num_variables)); + MicroResourceVariables* variables = new (allocator_buffer) + MicroResourceVariables(variable_array, max_num_variables); + return variables; +} + +int MicroResourceVariables::CreateIdIfNoneFound(const char* container, + const char* shared_name) { + int resource_id = FindId(container, shared_name); + if (resource_id >= 0) { + return resource_id; + } + + // no existing variable found for the given container and shared name pair. + if (num_resource_variables_ >= max_variable_count_) { + MicroPrintf( + "Failed to allocate resource variable. Maximum resource variable count " + "(%d) " + "reached.", + max_variable_count_); + return -1; + } + + resource_id = num_resource_variables_++; + resource_variables_[resource_id].container = container; + resource_variables_[resource_id].shared_name = shared_name; + resource_variables_[resource_id].resource_buffer = nullptr; + resource_variables_[resource_id].bytes = 0; + return resource_id; +} + +TfLiteStatus MicroResourceVariables::Read(int id, + const TfLiteEvalTensor* tensor) { + if (id < 0 || id >= num_resource_variables_) { + MicroPrintf("Attempting to read non-existent resource variable %d", id); + return kTfLiteError; + } + MicroResourceVariable variable = resource_variables_[id]; + TFLITE_DCHECK(EvalTensorBytes(tensor) == variable.bytes); + TFLITE_DCHECK(variable.resource_buffer != nullptr); + memcpy(tensor->data.raw, variable.resource_buffer, variable.bytes); + return kTfLiteOk; +} + +TfLiteStatus MicroResourceVariables::Allocate(int id, TfLiteContext* context, + const TfLiteTensor* tensor) { + if (id < 0 || id >= num_resource_variables_) { + MicroPrintf("Attempting to read non-existent resource variable %d", id); + return kTfLiteError; + } + + MicroResourceVariable& variable = resource_variables_[id]; + + if (variable.resource_buffer == nullptr) { + variable.bytes = tensor->bytes; + variable.resource_buffer = + context->AllocatePersistentBuffer(context, tensor->bytes); + if (variable.resource_buffer == nullptr) { + MicroPrintf("Failed to allocate resource buffer."); + return kTfLiteError; + } + // Zero out resource buffers by deafult. Buffers can be initialized to + // nonzero values using ASSIGN_VARIABLE. + memset(variable.resource_buffer, 0, variable.bytes); + } + + return kTfLiteOk; +} + +TfLiteStatus MicroResourceVariables::Assign(int id, + const TfLiteEvalTensor* tensor) { + if (id < 0 || id >= num_resource_variables_) { + MicroPrintf("Attempting to read non-existent resource variable %d", id); + return kTfLiteError; + } + MicroResourceVariable variable = resource_variables_[id]; + + if (variable.resource_buffer == nullptr) { + MicroPrintf( + "Attempting to assign from a TfLiteEvalTensor before the resource " + "buffer has been allocated. Make sure to call AssignResourceVariable " + "with a TfLiteTensor first."); + return kTfLiteError; + } + TFLITE_DCHECK(EvalTensorBytes(tensor) == variable.bytes); + memcpy(variable.resource_buffer, tensor->data.raw, variable.bytes); + return kTfLiteOk; +} + +int MicroResourceVariables::FindId(const char* container, + const char* shared_name) { + for (int i = 0; i < num_resource_variables_; i++) { + // Some TFLite flatbuffers contain null container names to save space. + if ((container == nullptr || + !strcmp(container, resource_variables_[i].container)) && + !strcmp(shared_name, resource_variables_[i].shared_name)) { + return i; + } + } + return -1; +} + +} // namespace tflite diff --git a/code/components/tflite-lib/tensorflow/lite/micro/micro_resource_variable.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_resource_variable.h new file mode 100644 index 00000000..0bab8e13 --- /dev/null +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_resource_variable.h @@ -0,0 +1,84 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TFLITE_MICRO_TENSORFLOW_LITE_MICRO_MICRO_RESOURCE_H_ +#define TFLITE_MICRO_TENSORFLOW_LITE_MICRO_MICRO_RESOURCE_H_ + +#include + +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/micro/micro_allocator.h" + +namespace tflite { + +class MicroResourceVariables { + public: + // Create + static MicroResourceVariables* Create(MicroAllocator* allocator, + int num_variables); + + // Creates a resource variable if none is available for the given container + // and shared name pair. Returns the resource ID corresponding to the + // container and shared name pair. If allocation fails, the returned resource + // ID will be negative. The the container and shared_name must outlive this + // class. + int CreateIdIfNoneFound(const char* container, const char* shared_name); + + // Read the resource buffer associated with the given ID into the given + // tensor. + TfLiteStatus Read(int id, const TfLiteEvalTensor* tensor); + + // Allocates the resource buffer if none has been allocated, based on the + // length of the input tensor. Copies input tensor contents to the resource + // buffer. + TfLiteStatus Allocate(int id, TfLiteContext* context, + const TfLiteTensor* tensor); + + // Copies input tensor contents to the resource buffer. + // AllocateResourceVariable with a TFLite tensor must have been called first + // in order to allocate the resource buffer. + TfLiteStatus Assign(int id, const TfLiteEvalTensor* tensor); + + private: + int FindId(const char* container, const char* shared_name); + + // Micro resource contains the mapping between resource container/name strings + // and resouce IDs. Each resource ID corresponds to a resource buffer pointer. + // The resouce ID is created during the VAR_HANDLE operator preparation stage. + // The resource buffer pointer is created during ASSIGN_VARIABLE preparation + // stage based on the size of the TFLiteTensor being assigned. + struct MicroResourceVariable { + const char* container; + const char* shared_name; + void* resource_buffer; + + // This is only for verifying read size. + size_t bytes; + }; + + MicroResourceVariables(MicroResourceVariable* variables, + int max_variable_count) + : resource_variables_(variables), + max_variable_count_(max_variable_count), + num_resource_variables_(0) {} + + MicroResourceVariable* resource_variables_; + int max_variable_count_; + int num_resource_variables_; +}; + +} // namespace tflite + +#endif // TFLITE_MICRO_TENSORFLOW_LITE_MICRO_MICRO_RESOURCE_H_ diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_string.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_string.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/micro_string.cc rename to code/components/tflite-lib/tensorflow/lite/micro/micro_string.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_string.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_string.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/micro_string.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_string.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_time.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_time.cc similarity index 95% rename from code/components/tfmicro/tensorflow/lite/micro/micro_time.cc rename to code/components/tflite-lib/tensorflow/lite/micro/micro_time.cc index d7c51f90..bbe3f1a8 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_time.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_time.cc @@ -22,8 +22,7 @@ limitations under the License. // To add an equivalent function for your own platform, create your own // implementation file, and place it in a subfolder with named after the OS // you're targeting. For example, see the Cortex M bare metal version in -// tensorflow/lite/micro/bluepill/micro_time.cc or the mbed one on -// tensorflow/lite/micro/mbed/micro_time.cc. +// tensorflow/lite/micro/bluepill/micro_time.cc #include "tensorflow/lite/micro/micro_time.h" diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_time.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_time.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/micro_time.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_time.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_utils.cc b/code/components/tflite-lib/tensorflow/lite/micro/micro_utils.cc similarity index 88% rename from code/components/tfmicro/tensorflow/lite/micro/micro_utils.cc rename to code/components/tflite-lib/tensorflow/lite/micro/micro_utils.cc index f58b7b45..97b83695 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_utils.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_utils.cc @@ -20,7 +20,9 @@ limitations under the License. #include #include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/compatibility.h" #include "tensorflow/lite/kernels/op_macros.h" +#include "tensorflow/lite/micro/memory_helpers.h" #include "tensorflow/lite/micro/micro_error_reporter.h" namespace tflite { @@ -33,6 +35,13 @@ int ElementCount(const TfLiteIntArray& dims) { return result; } +size_t EvalTensorBytes(const TfLiteEvalTensor* tensor) { + size_t bytes_per_element; + TFLITE_DCHECK(kTfLiteOk == + TfLiteTypeSizeOf(tensor->type, &bytes_per_element)); + return ElementCount(*tensor->dims) * bytes_per_element; +} + void SignedSymmetricPerChannelQuantize(const float* values, TfLiteIntArray* dims, int quantized_dimension, diff --git a/code/components/tfmicro/tensorflow/lite/micro/micro_utils.h b/code/components/tflite-lib/tensorflow/lite/micro/micro_utils.h similarity index 95% rename from code/components/tfmicro/tensorflow/lite/micro/micro_utils.h rename to code/components/tflite-lib/tensorflow/lite/micro/micro_utils.h index b0ec26e1..84d5c437 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/micro_utils.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/micro_utils.h @@ -29,6 +29,12 @@ namespace tflite { int ElementCount(const TfLiteIntArray& dims); +size_t EvalTensorBytes(const TfLiteEvalTensor* tensor); + +// C++11 does not support constexpr max; hence, use ternary conditional to +// create our own constexpr Max function. +constexpr int Max(int a, int b) { return a >= b ? a : b; } + // Converts a float value into a quantized value. Note that large values (close // to max int and min int) may see significant error due to a lack of floating // point granularity for large values. diff --git a/code/components/tfmicro/tensorflow/lite/micro/mock_micro_graph.cc b/code/components/tflite-lib/tensorflow/lite/micro/mock_micro_graph.cc similarity index 97% rename from code/components/tfmicro/tensorflow/lite/micro/mock_micro_graph.cc rename to code/components/tflite-lib/tensorflow/lite/micro/mock_micro_graph.cc index 557a27a0..951664b5 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/mock_micro_graph.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/mock_micro_graph.cc @@ -20,7 +20,7 @@ limitations under the License. namespace tflite { MockMicroGraph::MockMicroGraph(SimpleMemoryAllocator* allocator) - : MicroGraph(nullptr, nullptr, nullptr), + : MicroGraph(nullptr, nullptr, nullptr, nullptr), allocator_(allocator), init_count_(0), prepare_count_(0), diff --git a/code/components/tfmicro/tensorflow/lite/micro/mock_micro_graph.h b/code/components/tflite-lib/tensorflow/lite/micro/mock_micro_graph.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/mock_micro_graph.h rename to code/components/tflite-lib/tensorflow/lite/micro/mock_micro_graph.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/recording_micro_allocator.cc b/code/components/tflite-lib/tensorflow/lite/micro/recording_micro_allocator.cc similarity index 86% rename from code/components/tfmicro/tensorflow/lite/micro/recording_micro_allocator.cc rename to code/components/tflite-lib/tensorflow/lite/micro/recording_micro_allocator.cc index 1e32e235..349018d4 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/recording_micro_allocator.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/recording_micro_allocator.cc @@ -18,15 +18,36 @@ limitations under the License. #include "tensorflow/lite/core/api/error_reporter.h" #include "tensorflow/lite/kernels/internal/compatibility.h" #include "tensorflow/lite/micro/compatibility.h" +#include "tensorflow/lite/micro/memory_helpers.h" +#include "tensorflow/lite/micro/memory_planner/greedy_memory_planner.h" #include "tensorflow/lite/micro/micro_allocator.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" #include "tensorflow/lite/micro/recording_simple_memory_allocator.h" namespace tflite { +size_t RecordingMicroAllocator::GetDefaultTailUsage() { + // RecordingMicroAllocator inherits from MicroAllocator and its tail usage is + // similar with MicroAllocator with SimpleMemoryAllocator and MicroAllocator + // being replaced. + // TODO(b/208703041): a template version of AlignSizeUp to make expression + // shorter. + return MicroAllocator::GetDefaultTailUsage( + /*is_memory_planner_given=*/false) + + AlignSizeUp(sizeof(RecordingSimpleMemoryAllocator), + alignof(RecordingSimpleMemoryAllocator)) - + AlignSizeUp(sizeof(SimpleMemoryAllocator), + alignof(SimpleMemoryAllocator)) + + AlignSizeUp(sizeof(RecordingMicroAllocator), + alignof(RecordingMicroAllocator)) - + AlignSizeUp(sizeof(MicroAllocator), alignof(MicroAllocator)); +} + RecordingMicroAllocator::RecordingMicroAllocator( RecordingSimpleMemoryAllocator* recording_memory_allocator, - ErrorReporter* error_reporter) - : MicroAllocator(recording_memory_allocator, error_reporter), + MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter) + : MicroAllocator(recording_memory_allocator, memory_planner, + error_reporter), recording_memory_allocator_(recording_memory_allocator) {} RecordingMicroAllocator* RecordingMicroAllocator::Create( @@ -38,10 +59,16 @@ RecordingMicroAllocator* RecordingMicroAllocator::Create( arena_size); TFLITE_DCHECK(simple_memory_allocator != nullptr); + uint8_t* memory_planner_buffer = simple_memory_allocator->AllocateFromTail( + sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner)); + GreedyMemoryPlanner* memory_planner = + new (memory_planner_buffer) GreedyMemoryPlanner(); + uint8_t* allocator_buffer = simple_memory_allocator->AllocateFromTail( sizeof(RecordingMicroAllocator), alignof(RecordingMicroAllocator)); - RecordingMicroAllocator* allocator = new (allocator_buffer) - RecordingMicroAllocator(simple_memory_allocator, error_reporter); + RecordingMicroAllocator* allocator = + new (allocator_buffer) RecordingMicroAllocator( + simple_memory_allocator, memory_planner, error_reporter); return allocator; } diff --git a/code/components/tfmicro/tensorflow/lite/micro/recording_micro_allocator.h b/code/components/tflite-lib/tensorflow/lite/micro/recording_micro_allocator.h similarity index 96% rename from code/components/tfmicro/tensorflow/lite/micro/recording_micro_allocator.h rename to code/components/tflite-lib/tensorflow/lite/micro/recording_micro_allocator.h index c3f42239..6b039c03 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/recording_micro_allocator.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/recording_micro_allocator.h @@ -58,6 +58,9 @@ class RecordingMicroAllocator : public MicroAllocator { size_t arena_size, ErrorReporter* error_reporter); + // Returns the fixed amount of memory overhead of RecordingMicroAllocator. + static size_t GetDefaultTailUsage(); + // Returns the recorded allocations information for a given allocation type. RecordedAllocation GetRecordedAllocation( RecordedAllocationType allocation_type) const; @@ -92,6 +95,7 @@ class RecordingMicroAllocator : public MicroAllocator { private: RecordingMicroAllocator(RecordingSimpleMemoryAllocator* memory_allocator, + MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter); void PrintRecordedAllocation(RecordedAllocationType allocation_type, diff --git a/code/components/tfmicro/tensorflow/lite/micro/recording_micro_interpreter.h b/code/components/tflite-lib/tensorflow/lite/micro/recording_micro_interpreter.h similarity index 86% rename from code/components/tfmicro/tensorflow/lite/micro/recording_micro_interpreter.h rename to code/components/tflite-lib/tensorflow/lite/micro/recording_micro_interpreter.h index fcb89f5a..6d7602be 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/recording_micro_interpreter.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/recording_micro_interpreter.h @@ -38,19 +38,23 @@ class RecordingMicroInterpreter : public MicroInterpreter { const MicroOpResolver& op_resolver, uint8_t* tensor_arena, size_t tensor_arena_size, ErrorReporter* error_reporter, + MicroResourceVariables* resource_variable = nullptr, MicroProfiler* profiler = nullptr) : MicroInterpreter(model, op_resolver, RecordingMicroAllocator::Create( tensor_arena, tensor_arena_size, error_reporter), - error_reporter, profiler), + error_reporter, resource_variable, profiler), recording_micro_allocator_( static_cast(allocator())) {} RecordingMicroInterpreter(const Model* model, const MicroOpResolver& op_resolver, RecordingMicroAllocator* allocator, - ErrorReporter* error_reporter) - : MicroInterpreter(model, op_resolver, allocator, error_reporter), + ErrorReporter* error_reporter, + MicroResourceVariables* resource_variable = nullptr, + MicroProfiler* profiler = nullptr) + : MicroInterpreter(model, op_resolver, allocator, error_reporter, + resource_variable, profiler), recording_micro_allocator_(*allocator) {} const RecordingMicroAllocator& GetMicroAllocator() const { diff --git a/code/components/tfmicro/tensorflow/lite/micro/recording_simple_memory_allocator.cc b/code/components/tflite-lib/tensorflow/lite/micro/recording_simple_memory_allocator.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/recording_simple_memory_allocator.cc rename to code/components/tflite-lib/tensorflow/lite/micro/recording_simple_memory_allocator.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/recording_simple_memory_allocator.h b/code/components/tflite-lib/tensorflow/lite/micro/recording_simple_memory_allocator.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/recording_simple_memory_allocator.h rename to code/components/tflite-lib/tensorflow/lite/micro/recording_simple_memory_allocator.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/simple_memory_allocator.cc b/code/components/tflite-lib/tensorflow/lite/micro/simple_memory_allocator.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/simple_memory_allocator.cc rename to code/components/tflite-lib/tensorflow/lite/micro/simple_memory_allocator.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/simple_memory_allocator.h b/code/components/tflite-lib/tensorflow/lite/micro/simple_memory_allocator.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/simple_memory_allocator.h rename to code/components/tflite-lib/tensorflow/lite/micro/simple_memory_allocator.h index 35adaf1a..36ab80b3 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/simple_memory_allocator.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/simple_memory_allocator.h @@ -87,6 +87,8 @@ class SimpleMemoryAllocator { // account any temporary allocations. size_t GetUsedBytes() const; + TF_LITE_REMOVE_VIRTUAL_DELETE + protected: // Returns a pointer to the current end of the head buffer. uint8_t* head() const; @@ -103,8 +105,6 @@ class SimpleMemoryAllocator { uint8_t* head_; uint8_t* tail_; uint8_t* temp_; - - TF_LITE_REMOVE_VIRTUAL_DELETE }; } // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/system_setup.cc b/code/components/tflite-lib/tensorflow/lite/micro/system_setup.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/system_setup.cc rename to code/components/tflite-lib/tensorflow/lite/micro/system_setup.cc diff --git a/code/components/tfmicro/tensorflow/lite/micro/system_setup.h b/code/components/tflite-lib/tensorflow/lite/micro/system_setup.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/micro/system_setup.h rename to code/components/tflite-lib/tensorflow/lite/micro/system_setup.h diff --git a/code/components/tfmicro/tensorflow/lite/micro/test_helpers.cc b/code/components/tflite-lib/tensorflow/lite/micro/test_helpers.cc similarity index 84% rename from code/components/tfmicro/tensorflow/lite/micro/test_helpers.cc rename to code/components/tflite-lib/tensorflow/lite/micro/test_helpers.cc index 8bc28516..2a5700e6 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/test_helpers.cc +++ b/code/components/tflite-lib/tensorflow/lite/micro/test_helpers.cc @@ -28,6 +28,7 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/tensor_ctypes.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/micro/all_ops_resolver.h" +#include "tensorflow/lite/micro/kernels/kernel_util.h" #include "tensorflow/lite/micro/micro_utils.h" #include "tensorflow/lite/schema/schema_generated.h" @@ -64,6 +65,8 @@ class StackAllocator : public flatbuffers::Allocator { uint8_t data_backing_[kStackAllocatorSize]; uint8_t* data_; int data_size_; + + TF_LITE_REMOVE_VIRTUAL_DELETE }; flatbuffers::FlatBufferBuilder* BuilderInstance() { @@ -261,8 +264,8 @@ const Model* BuildSimpleStatefulModel() { const int op_id = model_builder.RegisterOp(BuiltinOperator_CUSTOM, "simple_stateful_op"); - const int input_tensor = model_builder.AddTensor(TensorType_UINT8, {3}); - const int median_tensor = model_builder.AddTensor(TensorType_UINT8, {3}); + const int input_tensor = model_builder.AddTensor(TensorType_INT8, {3}); + const int median_tensor = model_builder.AddTensor(TensorType_INT8, {3}); const int invoke_count_tensor = model_builder.AddTensor(TensorType_INT32, {1}); @@ -407,6 +410,83 @@ const Model* BuildModelWithUnusedInputs() { return model; } +const Model* BuildModelWithUnusedOperatorOutputs() { + using flatbuffers::Offset; + flatbuffers::FlatBufferBuilder* builder = BuilderInstance(); + + constexpr size_t buffers_size = 1; + const Offset buffers[buffers_size] = {CreateBuffer(*builder)}; + constexpr size_t tensor_shape_size = 2; + const int32_t tensor_shape[tensor_shape_size] = {1, 64}; + constexpr size_t tensors_size = 2; + const Offset tensors[tensors_size] = { + CreateTensor(*builder, + builder->CreateVector(tensor_shape, tensor_shape_size), + TensorType_INT8, 0, + builder->CreateString("test_input_tensor"), 0, false), + CreateTensor( + *builder, builder->CreateVector(tensor_shape, tensor_shape_size), + TensorType_INT8, 0, + builder->CreateString("test_unused_output_tensor"), 0, false)}; + constexpr size_t inputs_size = 0; + const int32_t inputs[inputs_size] = {}; + constexpr size_t outputs_size = 1; + const int32_t outputs[outputs_size] = {0}; + constexpr size_t operator_inputs_size = 0; + const int32_t operator_inputs[operator_inputs_size] = {}; + constexpr size_t operator_outputs_size = 2; + const int32_t operator_outputs[operator_outputs_size] = {0, 1}; + constexpr size_t operators_size = 1; + const Offset operators[operators_size] = { + CreateOperator( + *builder, 0, + builder->CreateVector(operator_inputs, operator_inputs_size), + builder->CreateVector(operator_outputs, operator_outputs_size), + BuiltinOptions_NONE), + }; + constexpr size_t subgraphs_size = 1; + const Offset subgraphs[subgraphs_size] = { + CreateSubGraph(*builder, builder->CreateVector(tensors, tensors_size), + builder->CreateVector(inputs, inputs_size), + builder->CreateVector(outputs, outputs_size), + builder->CreateVector(operators, operators_size), + builder->CreateString("test_subgraph"))}; + constexpr size_t operator_codes_size = 1; + const Offset operator_codes[operator_codes_size] = { + CreateOperatorCodeDirect(*builder, /*deprecated_builtin_code=*/0, + "mock_custom", + /*version=*/0, BuiltinOperator_CUSTOM)}; + const Offset model_offset = CreateModel( + *builder, 0, builder->CreateVector(operator_codes, operator_codes_size), + builder->CreateVector(subgraphs, subgraphs_size), + builder->CreateString("test_model"), + builder->CreateVector(buffers, buffers_size)); + FinishModelBuffer(*builder, model_offset); + void* model_pointer = builder->GetBufferPointer(); + const Model* model = flatbuffers::GetRoot(model_pointer); + return model; +} + +const Model* BuildModelWith256x256Tensor() { + using flatbuffers::Offset; + flatbuffers::FlatBufferBuilder* fb_builder = BuilderInstance(); + + ModelBuilder model_builder(fb_builder); + + const int op_id = + model_builder.RegisterOp(BuiltinOperator_CUSTOM, "mock_custom"); + const int input1_tensor = + model_builder.AddTensor(TensorType_INT8, {256, 256}); + const int input2_tensor = + model_builder.AddTensor(TensorType_INT8, {256, 256}); + const int output_tensor = + model_builder.AddTensor(TensorType_INT8, {256, 256}); + + model_builder.AddNode(op_id, {input1_tensor, input2_tensor}, {output_tensor}); + return model_builder.BuildModel({input1_tensor, input2_tensor}, + {output_tensor}); +} + const Model* BuildSimpleMockModel() { using flatbuffers::Offset; flatbuffers::FlatBufferBuilder* builder = BuilderInstance(); @@ -428,7 +508,7 @@ const Model* BuildSimpleMockModel() { builder->CreateString("test_input_tensor"), 0, false), CreateTensor(*builder, builder->CreateVector(tensor_shape, tensor_shape_size), - TensorType_UINT8, 1, + TensorType_INT8, 1, builder->CreateString("test_weight_tensor"), 0, false), CreateTensor(*builder, builder->CreateVector(tensor_shape, tensor_shape_size), @@ -524,7 +604,7 @@ const Model* BuildComplexMockModel() { 0, true /* is_variable */), CreateTensor( *builder, builder->CreateVector(tensor_shape, tensor_shape_size), - TensorType_UINT8, 2, builder->CreateString("test_weight_tensor_1"), 0, + TensorType_INT8, 2, builder->CreateString("test_weight_tensor_1"), 0, false /* is_variable */), // Op 1 output / Op 2 input: CreateTensor( @@ -538,7 +618,7 @@ const Model* BuildComplexMockModel() { 0, true /* is_variable */), CreateTensor( *builder, builder->CreateVector(tensor_shape, tensor_shape_size), - TensorType_UINT8, 2, builder->CreateString("test_weight_tensor_2"), 0, + TensorType_INT8, 2, builder->CreateString("test_weight_tensor_2"), 0, false /* is_variable */), // Op 2 output / Op 3 input: CreateTensor( @@ -552,7 +632,7 @@ const Model* BuildComplexMockModel() { 0, true /* is_variable */), CreateTensor( *builder, builder->CreateVector(tensor_shape, tensor_shape_size), - TensorType_UINT8, 2, builder->CreateString("test_weight_tensor_3"), 0, + TensorType_INT8, 2, builder->CreateString("test_weight_tensor_3"), 0, false /* is_variable */), // Op 3 output: CreateTensor( @@ -823,6 +903,68 @@ const Model* BuildSimpleModelWithSubgraphsAndIf() { return model; } +// Mock model with one main subgraph containing a single CALL_ONCE op (with null +// inputs and outputs) which invokes a second subgraph which has null inputs and +// outputs. +const Model* BuildSimpleMockModelWithNullInputsOutputs() { + using flatbuffers::Offset; + flatbuffers::FlatBufferBuilder* builder = BuilderInstance(); + + constexpr size_t buffers_size = 1; + const Offset buffers[buffers_size] = { + CreateBuffer(*builder), + }; + constexpr size_t tensor_shape_size = 1; + const int32_t tensor_shape[tensor_shape_size] = {0}; + constexpr size_t tensors_size = 1; + const Offset tensors[tensors_size] = { + CreateTensor(*builder, + builder->CreateVector(tensor_shape, tensor_shape_size), + TensorType_INT32, 0, + builder->CreateString("test_input_tensor1"), 0, false), + }; + constexpr size_t subgraph0_inputs_size = 1; + const int32_t subgraph0_inputs[subgraph0_inputs_size] = {0}; + constexpr size_t subgraph0_outputs_size = 1; + const int32_t subgraph0_outputs[subgraph0_outputs_size] = {0}; + constexpr size_t operators_size = 1; + const Offset subgraph0_operators[operators_size] = { + CreateOperator(*builder, 0, {}, {}, BuiltinOptions_CallOnceOptions, + CreateCallOnceOptions(*builder, 1).Union()), + }; + const Offset subgraph1_operators[operators_size] = { + CreateOperator(*builder, 1, {}, {}, BuiltinOptions_NONE)}; + constexpr size_t subgraphs_size = 2; + const Offset subgraphs[subgraphs_size] = { + CreateSubGraph( + *builder, builder->CreateVector(tensors, tensors_size), + builder->CreateVector(subgraph0_inputs, subgraph0_inputs_size), + builder->CreateVector(subgraph0_outputs, subgraph0_outputs_size), + builder->CreateVector(subgraph0_operators, operators_size), + builder->CreateString("main_subgraph")), + CreateSubGraph(*builder, builder->CreateVector(tensors, tensors_size), {}, + {}, + builder->CreateVector(subgraph1_operators, operators_size), + builder->CreateString("secondary subgraph")), + }; + constexpr size_t operator_codes_size = 2; + const Offset operator_codes[operator_codes_size] = { + CreateOperatorCodeDirect(*builder, /*deprecated_builtin_code=*/0, + "call_once_op", + /*version=*/0, BuiltinOperator_CALL_ONCE), + CreateOperatorCodeDirect(*builder, /*deprecated_builtin_code=*/0, "no_op", + /*version=*/0, BuiltinOperator_CUSTOM)}; + const Offset model_offset = CreateModel( + *builder, 0, builder->CreateVector(operator_codes, operator_codes_size), + builder->CreateVector(subgraphs, subgraphs_size), + builder->CreateString("test_model"), + builder->CreateVector(buffers, buffers_size)); + FinishModelBuffer(*builder, model_offset); + void* model_pointer = builder->GetBufferPointer(); + const Model* model = flatbuffers::GetRoot(model_pointer); + return model; +} + } // namespace const TfLiteRegistration* SimpleStatefulOp::getRegistration() { @@ -856,7 +998,7 @@ TfLiteStatus SimpleStatefulOp::Prepare(TfLiteContext* context, // Make sure that the input is in uint8_t with at least 1 data entry. const TfLiteTensor* input; TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kInputTensor, &input)); - if (input->type != kTfLiteUInt8) return kTfLiteError; + if (input->type != kTfLiteInt8) return kTfLiteError; if (NumElements(input->dims) == 0) return kTfLiteError; // Allocate a temporary buffer with the same size of input for sorting. @@ -940,14 +1082,15 @@ TfLiteStatus MockCustom::Prepare(TfLiteContext* context, TfLiteNode* node) { } TfLiteStatus MockCustom::Invoke(TfLiteContext* context, TfLiteNode* node) { - const TfLiteTensor* input; - TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &input)); + const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0); + TF_LITE_ENSURE(context, input != nullptr); const int32_t* input_data = input->data.i32; - const TfLiteTensor* weight; - TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 1, &weight)); + const TfLiteEvalTensor* weight = + tflite::micro::GetEvalInput(context, node, 1); + TF_LITE_ENSURE(context, weight != nullptr); const uint8_t* weight_data = weight->data.uint8; - TfLiteTensor* output; - TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output)); + TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0); + TF_LITE_ENSURE(context, output != nullptr); int32_t* output_data = output->data.i32; output_data[0] = 0; // Catch output tensor sharing memory with an input tensor @@ -1010,6 +1153,40 @@ TfLiteStatus MultipleInputs::Invoke(TfLiteContext* context, TfLiteNode* node) { bool MultipleInputs::freed_ = false; +const TfLiteRegistration* NoOp::getRegistration() { + return GetMutableRegistration(); +} + +TfLiteRegistration* NoOp::GetMutableRegistration() { + static TfLiteRegistration r; + r.init = Init; + r.prepare = Prepare; + r.invoke = Invoke; + r.free = Free; + return &r; +} + +void* NoOp::Init(TfLiteContext* context, const char* buffer, size_t length) { + // We don't support delegate in TFL micro. This is a weak check to test if + // context struct being zero-initialized. + TFLITE_DCHECK(context->ReplaceNodeSubsetsWithDelegateKernels == nullptr); + freed_ = false; + // Do nothing. + return nullptr; +} + +void NoOp::Free(TfLiteContext* context, void* buffer) { freed_ = true; } + +TfLiteStatus NoOp::Prepare(TfLiteContext* context, TfLiteNode* node) { + return kTfLiteOk; +} + +TfLiteStatus NoOp::Invoke(TfLiteContext* context, TfLiteNode* node) { + return kTfLiteOk; +} + +bool NoOp::freed_ = false; + AllOpsResolver GetOpResolver() { AllOpsResolver op_resolver; op_resolver.AddCustom("mock_custom", MockCustom::GetMutableRegistration()); @@ -1017,8 +1194,10 @@ AllOpsResolver GetOpResolver() { SimpleStatefulOp::GetMutableRegistration()); op_resolver.AddCustom("multiple_inputs_op", MultipleInputs::GetMutableRegistration()); + op_resolver.AddCustom("no_op", NoOp::GetMutableRegistration()); return op_resolver; } + const Model* GetModelWithUnusedInputs() { static Model* model = nullptr; if (!model) { @@ -1027,6 +1206,19 @@ const Model* GetModelWithUnusedInputs() { return model; } +const Model* GetModelWithUnusedOperatorOutputs() { + static Model* model = nullptr; + if (!model) { + model = const_cast(BuildModelWithUnusedOperatorOutputs()); + } + return model; +} + +const Model* GetModelWith256x256Tensor() { + static const Model* model = BuildModelWith256x256Tensor(); + return model; +} + const Model* GetSimpleMockModel() { static Model* model = nullptr; if (!model) { @@ -1051,6 +1243,14 @@ const Model* GetSimpleModelWithSubgraphsAndIf() { return model; } +const Model* GetSimpleModelWithNullInputsAndOutputs() { + static Model* model = nullptr; + if (!model) { + model = const_cast(BuildSimpleMockModelWithNullInputsOutputs()); + } + return model; +} + const Model* GetComplexMockModel() { static Model* model = nullptr; if (!model) { @@ -1103,13 +1303,21 @@ const Tensor* Create1dFlatbufferTensor(int size, bool is_variable) { const Tensor* CreateQuantizedFlatbufferTensor(int size) { using flatbuffers::Offset; flatbuffers::FlatBufferBuilder* builder = BuilderInstance(); + constexpr size_t quant_params_size = 1; + const float min_array[quant_params_size] = {0.1f}; + const float max_array[quant_params_size] = {0.2f}; + const float scale_array[quant_params_size] = {0.3f}; + const int64_t zero_point_array[quant_params_size] = {100ll}; + const Offset quant_params = CreateQuantizationParameters( *builder, - /*min=*/builder->CreateVector({0.1f}), - /*max=*/builder->CreateVector({0.2f}), - /*scale=*/builder->CreateVector({0.3f}), - /*zero_point=*/builder->CreateVector({100ll})); + /*min=*/builder->CreateVector(min_array, quant_params_size), + /*max=*/builder->CreateVector(max_array, quant_params_size), + /*scale=*/ + builder->CreateVector(scale_array, quant_params_size), + /*zero_point=*/ + builder->CreateVector(zero_point_array, quant_params_size)); constexpr size_t tensor_shape_size = 1; const int32_t tensor_shape[tensor_shape_size] = {size}; diff --git a/code/components/tfmicro/tensorflow/lite/micro/test_helpers.h b/code/components/tflite-lib/tensorflow/lite/micro/test_helpers.h similarity index 91% rename from code/components/tfmicro/tensorflow/lite/micro/test_helpers.h rename to code/components/tflite-lib/tensorflow/lite/micro/test_helpers.h index a093e9e3..6ed7efd1 100644 --- a/code/components/tfmicro/tensorflow/lite/micro/test_helpers.h +++ b/code/components/tflite-lib/tensorflow/lite/micro/test_helpers.h @@ -88,6 +88,19 @@ class MultipleInputs { static bool freed_; }; +// A simple no-op operator. +class NoOp { + public: + static const TfLiteRegistration* getRegistration(); + static TfLiteRegistration* GetMutableRegistration(); + static void* Init(TfLiteContext* context, const char* buffer, size_t length); + static void Free(TfLiteContext* context, void* buffer); + static TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node); + static TfLiteStatus Invoke(TfLiteContext* context, TfLiteNode* node); + + static bool freed_; +}; + // Returns an Op Resolver that can be used in the testing code. AllOpsResolver GetOpResolver(); @@ -99,6 +112,12 @@ const Model* GetSimpleMockModel(); // tensors, and operators. const Model* GetComplexMockModel(); +// Returns a simple example flatbuffer TensorFlow Lite model. Contains 1 input, +// 1 layer of weights, 1 output Tensor, and 1 operator. +// The size of all three tensors is 256 x 256, which is larger than what other +// models provide from this test helper. +const Model* GetModelWith256x256Tensor(); + // Returns a simple flatbuffer model with two branches. const Model* GetSimpleModelWithBranch(); @@ -128,12 +147,19 @@ const Model* GetModelWithOfflinePlanning(int num_tensors, // output. const Model* GetModelWithUnusedInputs(); +// Returns a flatbuffer with a single operator, zero inputs and two outputs +// (one unused). +const Model* GetModelWithUnusedOperatorOutputs(); + // Returns a flatbuffer model with `simple_stateful_op` const Model* GetSimpleStatefulModel(); // Returns a flatbuffer model with "if" and two subgraphs. const Model* GetSimpleModelWithSubgraphsAndIf(); +// Returns a flatbuffer model with null subgraph/operator inputs and outputs. +const Model* GetSimpleModelWithNullInputsAndOutputs(); + // Builds a one-dimensional flatbuffer tensor of the given size. const Tensor* Create1dFlatbufferTensor(int size, bool is_variable = false); diff --git a/code/components/tfmicro/tensorflow/lite/portable_type_to_tflitetype.h b/code/components/tflite-lib/tensorflow/lite/portable_type_to_tflitetype.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/portable_type_to_tflitetype.h rename to code/components/tflite-lib/tensorflow/lite/portable_type_to_tflitetype.h diff --git a/code/components/tfmicro/tensorflow/lite/schema/schema_generated.h b/code/components/tflite-lib/tensorflow/lite/schema/schema_generated.h similarity index 97% rename from code/components/tfmicro/tensorflow/lite/schema/schema_generated.h rename to code/components/tflite-lib/tensorflow/lite/schema/schema_generated.h index 77253a4e..3620145d 100644 --- a/code/components/tfmicro/tensorflow/lite/schema/schema_generated.h +++ b/code/components/tflite-lib/tensorflow/lite/schema/schema_generated.h @@ -385,6 +385,15 @@ struct ReadVariableOptionsT; struct AssignVariableOptions; struct AssignVariableOptionsT; +struct RandomOptions; +struct RandomOptionsT; + +struct BucketizeOptions; +struct BucketizeOptionsT; + +struct GeluOptions; +struct GeluOptionsT; + struct OperatorCode; struct OperatorCodeT; @@ -854,11 +863,16 @@ enum BuiltinOperator { BuiltinOperator_READ_VARIABLE = 143, BuiltinOperator_ASSIGN_VARIABLE = 144, BuiltinOperator_BROADCAST_ARGS = 145, + BuiltinOperator_RANDOM_STANDARD_NORMAL = 146, + BuiltinOperator_BUCKETIZE = 147, + BuiltinOperator_RANDOM_UNIFORM = 148, + BuiltinOperator_MULTINOMIAL = 149, + BuiltinOperator_GELU = 150, BuiltinOperator_MIN = BuiltinOperator_ADD, - BuiltinOperator_MAX = BuiltinOperator_BROADCAST_ARGS + BuiltinOperator_MAX = BuiltinOperator_GELU }; -inline const BuiltinOperator (&EnumValuesBuiltinOperator())[146] { +inline const BuiltinOperator (&EnumValuesBuiltinOperator())[151] { static const BuiltinOperator values[] = { BuiltinOperator_ADD, BuiltinOperator_AVERAGE_POOL_2D, @@ -1005,13 +1019,18 @@ inline const BuiltinOperator (&EnumValuesBuiltinOperator())[146] { BuiltinOperator_VAR_HANDLE, BuiltinOperator_READ_VARIABLE, BuiltinOperator_ASSIGN_VARIABLE, - BuiltinOperator_BROADCAST_ARGS + BuiltinOperator_BROADCAST_ARGS, + BuiltinOperator_RANDOM_STANDARD_NORMAL, + BuiltinOperator_BUCKETIZE, + BuiltinOperator_RANDOM_UNIFORM, + BuiltinOperator_MULTINOMIAL, + BuiltinOperator_GELU }; return values; } inline const char * const *EnumNamesBuiltinOperator() { - static const char * const names[147] = { + static const char * const names[152] = { "ADD", "AVERAGE_POOL_2D", "CONCATENATION", @@ -1158,13 +1177,18 @@ inline const char * const *EnumNamesBuiltinOperator() { "READ_VARIABLE", "ASSIGN_VARIABLE", "BROADCAST_ARGS", + "RANDOM_STANDARD_NORMAL", + "BUCKETIZE", + "RANDOM_UNIFORM", + "MULTINOMIAL", + "GELU", nullptr }; return names; } inline const char *EnumNameBuiltinOperator(BuiltinOperator e) { - if (flatbuffers::IsOutRange(e, BuiltinOperator_ADD, BuiltinOperator_BROADCAST_ARGS)) return ""; + if (flatbuffers::IsOutRange(e, BuiltinOperator_ADD, BuiltinOperator_GELU)) return ""; const size_t index = static_cast(e); return EnumNamesBuiltinOperator()[index]; } @@ -1284,11 +1308,14 @@ enum BuiltinOptions { BuiltinOptions_VarHandleOptions = 111, BuiltinOptions_ReadVariableOptions = 112, BuiltinOptions_AssignVariableOptions = 113, + BuiltinOptions_RandomOptions = 114, + BuiltinOptions_BucketizeOptions = 115, + BuiltinOptions_GeluOptions = 116, BuiltinOptions_MIN = BuiltinOptions_NONE, - BuiltinOptions_MAX = BuiltinOptions_AssignVariableOptions + BuiltinOptions_MAX = BuiltinOptions_GeluOptions }; -inline const BuiltinOptions (&EnumValuesBuiltinOptions())[114] { +inline const BuiltinOptions (&EnumValuesBuiltinOptions())[117] { static const BuiltinOptions values[] = { BuiltinOptions_NONE, BuiltinOptions_Conv2DOptions, @@ -1403,13 +1430,16 @@ inline const BuiltinOptions (&EnumValuesBuiltinOptions())[114] { BuiltinOptions_HashtableSizeOptions, BuiltinOptions_VarHandleOptions, BuiltinOptions_ReadVariableOptions, - BuiltinOptions_AssignVariableOptions + BuiltinOptions_AssignVariableOptions, + BuiltinOptions_RandomOptions, + BuiltinOptions_BucketizeOptions, + BuiltinOptions_GeluOptions }; return values; } inline const char * const *EnumNamesBuiltinOptions() { - static const char * const names[115] = { + static const char * const names[118] = { "NONE", "Conv2DOptions", "DepthwiseConv2DOptions", @@ -1524,13 +1554,16 @@ inline const char * const *EnumNamesBuiltinOptions() { "VarHandleOptions", "ReadVariableOptions", "AssignVariableOptions", + "RandomOptions", + "BucketizeOptions", + "GeluOptions", nullptr }; return names; } inline const char *EnumNameBuiltinOptions(BuiltinOptions e) { - if (flatbuffers::IsOutRange(e, BuiltinOptions_NONE, BuiltinOptions_AssignVariableOptions)) return ""; + if (flatbuffers::IsOutRange(e, BuiltinOptions_NONE, BuiltinOptions_GeluOptions)) return ""; const size_t index = static_cast(e); return EnumNamesBuiltinOptions()[index]; } @@ -1991,6 +2024,18 @@ template<> struct BuiltinOptionsTraits { static const BuiltinOptions enum_value = BuiltinOptions_AssignVariableOptions; }; +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_RandomOptions; +}; + +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_BucketizeOptions; +}; + +template<> struct BuiltinOptionsTraits { + static const BuiltinOptions enum_value = BuiltinOptions_GeluOptions; +}; + struct BuiltinOptionsUnion { BuiltinOptions type; void *value; @@ -2927,6 +2972,30 @@ struct BuiltinOptionsUnion { return type == BuiltinOptions_AssignVariableOptions ? reinterpret_cast(value) : nullptr; } + tflite::RandomOptionsT *AsRandomOptions() { + return type == BuiltinOptions_RandomOptions ? + reinterpret_cast(value) : nullptr; + } + const tflite::RandomOptionsT *AsRandomOptions() const { + return type == BuiltinOptions_RandomOptions ? + reinterpret_cast(value) : nullptr; + } + tflite::BucketizeOptionsT *AsBucketizeOptions() { + return type == BuiltinOptions_BucketizeOptions ? + reinterpret_cast(value) : nullptr; + } + const tflite::BucketizeOptionsT *AsBucketizeOptions() const { + return type == BuiltinOptions_BucketizeOptions ? + reinterpret_cast(value) : nullptr; + } + tflite::GeluOptionsT *AsGeluOptions() { + return type == BuiltinOptions_GeluOptions ? + reinterpret_cast(value) : nullptr; + } + const tflite::GeluOptionsT *AsGeluOptions() const { + return type == BuiltinOptions_GeluOptions ? + reinterpret_cast(value) : nullptr; + } }; bool VerifyBuiltinOptions(flatbuffers::Verifier &verifier, const void *obj, BuiltinOptions type); @@ -10343,6 +10412,189 @@ inline flatbuffers::Offset CreateAssignVariableOptions( flatbuffers::Offset CreateAssignVariableOptions(flatbuffers::FlatBufferBuilder &_fbb, const AssignVariableOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct RandomOptionsT : public flatbuffers::NativeTable { + typedef RandomOptions TableType; + int64_t seed; + int64_t seed2; + RandomOptionsT() + : seed(0), + seed2(0) { + } +}; + +struct RandomOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef RandomOptionsT NativeTableType; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SEED = 4, + VT_SEED2 = 6 + }; + int64_t seed() const { + return GetField(VT_SEED, 0); + } + int64_t seed2() const { + return GetField(VT_SEED2, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SEED) && + VerifyField(verifier, VT_SEED2) && + verifier.EndTable(); + } + RandomOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(RandomOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const RandomOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct RandomOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_seed(int64_t seed) { + fbb_.AddElement(RandomOptions::VT_SEED, seed, 0); + } + void add_seed2(int64_t seed2) { + fbb_.AddElement(RandomOptions::VT_SEED2, seed2, 0); + } + explicit RandomOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RandomOptionsBuilder &operator=(const RandomOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRandomOptions( + flatbuffers::FlatBufferBuilder &_fbb, + int64_t seed = 0, + int64_t seed2 = 0) { + RandomOptionsBuilder builder_(_fbb); + builder_.add_seed2(seed2); + builder_.add_seed(seed); + return builder_.Finish(); +} + +flatbuffers::Offset CreateRandomOptions(flatbuffers::FlatBufferBuilder &_fbb, const RandomOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BucketizeOptionsT : public flatbuffers::NativeTable { + typedef BucketizeOptions TableType; + std::vector boundaries; + BucketizeOptionsT() { + } +}; + +struct BucketizeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef BucketizeOptionsT NativeTableType; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_BOUNDARIES = 4 + }; + const flatbuffers::Vector *boundaries() const { + return GetPointer *>(VT_BOUNDARIES); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_BOUNDARIES) && + verifier.VerifyVector(boundaries()) && + verifier.EndTable(); + } + BucketizeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BucketizeOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const BucketizeOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BucketizeOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_boundaries(flatbuffers::Offset> boundaries) { + fbb_.AddOffset(BucketizeOptions::VT_BOUNDARIES, boundaries); + } + explicit BucketizeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + BucketizeOptionsBuilder &operator=(const BucketizeOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateBucketizeOptions( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> boundaries = 0) { + BucketizeOptionsBuilder builder_(_fbb); + builder_.add_boundaries(boundaries); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateBucketizeOptionsDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *boundaries = nullptr) { + auto boundaries__ = boundaries ? _fbb.CreateVector(*boundaries) : 0; + return tflite::CreateBucketizeOptions( + _fbb, + boundaries__); +} + +flatbuffers::Offset CreateBucketizeOptions(flatbuffers::FlatBufferBuilder &_fbb, const BucketizeOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct GeluOptionsT : public flatbuffers::NativeTable { + typedef GeluOptions TableType; + bool approximate; + GeluOptionsT() + : approximate(false) { + } +}; + +struct GeluOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef GeluOptionsT NativeTableType; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_APPROXIMATE = 4 + }; + bool approximate() const { + return GetField(VT_APPROXIMATE, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_APPROXIMATE) && + verifier.EndTable(); + } + GeluOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(GeluOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const GeluOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct GeluOptionsBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_approximate(bool approximate) { + fbb_.AddElement(GeluOptions::VT_APPROXIMATE, static_cast(approximate), 0); + } + explicit GeluOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + GeluOptionsBuilder &operator=(const GeluOptionsBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateGeluOptions( + flatbuffers::FlatBufferBuilder &_fbb, + bool approximate = false) { + GeluOptionsBuilder builder_(_fbb); + builder_.add_approximate(approximate); + return builder_.Finish(); +} + +flatbuffers::Offset CreateGeluOptions(flatbuffers::FlatBufferBuilder &_fbb, const GeluOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct OperatorCodeT : public flatbuffers::NativeTable { typedef OperatorCode TableType; int8_t deprecated_builtin_code; @@ -10832,6 +11084,15 @@ struct Operator FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const tflite::AssignVariableOptions *builtin_options_as_AssignVariableOptions() const { return builtin_options_type() == tflite::BuiltinOptions_AssignVariableOptions ? static_cast(builtin_options()) : nullptr; } + const tflite::RandomOptions *builtin_options_as_RandomOptions() const { + return builtin_options_type() == tflite::BuiltinOptions_RandomOptions ? static_cast(builtin_options()) : nullptr; + } + const tflite::BucketizeOptions *builtin_options_as_BucketizeOptions() const { + return builtin_options_type() == tflite::BuiltinOptions_BucketizeOptions ? static_cast(builtin_options()) : nullptr; + } + const tflite::GeluOptions *builtin_options_as_GeluOptions() const { + return builtin_options_type() == tflite::BuiltinOptions_GeluOptions ? static_cast(builtin_options()) : nullptr; + } const flatbuffers::Vector *custom_options() const { return GetPointer *>(VT_CUSTOM_OPTIONS); } @@ -11320,6 +11581,18 @@ template<> inline const tflite::AssignVariableOptions *Operator::builtin_options return builtin_options_as_AssignVariableOptions(); } +template<> inline const tflite::RandomOptions *Operator::builtin_options_as() const { + return builtin_options_as_RandomOptions(); +} + +template<> inline const tflite::BucketizeOptions *Operator::builtin_options_as() const { + return builtin_options_as_BucketizeOptions(); +} + +template<> inline const tflite::GeluOptions *Operator::builtin_options_as() const { + return builtin_options_as_GeluOptions(); +} + struct OperatorBuilder { flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; @@ -15325,6 +15598,87 @@ inline flatbuffers::Offset CreateAssignVariableOptions(fl _fbb); } +inline RandomOptionsT *RandomOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new RandomOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void RandomOptions::UnPackTo(RandomOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = seed(); _o->seed = _e; } + { auto _e = seed2(); _o->seed2 = _e; } +} + +inline flatbuffers::Offset RandomOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const RandomOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateRandomOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateRandomOptions(flatbuffers::FlatBufferBuilder &_fbb, const RandomOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const RandomOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _seed = _o->seed; + auto _seed2 = _o->seed2; + return tflite::CreateRandomOptions( + _fbb, + _seed, + _seed2); +} + +inline BucketizeOptionsT *BucketizeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new BucketizeOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void BucketizeOptions::UnPackTo(BucketizeOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = boundaries(); if (_e) { _o->boundaries.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->boundaries[_i] = _e->Get(_i); } } } +} + +inline flatbuffers::Offset BucketizeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BucketizeOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateBucketizeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateBucketizeOptions(flatbuffers::FlatBufferBuilder &_fbb, const BucketizeOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const BucketizeOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _boundaries = _o->boundaries.size() ? _fbb.CreateVector(_o->boundaries) : 0; + return tflite::CreateBucketizeOptions( + _fbb, + _boundaries); +} + +inline GeluOptionsT *GeluOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new GeluOptionsT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void GeluOptions::UnPackTo(GeluOptionsT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = approximate(); _o->approximate = _e; } +} + +inline flatbuffers::Offset GeluOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const GeluOptionsT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateGeluOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateGeluOptions(flatbuffers::FlatBufferBuilder &_fbb, const GeluOptionsT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const GeluOptionsT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _approximate = _o->approximate; + return tflite::CreateGeluOptions( + _fbb, + _approximate); +} + inline OperatorCodeT *OperatorCode::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = new OperatorCodeT(); UnPackTo(_o, _resolver); @@ -16252,6 +16606,18 @@ inline bool VerifyBuiltinOptions(flatbuffers::Verifier &verifier, const void *ob auto ptr = reinterpret_cast(obj); return verifier.VerifyTable(ptr); } + case BuiltinOptions_RandomOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_BucketizeOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_GeluOptions: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } default: return true; } } @@ -16722,6 +17088,18 @@ inline void *BuiltinOptionsUnion::UnPack(const void *obj, BuiltinOptions type, c auto ptr = reinterpret_cast(obj); return ptr->UnPack(resolver); } + case BuiltinOptions_RandomOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_BucketizeOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_GeluOptions: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } default: return nullptr; } } @@ -17180,6 +17558,18 @@ inline flatbuffers::Offset BuiltinOptionsUnion::Pack(flatbuffers::FlatBuff auto ptr = reinterpret_cast(value); return CreateAssignVariableOptions(_fbb, ptr, _rehasher).Union(); } + case BuiltinOptions_RandomOptions: { + auto ptr = reinterpret_cast(value); + return CreateRandomOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_BucketizeOptions: { + auto ptr = reinterpret_cast(value); + return CreateBucketizeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_GeluOptions: { + auto ptr = reinterpret_cast(value); + return CreateGeluOptions(_fbb, ptr, _rehasher).Union(); + } default: return 0; } } @@ -17638,6 +18028,18 @@ inline BuiltinOptionsUnion::BuiltinOptionsUnion(const BuiltinOptionsUnion &u) FL value = new tflite::AssignVariableOptionsT(*reinterpret_cast(u.value)); break; } + case BuiltinOptions_RandomOptions: { + value = new tflite::RandomOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_BucketizeOptions: { + value = new tflite::BucketizeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_GeluOptions: { + value = new tflite::GeluOptionsT(*reinterpret_cast(u.value)); + break; + } default: break; } @@ -18210,6 +18612,21 @@ inline void BuiltinOptionsUnion::Reset() { delete ptr; break; } + case BuiltinOptions_RandomOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_BucketizeOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_GeluOptions: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } default: break; } value = nullptr; diff --git a/code/components/tfmicro/tensorflow/lite/schema/schema_utils.cc b/code/components/tflite-lib/tensorflow/lite/schema/schema_utils.cc similarity index 100% rename from code/components/tfmicro/tensorflow/lite/schema/schema_utils.cc rename to code/components/tflite-lib/tensorflow/lite/schema/schema_utils.cc diff --git a/code/components/tfmicro/tensorflow/lite/schema/schema_utils.h b/code/components/tflite-lib/tensorflow/lite/schema/schema_utils.h similarity index 100% rename from code/components/tfmicro/tensorflow/lite/schema/schema_utils.h rename to code/components/tflite-lib/tensorflow/lite/schema/schema_utils.h diff --git a/code/components/tfmicro/third_party/flatbuffers/LICENSE.txt b/code/components/tflite-lib/third_party/flatbuffers/LICENSE.txt similarity index 100% rename from code/components/tfmicro/third_party/flatbuffers/LICENSE.txt rename to code/components/tflite-lib/third_party/flatbuffers/LICENSE.txt diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/allocator.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/allocator.h new file mode 100644 index 00000000..f4ef22db --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/allocator.h @@ -0,0 +1,68 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_ALLOCATOR_H_ +#define FLATBUFFERS_ALLOCATOR_H_ + +#include "flatbuffers/base.h" + +namespace flatbuffers { + +// Allocator interface. This is flatbuffers-specific and meant only for +// `vector_downward` usage. +class Allocator { + public: + virtual ~Allocator() {} + + // Allocate `size` bytes of memory. + virtual uint8_t *allocate(size_t size) = 0; + + // Deallocate `size` bytes of memory at `p` allocated by this allocator. + virtual void deallocate(uint8_t *p, size_t size) = 0; + + // Reallocate `new_size` bytes of memory, replacing the old region of size + // `old_size` at `p`. In contrast to a normal realloc, this grows downwards, + // and is intended specifcally for `vector_downward` use. + // `in_use_back` and `in_use_front` indicate how much of `old_size` is + // actually in use at each end, and needs to be copied. + virtual uint8_t *reallocate_downward(uint8_t *old_p, size_t old_size, + size_t new_size, size_t in_use_back, + size_t in_use_front) { + FLATBUFFERS_ASSERT(new_size > old_size); // vector_downward only grows + uint8_t *new_p = allocate(new_size); + memcpy_downward(old_p, old_size, new_p, new_size, in_use_back, + in_use_front); + deallocate(old_p, old_size); + return new_p; + } + + protected: + // Called by `reallocate_downward` to copy memory from `old_p` of `old_size` + // to `new_p` of `new_size`. Only memory of size `in_use_front` and + // `in_use_back` will be copied from the front and back of the old memory + // allocation. + void memcpy_downward(uint8_t *old_p, size_t old_size, uint8_t *new_p, + size_t new_size, size_t in_use_back, + size_t in_use_front) { + memcpy(new_p + new_size - in_use_back, old_p + old_size - in_use_back, + in_use_back); + memcpy(new_p, old_p, in_use_front); + } +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_ALLOCATOR_H_ \ No newline at end of file diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/array.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/array.h new file mode 100644 index 00000000..d4b73fc9 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/array.h @@ -0,0 +1,243 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_ARRAY_H_ +#define FLATBUFFERS_ARRAY_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/stl_emulation.h" +#include "flatbuffers/vector.h" + +namespace flatbuffers { + +// This is used as a helper type for accessing arrays. +template class Array { + // Array can carry only POD data types (scalars or structs). + typedef typename flatbuffers::bool_constant::value> + scalar_tag; + typedef + typename flatbuffers::conditional::type + IndirectHelperType; + + public: + typedef uint16_t size_type; + typedef typename IndirectHelper::return_type return_type; + typedef VectorIterator const_iterator; + typedef VectorReverseIterator const_reverse_iterator; + + // If T is a LE-scalar or a struct (!scalar_tag::value). + static FLATBUFFERS_CONSTEXPR bool is_span_observable = + (scalar_tag::value && (FLATBUFFERS_LITTLEENDIAN || sizeof(T) == 1)) || + !scalar_tag::value; + + FLATBUFFERS_CONSTEXPR uint16_t size() const { return length; } + + return_type Get(uoffset_t i) const { + FLATBUFFERS_ASSERT(i < size()); + return IndirectHelper::Read(Data(), i); + } + + return_type operator[](uoffset_t i) const { return Get(i); } + + // If this is a Vector of enums, T will be its storage type, not the enum + // type. This function makes it convenient to retrieve value with enum + // type E. + template E GetEnum(uoffset_t i) const { + return static_cast(Get(i)); + } + + const_iterator begin() const { return const_iterator(Data(), 0); } + const_iterator end() const { return const_iterator(Data(), size()); } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + const_reverse_iterator crbegin() const { return rbegin(); } + const_reverse_iterator crend() const { return rend(); } + + // Get a mutable pointer to elements inside this array. + // This method used to mutate arrays of structs followed by a @p Mutate + // operation. For primitive types use @p Mutate directly. + // @warning Assignments and reads to/from the dereferenced pointer are not + // automatically converted to the correct endianness. + typename flatbuffers::conditional::type + GetMutablePointer(uoffset_t i) const { + FLATBUFFERS_ASSERT(i < size()); + return const_cast(&data()[i]); + } + + // Change elements if you have a non-const pointer to this object. + void Mutate(uoffset_t i, const T &val) { MutateImpl(scalar_tag(), i, val); } + + // The raw data in little endian format. Use with care. + const uint8_t *Data() const { return data_; } + + uint8_t *Data() { return data_; } + + // Similarly, but typed, much like std::vector::data + const T *data() const { return reinterpret_cast(Data()); } + T *data() { return reinterpret_cast(Data()); } + + // Copy data from a span with endian conversion. + // If this Array and the span overlap, the behavior is undefined. + void CopyFromSpan(flatbuffers::span src) { + const auto p1 = reinterpret_cast(src.data()); + const auto p2 = Data(); + FLATBUFFERS_ASSERT(!(p1 >= p2 && p1 < (p2 + length)) && + !(p2 >= p1 && p2 < (p1 + length))); + (void)p1; + (void)p2; + CopyFromSpanImpl(flatbuffers::bool_constant(), src); + } + + protected: + void MutateImpl(flatbuffers::true_type, uoffset_t i, const T &val) { + FLATBUFFERS_ASSERT(i < size()); + WriteScalar(data() + i, val); + } + + void MutateImpl(flatbuffers::false_type, uoffset_t i, const T &val) { + *(GetMutablePointer(i)) = val; + } + + void CopyFromSpanImpl(flatbuffers::true_type, + flatbuffers::span src) { + // Use std::memcpy() instead of std::copy() to avoid performance degradation + // due to aliasing if T is char or unsigned char. + // The size is known at compile time, so memcpy would be inlined. + std::memcpy(data(), src.data(), length * sizeof(T)); + } + + // Copy data from flatbuffers::span with endian conversion. + void CopyFromSpanImpl(flatbuffers::false_type, + flatbuffers::span src) { + for (size_type k = 0; k < length; k++) { Mutate(k, src[k]); } + } + + // This class is only used to access pre-existing data. Don't ever + // try to construct these manually. + // 'constexpr' allows us to use 'size()' at compile time. + // @note Must not use 'FLATBUFFERS_CONSTEXPR' here, as const is not allowed on + // a constructor. +#if defined(__cpp_constexpr) + constexpr Array(); +#else + Array(); +#endif + + uint8_t data_[length * sizeof(T)]; + + private: + // This class is a pointer. Copying will therefore create an invalid object. + // Private and unimplemented copy constructor. + Array(const Array &); + Array &operator=(const Array &); +}; + +// Specialization for Array[struct] with access using Offset pointer. +// This specialization used by idl_gen_text.cpp. +template class Array, length> { + static_assert(flatbuffers::is_same::value, "unexpected type T"); + + public: + typedef const void *return_type; + + const uint8_t *Data() const { return data_; } + + // Make idl_gen_text.cpp::PrintContainer happy. + return_type operator[](uoffset_t) const { + FLATBUFFERS_ASSERT(false); + return nullptr; + } + + private: + // This class is only used to access pre-existing data. + Array(); + Array(const Array &); + Array &operator=(const Array &); + + uint8_t data_[1]; +}; + +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_span(Array &arr) + FLATBUFFERS_NOEXCEPT { + static_assert( + Array::is_span_observable, + "wrong type U, only plain struct, LE-scalar, or byte types are allowed"); + return span(arr.data(), N); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_span( + const Array &arr) FLATBUFFERS_NOEXCEPT { + static_assert( + Array::is_span_observable, + "wrong type U, only plain struct, LE-scalar, or byte types are allowed"); + return span(arr.data(), N); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span +make_bytes_span(Array &arr) FLATBUFFERS_NOEXCEPT { + static_assert(Array::is_span_observable, + "internal error, Array might hold only scalars or structs"); + return span(arr.Data(), sizeof(U) * N); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span +make_bytes_span(const Array &arr) FLATBUFFERS_NOEXCEPT { + static_assert(Array::is_span_observable, + "internal error, Array might hold only scalars or structs"); + return span(arr.Data(), sizeof(U) * N); +} + +// Cast a raw T[length] to a raw flatbuffers::Array +// without endian conversion. Use with care. +// TODO: move these Cast-methods to `internal` namespace. +template +Array &CastToArray(T (&arr)[length]) { + return *reinterpret_cast *>(arr); +} + +template +const Array &CastToArray(const T (&arr)[length]) { + return *reinterpret_cast *>(arr); +} + +template +Array &CastToArrayOfEnum(T (&arr)[length]) { + static_assert(sizeof(E) == sizeof(T), "invalid enum type E"); + return *reinterpret_cast *>(arr); +} + +template +const Array &CastToArrayOfEnum(const T (&arr)[length]) { + static_assert(sizeof(E) == sizeof(T), "invalid enum type E"); + return *reinterpret_cast *>(arr); +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_ARRAY_H_ diff --git a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/base.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/base.h similarity index 86% rename from code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/base.h rename to code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/base.h index 6f3fea52..371b6fdb 100644 --- a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/base.h +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/base.h @@ -60,10 +60,6 @@ #include #endif -#ifdef _STLPORT_VERSION - #define FLATBUFFERS_CPP98_STL -#endif - #ifdef __ANDROID__ #include #endif @@ -152,9 +148,9 @@ #endif #endif // !defined(FLATBUFFERS_LITTLEENDIAN) -#define FLATBUFFERS_VERSION_MAJOR 1 -#define FLATBUFFERS_VERSION_MINOR 12 -#define FLATBUFFERS_VERSION_REVISION 0 +#define FLATBUFFERS_VERSION_MAJOR 2 +#define FLATBUFFERS_VERSION_MINOR 0 +#define FLATBUFFERS_VERSION_REVISION 5 #define FLATBUFFERS_STRING_EXPAND(X) #X #define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X) namespace flatbuffers { @@ -187,10 +183,9 @@ namespace flatbuffers { #define FLATBUFFERS_CONSTEXPR_CPP11 #endif -// This macro is never used in code! #if (defined(__cplusplus) && __cplusplus >= 201402L) || \ (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) - #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR + #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR_CPP11 #else #define FLATBUFFERS_CONSTEXPR_CPP14 #endif @@ -208,9 +203,15 @@ namespace flatbuffers { #if (!defined(_MSC_VER) || _MSC_FULL_VER >= 180020827) && \ (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)) || \ defined(__clang__) - #define FLATBUFFERS_DELETE_FUNC(func) func = delete; + #define FLATBUFFERS_DELETE_FUNC(func) func = delete #else - #define FLATBUFFERS_DELETE_FUNC(func) private: func; + #define FLATBUFFERS_DELETE_FUNC(func) private: func +#endif + +#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \ + defined(__clang__) + #define FLATBUFFERS_DEFAULT_DECLARATION #endif // Check if we can use template aliases @@ -252,6 +253,11 @@ namespace flatbuffers { #endif // __has_include #endif // !FLATBUFFERS_HAS_STRING_VIEW +#ifndef FLATBUFFERS_GENERAL_HEAP_ALLOC_OK + // Allow heap allocations to be used + #define FLATBUFFERS_GENERAL_HEAP_ALLOC_OK 1 +#endif // !FLATBUFFERS_GENERAL_HEAP_ALLOC_OK + #ifndef FLATBUFFERS_HAS_NEW_STRTOD // Modern (C++11) strtod and strtof functions are available for use. // 1) nan/inf strings as argument of strtod; @@ -294,7 +300,7 @@ template FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) { #if ((__cplusplus >= 201703L) \ || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L))) // All attributes unknown to an implementation are ignored without causing an error. - #define FLATBUFFERS_ATTRIBUTE(attr) [[attr]] + #define FLATBUFFERS_ATTRIBUTE(attr) attr #define FLATBUFFERS_FALLTHROUGH() [[fallthrough]] #else @@ -335,6 +341,14 @@ typedef uintmax_t largest_scalar_t; // We support aligning the contents of buffers up to this size. #define FLATBUFFERS_MAX_ALIGNMENT 16 +/// @brief The length of a FlatBuffer file header. +static const size_t kFileIdentifierLength = 4; + +inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) { + return (min_align <= align) && (align <= (FLATBUFFERS_MAX_ALIGNMENT)) && + (align & (align - 1)) == 0; // must be power of 2 +} + #if defined(_MSC_VER) #pragma warning(disable: 4351) // C4351: new behavior: elements of array ... will be default initialized #pragma warning(push) @@ -433,5 +447,38 @@ inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) { return ((~buf_size) + 1) & (scalar_size - 1); } +// Generic 'operator==' with conditional specialisations. +// T e - new value of a scalar field. +// T def - default of scalar (is known at compile-time). +template inline bool IsTheSameAs(T e, T def) { return e == def; } + +#if defined(FLATBUFFERS_NAN_DEFAULTS) && \ + defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) +// Like `operator==(e, def)` with weak NaN if T=(float|double). +template inline bool IsFloatTheSameAs(T e, T def) { + return (e == def) || ((def != def) && (e != e)); +} +template<> inline bool IsTheSameAs(float e, float def) { + return IsFloatTheSameAs(e, def); +} +template<> inline bool IsTheSameAs(double e, double def) { + return IsFloatTheSameAs(e, def); +} +#endif + +// Check 'v' is out of closed range [low; high]. +// Workaround for GCC warning [-Werror=type-limits]: +// comparison is always true due to limited range of data type. +template +inline bool IsOutRange(const T &v, const T &low, const T &high) { + return (v < low) || (high < v); +} + +// Check 'v' is in closed range [low; high]. +template +inline bool IsInRange(const T &v, const T &low, const T &high) { + return !IsOutRange(v, low, high); +} + } // namespace flatbuffers #endif // FLATBUFFERS_BASE_H_ diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/buffer.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/buffer.h new file mode 100644 index 00000000..e8d2ce9c --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/buffer.h @@ -0,0 +1,142 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_BUFFER_H_ +#define FLATBUFFERS_BUFFER_H_ + +#include "flatbuffers/base.h" + +namespace flatbuffers { + +// Wrapper for uoffset_t to allow safe template specialization. +// Value is allowed to be 0 to indicate a null object (see e.g. AddOffset). +template struct Offset { + uoffset_t o; + Offset() : o(0) {} + Offset(uoffset_t _o) : o(_o) {} + Offset Union() const { return Offset(o); } + bool IsNull() const { return !o; } +}; + +inline void EndianCheck() { + int endiantest = 1; + // If this fails, see FLATBUFFERS_LITTLEENDIAN above. + FLATBUFFERS_ASSERT(*reinterpret_cast(&endiantest) == + FLATBUFFERS_LITTLEENDIAN); + (void)endiantest; +} + +template FLATBUFFERS_CONSTEXPR size_t AlignOf() { + // clang-format off + #ifdef _MSC_VER + return __alignof(T); + #else + #ifndef alignof + return __alignof__(T); + #else + return alignof(T); + #endif + #endif + // clang-format on +} + +// Lexicographically compare two strings (possibly containing nulls), and +// return true if the first is less than the second. +static inline bool StringLessThan(const char *a_data, uoffset_t a_size, + const char *b_data, uoffset_t b_size) { + const auto cmp = memcmp(a_data, b_data, (std::min)(a_size, b_size)); + return cmp == 0 ? a_size < b_size : cmp < 0; +} + +// When we read serialized data from memory, in the case of most scalars, +// we want to just read T, but in the case of Offset, we want to actually +// perform the indirection and return a pointer. +// The template specialization below does just that. +// It is wrapped in a struct since function templates can't overload on the +// return type like this. +// The typedef is for the convenience of callers of this function +// (avoiding the need for a trailing return decltype) +template struct IndirectHelper { + typedef T return_type; + typedef T mutable_return_type; + static const size_t element_stride = sizeof(T); + static return_type Read(const uint8_t *p, uoffset_t i) { + return EndianScalar((reinterpret_cast(p))[i]); + } +}; +template struct IndirectHelper> { + typedef const T *return_type; + typedef T *mutable_return_type; + static const size_t element_stride = sizeof(uoffset_t); + static return_type Read(const uint8_t *p, uoffset_t i) { + p += i * sizeof(uoffset_t); + return reinterpret_cast(p + ReadScalar(p)); + } +}; +template struct IndirectHelper { + typedef const T *return_type; + typedef T *mutable_return_type; + static const size_t element_stride = sizeof(T); + static return_type Read(const uint8_t *p, uoffset_t i) { + return reinterpret_cast(p + i * sizeof(T)); + } +}; + +/// @brief Get a pointer to the the file_identifier section of the buffer. +/// @return Returns a const char pointer to the start of the file_identifier +/// characters in the buffer. The returned char * has length +/// 'flatbuffers::FlatBufferBuilder::kFileIdentifierLength'. +/// This function is UNDEFINED for FlatBuffers whose schema does not include +/// a file_identifier (likely points at padding or the start of a the root +/// vtable). +inline const char *GetBufferIdentifier(const void *buf, + bool size_prefixed = false) { + return reinterpret_cast(buf) + + ((size_prefixed) ? 2 * sizeof(uoffset_t) : sizeof(uoffset_t)); +} + +// Helper to see if the identifier in a buffer has the expected value. +inline bool BufferHasIdentifier(const void *buf, const char *identifier, + bool size_prefixed = false) { + return strncmp(GetBufferIdentifier(buf, size_prefixed), identifier, + flatbuffers::kFileIdentifierLength) == 0; +} + +/// @cond FLATBUFFERS_INTERNAL +// Helpers to get a typed pointer to the root object contained in the buffer. +template T *GetMutableRoot(void *buf) { + EndianCheck(); + return reinterpret_cast( + reinterpret_cast(buf) + + EndianScalar(*reinterpret_cast(buf))); +} + +template T *GetMutableSizePrefixedRoot(void *buf) { + return GetMutableRoot(reinterpret_cast(buf) + + sizeof(uoffset_t)); +} + +template const T *GetRoot(const void *buf) { + return GetMutableRoot(const_cast(buf)); +} + +template const T *GetSizePrefixedRoot(const void *buf) { + return GetRoot(reinterpret_cast(buf) + sizeof(uoffset_t)); +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_BUFFER_H_ \ No newline at end of file diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/buffer_ref.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/buffer_ref.h new file mode 100644 index 00000000..ce302073 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/buffer_ref.h @@ -0,0 +1,53 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_BUFFER_REF_H_ +#define FLATBUFFERS_BUFFER_REF_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/verifier.h" + +namespace flatbuffers { + +// Convenient way to bundle a buffer and its length, to pass it around +// typed by its root. +// A BufferRef does not own its buffer. +struct BufferRefBase {}; // for std::is_base_of + +template struct BufferRef : BufferRefBase { + BufferRef() : buf(nullptr), len(0), must_free(false) {} + BufferRef(uint8_t *_buf, uoffset_t _len) + : buf(_buf), len(_len), must_free(false) {} + + ~BufferRef() { + if (must_free) free(buf); + } + + const T *GetRoot() const { return flatbuffers::GetRoot(buf); } + + bool Verify() { + Verifier verifier(buf, len); + return verifier.VerifyBuffer(nullptr); + } + + uint8_t *buf; + uoffset_t len; + bool must_free; +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_BUFFER_REF_H_ \ No newline at end of file diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/default_allocator.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/default_allocator.h new file mode 100644 index 00000000..975d9380 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/default_allocator.h @@ -0,0 +1,58 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_DEFAULT_ALLOCATOR_H_ +#define FLATBUFFERS_DEFAULT_ALLOCATOR_H_ + +#include "flatbuffers/allocator.h" +#include "flatbuffers/base.h" + +namespace flatbuffers { + +// DefaultAllocator uses new/delete to allocate memory regions +class DefaultAllocator : public Allocator { + public: + uint8_t *allocate(size_t size) FLATBUFFERS_OVERRIDE { + return new uint8_t[size]; + } + + void deallocate(uint8_t *p, size_t) FLATBUFFERS_OVERRIDE { delete[] p; } + + static void dealloc(void *p, size_t) { delete[] static_cast(p); } +}; + +// These functions allow for a null allocator to mean use the default allocator, +// as used by DetachedBuffer and vector_downward below. +// This is to avoid having a statically or dynamically allocated default +// allocator, or having to move it between the classes that may own it. +inline uint8_t *Allocate(Allocator *allocator, size_t size) { + return allocator->allocate(size); +} + +inline void Deallocate(Allocator *allocator, uint8_t *p, size_t size) { + allocator->deallocate(p, size); +} + +inline uint8_t *ReallocateDownward(Allocator *allocator, uint8_t *old_p, + size_t old_size, size_t new_size, + size_t in_use_back, size_t in_use_front) { + return allocator->reallocate_downward(old_p, old_size, new_size, in_use_back, + in_use_front); +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_DEFAULT_ALLOCATOR_H_ diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/detached_buffer.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/detached_buffer.h new file mode 100644 index 00000000..760a0884 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/detached_buffer.h @@ -0,0 +1,114 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_DETACHED_BUFFER_H_ +#define FLATBUFFERS_DETACHED_BUFFER_H_ + +#include "flatbuffers/allocator.h" +#include "flatbuffers/base.h" +#include "flatbuffers/default_allocator.h" + +namespace flatbuffers { + +// DetachedBuffer is a finished flatbuffer memory region, detached from its +// builder. The original memory region and allocator are also stored so that +// the DetachedBuffer can manage the memory lifetime. +class DetachedBuffer { + public: + DetachedBuffer() + : allocator_(nullptr), + own_allocator_(false), + buf_(nullptr), + reserved_(0), + cur_(nullptr), + size_(0) {} + + DetachedBuffer(Allocator *allocator, bool own_allocator, uint8_t *buf, + size_t reserved, uint8_t *cur, size_t sz) + : allocator_(allocator), + own_allocator_(own_allocator), + buf_(buf), + reserved_(reserved), + cur_(cur), + size_(sz) {} + + DetachedBuffer(DetachedBuffer &&other) + : allocator_(other.allocator_), + own_allocator_(other.own_allocator_), + buf_(other.buf_), + reserved_(other.reserved_), + cur_(other.cur_), + size_(other.size_) { + other.reset(); + } + + DetachedBuffer &operator=(DetachedBuffer &&other) { + if (this == &other) return *this; + + destroy(); + + allocator_ = other.allocator_; + own_allocator_ = other.own_allocator_; + buf_ = other.buf_; + reserved_ = other.reserved_; + cur_ = other.cur_; + size_ = other.size_; + + other.reset(); + + return *this; + } + + ~DetachedBuffer() { destroy(); } + + const uint8_t *data() const { return cur_; } + + uint8_t *data() { return cur_; } + + size_t size() const { return size_; } + + // These may change access mode, leave these at end of public section + FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer &other)); + FLATBUFFERS_DELETE_FUNC( + DetachedBuffer &operator=(const DetachedBuffer &other)); + + protected: + Allocator *allocator_; + bool own_allocator_; + uint8_t *buf_; + size_t reserved_; + uint8_t *cur_; + size_t size_; + + inline void destroy() { + if (buf_) Deallocate(allocator_, buf_, reserved_); + if (own_allocator_ && allocator_) { delete allocator_; } + reset(); + } + + inline void reset() { + allocator_ = nullptr; + own_allocator_ = false; + buf_ = nullptr; + reserved_ = 0; + cur_ = nullptr; + size_ = 0; + } +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_DETACHED_BUFFER_H_ diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h new file mode 100644 index 00000000..8be4efbe --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h @@ -0,0 +1,1187 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_FLATBUFFER_BUILDER_H_ +#define FLATBUFFERS_FLATBUFFER_BUILDER_H_ + +#include + +#include "flatbuffers/allocator.h" +#include "flatbuffers/array.h" +#include "flatbuffers/base.h" +#include "flatbuffers/buffer_ref.h" +#include "flatbuffers/default_allocator.h" +#include "flatbuffers/detached_buffer.h" +#include "flatbuffers/stl_emulation.h" +#include "flatbuffers/string.h" +#include "flatbuffers/struct.h" +#include "flatbuffers/table.h" +#include "flatbuffers/vector.h" +#include "flatbuffers/vector_downward.h" +#include "flatbuffers/verifier.h" + +namespace flatbuffers { + +// Converts a Field ID to a virtual table offset. +inline voffset_t FieldIndexToOffset(voffset_t field_id) { + // Should correspond to what EndTable() below builds up. + const int fixed_fields = 2; // Vtable size and Object Size. + return static_cast((field_id + fixed_fields) * sizeof(voffset_t)); +} + +template +const T *data(const std::vector &v) { + // Eventually the returned pointer gets passed down to memcpy, so + // we need it to be non-null to avoid undefined behavior. + static uint8_t t; + return v.empty() ? reinterpret_cast(&t) : &v.front(); +} +template T *data(std::vector &v) { + // Eventually the returned pointer gets passed down to memcpy, so + // we need it to be non-null to avoid undefined behavior. + static uint8_t t; + return v.empty() ? reinterpret_cast(&t) : &v.front(); +} + +/// @addtogroup flatbuffers_cpp_api +/// @{ +/// @class FlatBufferBuilder +/// @brief Helper class to hold data needed in creation of a FlatBuffer. +/// To serialize data, you typically call one of the `Create*()` functions in +/// the generated code, which in turn call a sequence of `StartTable`/ +/// `PushElement`/`AddElement`/`EndTable`, or the builtin `CreateString`/ +/// `CreateVector` functions. Do this is depth-first order to build up a tree to +/// the root. `Finish()` wraps up the buffer ready for transport. +class FlatBufferBuilder { + public: + /// @brief Default constructor for FlatBufferBuilder. + /// @param[in] initial_size The initial size of the buffer, in bytes. Defaults + /// to `1024`. + /// @param[in] allocator An `Allocator` to use. If null will use + /// `DefaultAllocator`. + /// @param[in] own_allocator Whether the builder/vector should own the + /// allocator. Defaults to / `false`. + /// @param[in] buffer_minalign Force the buffer to be aligned to the given + /// minimum alignment upon reallocation. Only needed if you intend to store + /// types with custom alignment AND you wish to read the buffer in-place + /// directly after creation. + explicit FlatBufferBuilder( + size_t initial_size = 1024, Allocator *allocator = nullptr, + bool own_allocator = false, + size_t buffer_minalign = AlignOf()) + : buf_(initial_size, allocator, own_allocator, buffer_minalign), + num_field_loc(0), + max_voffset_(0), + nested(false), + finished(false), + minalign_(1), + force_defaults_(false), + dedup_vtables_(true), + string_pool(nullptr) { + EndianCheck(); + } + + /// @brief Move constructor for FlatBufferBuilder. + FlatBufferBuilder(FlatBufferBuilder &&other) + : buf_(1024, nullptr, false, AlignOf()), + num_field_loc(0), + max_voffset_(0), + nested(false), + finished(false), + minalign_(1), + force_defaults_(false), + dedup_vtables_(true), + string_pool(nullptr) { + EndianCheck(); + // Default construct and swap idiom. + // Lack of delegating constructors in vs2010 makes it more verbose than + // needed. + Swap(other); + } + + /// @brief Move assignment operator for FlatBufferBuilder. + FlatBufferBuilder &operator=(FlatBufferBuilder &&other) { + // Move construct a temporary and swap idiom + FlatBufferBuilder temp(std::move(other)); + Swap(temp); + return *this; + } + + void Swap(FlatBufferBuilder &other) { + using std::swap; + buf_.swap(other.buf_); + swap(num_field_loc, other.num_field_loc); + swap(max_voffset_, other.max_voffset_); + swap(nested, other.nested); + swap(finished, other.finished); + swap(minalign_, other.minalign_); + swap(force_defaults_, other.force_defaults_); + swap(dedup_vtables_, other.dedup_vtables_); + swap(string_pool, other.string_pool); + } + + ~FlatBufferBuilder() { + if (string_pool) delete string_pool; + } + + void Reset() { + Clear(); // clear builder state + buf_.reset(); // deallocate buffer + } + + /// @brief Reset all the state in this FlatBufferBuilder so it can be reused + /// to construct another buffer. + void Clear() { + ClearOffsets(); + buf_.clear(); + nested = false; + finished = false; + minalign_ = 1; + if (string_pool) string_pool->clear(); + } + + /// @brief The current size of the serialized buffer, counting from the end. + /// @return Returns an `uoffset_t` with the current size of the buffer. + uoffset_t GetSize() const { return buf_.size(); } + + /// @brief Get the serialized buffer (after you call `Finish()`). + /// @return Returns an `uint8_t` pointer to the FlatBuffer data inside the + /// buffer. + uint8_t *GetBufferPointer() const { + Finished(); + return buf_.data(); + } + + /// @brief Get the serialized buffer (after you call `Finish()`) as a span. + /// @return Returns a constructed flatbuffers::span that is a view over the + /// FlatBuffer data inside the buffer. + flatbuffers::span GetBufferSpan() const { + Finished(); + return flatbuffers::span(buf_.data(), buf_.size()); + } + + /// @brief Get a pointer to an unfinished buffer. + /// @return Returns a `uint8_t` pointer to the unfinished buffer. + uint8_t *GetCurrentBufferPointer() const { return buf_.data(); } + + /// @brief Get the released pointer to the serialized buffer. + /// @warning Do NOT attempt to use this FlatBufferBuilder afterwards! + /// @return A `FlatBuffer` that owns the buffer and its allocator and + /// behaves similar to a `unique_ptr` with a deleter. + FLATBUFFERS_ATTRIBUTE([[deprecated("use Release() instead")]]) + DetachedBuffer ReleaseBufferPointer() { + Finished(); + return buf_.release(); + } + + /// @brief Get the released DetachedBuffer. + /// @return A `DetachedBuffer` that owns the buffer and its allocator. + DetachedBuffer Release() { + Finished(); + return buf_.release(); + } + + /// @brief Get the released pointer to the serialized buffer. + /// @param size The size of the memory block containing + /// the serialized `FlatBuffer`. + /// @param offset The offset from the released pointer where the finished + /// `FlatBuffer` starts. + /// @return A raw pointer to the start of the memory block containing + /// the serialized `FlatBuffer`. + /// @remark If the allocator is owned, it gets deleted when the destructor is + /// called.. + uint8_t *ReleaseRaw(size_t &size, size_t &offset) { + Finished(); + return buf_.release_raw(size, offset); + } + + /// @brief get the minimum alignment this buffer needs to be accessed + /// properly. This is only known once all elements have been written (after + /// you call Finish()). You can use this information if you need to embed + /// a FlatBuffer in some other buffer, such that you can later read it + /// without first having to copy it into its own buffer. + size_t GetBufferMinAlignment() const { + Finished(); + return minalign_; + } + + /// @cond FLATBUFFERS_INTERNAL + void Finished() const { + // If you get this assert, you're attempting to get access a buffer + // which hasn't been finished yet. Be sure to call + // FlatBufferBuilder::Finish with your root table. + // If you really need to access an unfinished buffer, call + // GetCurrentBufferPointer instead. + FLATBUFFERS_ASSERT(finished); + } + /// @endcond + + /// @brief In order to save space, fields that are set to their default value + /// don't get serialized into the buffer. + /// @param[in] fd When set to `true`, always serializes default values that + /// are set. Optional fields which are not set explicitly, will still not be + /// serialized. + void ForceDefaults(bool fd) { force_defaults_ = fd; } + + /// @brief By default vtables are deduped in order to save space. + /// @param[in] dedup When set to `true`, dedup vtables. + void DedupVtables(bool dedup) { dedup_vtables_ = dedup; } + + /// @cond FLATBUFFERS_INTERNAL + void Pad(size_t num_bytes) { buf_.fill(num_bytes); } + + void TrackMinAlign(size_t elem_size) { + if (elem_size > minalign_) minalign_ = elem_size; + } + + void Align(size_t elem_size) { + TrackMinAlign(elem_size); + buf_.fill(PaddingBytes(buf_.size(), elem_size)); + } + + void PushFlatBuffer(const uint8_t *bytes, size_t size) { + PushBytes(bytes, size); + finished = true; + } + + void PushBytes(const uint8_t *bytes, size_t size) { buf_.push(bytes, size); } + + void PopBytes(size_t amount) { buf_.pop(amount); } + + template void AssertScalarT() { + // The code assumes power of 2 sizes and endian-swap-ability. + static_assert(flatbuffers::is_scalar::value, "T must be a scalar type"); + } + + // Write a single aligned scalar to the buffer + template uoffset_t PushElement(T element) { + AssertScalarT(); + Align(sizeof(T)); + buf_.push_small(EndianScalar(element)); + return GetSize(); + } + + template uoffset_t PushElement(Offset off) { + // Special case for offsets: see ReferTo below. + return PushElement(ReferTo(off.o)); + } + + // When writing fields, we track where they are, so we can create correct + // vtables later. + void TrackField(voffset_t field, uoffset_t off) { + FieldLoc fl = { off, field }; + buf_.scratch_push_small(fl); + num_field_loc++; + if (field > max_voffset_) { + max_voffset_ = field; + } + } + + // Like PushElement, but additionally tracks the field this represents. + template void AddElement(voffset_t field, T e, T def) { + // We don't serialize values equal to the default. + if (IsTheSameAs(e, def) && !force_defaults_) return; + TrackField(field, PushElement(e)); + } + + template void AddElement(voffset_t field, T e) { + TrackField(field, PushElement(e)); + } + + template void AddOffset(voffset_t field, Offset off) { + if (off.IsNull()) return; // Don't store. + AddElement(field, ReferTo(off.o), static_cast(0)); + } + + template void AddStruct(voffset_t field, const T *structptr) { + if (!structptr) return; // Default, don't store. + Align(AlignOf()); + buf_.push_small(*structptr); + TrackField(field, GetSize()); + } + + void AddStructOffset(voffset_t field, uoffset_t off) { + TrackField(field, off); + } + + // Offsets initially are relative to the end of the buffer (downwards). + // This function converts them to be relative to the current location + // in the buffer (when stored here), pointing upwards. + uoffset_t ReferTo(uoffset_t off) { + // Align to ensure GetSize() below is correct. + Align(sizeof(uoffset_t)); + // Offset must refer to something already in buffer. + const uoffset_t size = GetSize(); + FLATBUFFERS_ASSERT(off && off <= size); + return size - off + static_cast(sizeof(uoffset_t)); + } + + void NotNested() { + // If you hit this, you're trying to construct a Table/Vector/String + // during the construction of its parent table (between the MyTableBuilder + // and table.Finish(). + // Move the creation of these sub-objects to above the MyTableBuilder to + // not get this assert. + // Ignoring this assert may appear to work in simple cases, but the reason + // it is here is that storing objects in-line may cause vtable offsets + // to not fit anymore. It also leads to vtable duplication. + FLATBUFFERS_ASSERT(!nested); + // If you hit this, fields were added outside the scope of a table. + FLATBUFFERS_ASSERT(!num_field_loc); + } + + // From generated code (or from the parser), we call StartTable/EndTable + // with a sequence of AddElement calls in between. + uoffset_t StartTable() { + NotNested(); + nested = true; + return GetSize(); + } + + // This finishes one serialized object by generating the vtable if it's a + // table, comparing it against existing vtables, and writing the + // resulting vtable offset. + uoffset_t EndTable(uoffset_t start) { + // If you get this assert, a corresponding StartTable wasn't called. + FLATBUFFERS_ASSERT(nested); + // Write the vtable offset, which is the start of any Table. + // We fill it's value later. + auto vtableoffsetloc = PushElement(0); + // Write a vtable, which consists entirely of voffset_t elements. + // It starts with the number of offsets, followed by a type id, followed + // by the offsets themselves. In reverse: + // Include space for the last offset and ensure empty tables have a + // minimum size. + max_voffset_ = + (std::max)(static_cast(max_voffset_ + sizeof(voffset_t)), + FieldIndexToOffset(0)); + buf_.fill_big(max_voffset_); + auto table_object_size = vtableoffsetloc - start; + // Vtable use 16bit offsets. + FLATBUFFERS_ASSERT(table_object_size < 0x10000); + WriteScalar(buf_.data() + sizeof(voffset_t), + static_cast(table_object_size)); + WriteScalar(buf_.data(), max_voffset_); + // Write the offsets into the table + for (auto it = buf_.scratch_end() - num_field_loc * sizeof(FieldLoc); + it < buf_.scratch_end(); it += sizeof(FieldLoc)) { + auto field_location = reinterpret_cast(it); + auto pos = static_cast(vtableoffsetloc - field_location->off); + // If this asserts, it means you've set a field twice. + FLATBUFFERS_ASSERT( + !ReadScalar(buf_.data() + field_location->id)); + WriteScalar(buf_.data() + field_location->id, pos); + } + ClearOffsets(); + auto vt1 = reinterpret_cast(buf_.data()); + auto vt1_size = ReadScalar(vt1); + auto vt_use = GetSize(); + // See if we already have generated a vtable with this exact same + // layout before. If so, make it point to the old one, remove this one. + if (dedup_vtables_) { + for (auto it = buf_.scratch_data(); it < buf_.scratch_end(); + it += sizeof(uoffset_t)) { + auto vt_offset_ptr = reinterpret_cast(it); + auto vt2 = reinterpret_cast(buf_.data_at(*vt_offset_ptr)); + auto vt2_size = ReadScalar(vt2); + if (vt1_size != vt2_size || 0 != memcmp(vt2, vt1, vt1_size)) continue; + vt_use = *vt_offset_ptr; + buf_.pop(GetSize() - vtableoffsetloc); + break; + } + } + // If this is a new vtable, remember it. + if (vt_use == GetSize()) { buf_.scratch_push_small(vt_use); } + // Fill the vtable offset we created above. + // The offset points from the beginning of the object to where the + // vtable is stored. + // Offsets default direction is downward in memory for future format + // flexibility (storing all vtables at the start of the file). + WriteScalar(buf_.data_at(vtableoffsetloc), + static_cast(vt_use) - + static_cast(vtableoffsetloc)); + + nested = false; + return vtableoffsetloc; + } + + FLATBUFFERS_ATTRIBUTE([[deprecated("call the version above instead")]]) + uoffset_t EndTable(uoffset_t start, voffset_t /*numfields*/) { + return EndTable(start); + } + + // This checks a required field has been set in a given table that has + // just been constructed. + template void Required(Offset table, voffset_t field); + + uoffset_t StartStruct(size_t alignment) { + Align(alignment); + return GetSize(); + } + + uoffset_t EndStruct() { return GetSize(); } + + void ClearOffsets() { + buf_.scratch_pop(num_field_loc * sizeof(FieldLoc)); + num_field_loc = 0; + max_voffset_ = 0; + } + + // Aligns such that when "len" bytes are written, an object can be written + // after it with "alignment" without padding. + void PreAlign(size_t len, size_t alignment) { + TrackMinAlign(alignment); + buf_.fill(PaddingBytes(GetSize() + len, alignment)); + } + template void PreAlign(size_t len) { + AssertScalarT(); + PreAlign(len, sizeof(T)); + } + /// @endcond + + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const char pointer to the data to be stored as a string. + /// @param[in] len The number of bytes that should be stored from `str`. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(const char *str, size_t len) { + NotNested(); + PreAlign(len + 1); // Always 0-terminated. + buf_.fill(1); + PushBytes(reinterpret_cast(str), len); + PushElement(static_cast(len)); + return Offset(GetSize()); + } + + /// @brief Store a string in the buffer, which is null-terminated. + /// @param[in] str A const char pointer to a C-string to add to the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(const char *str) { + return CreateString(str, strlen(str)); + } + + /// @brief Store a string in the buffer, which is null-terminated. + /// @param[in] str A char pointer to a C-string to add to the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(char *str) { + return CreateString(str, strlen(str)); + } + + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const reference to a std::string to store in the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(const std::string &str) { + return CreateString(str.c_str(), str.length()); + } + + // clang-format off + #ifdef FLATBUFFERS_HAS_STRING_VIEW + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const string_view to copy in to the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(flatbuffers::string_view str) { + return CreateString(str.data(), str.size()); + } + #endif // FLATBUFFERS_HAS_STRING_VIEW + // clang-format on + + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const pointer to a `String` struct to add to the buffer. + /// @return Returns the offset in the buffer where the string starts + Offset CreateString(const String *str) { + return str ? CreateString(str->c_str(), str->size()) : 0; + } + + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const reference to a std::string like type with support + /// of T::c_str() and T::length() to store in the buffer. + /// @return Returns the offset in the buffer where the string starts. + template Offset CreateString(const T &str) { + return CreateString(str.c_str(), str.length()); + } + + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. This uses a map + /// stored on the heap, but only stores the numerical offsets. + /// @param[in] str A const char pointer to the data to be stored as a string. + /// @param[in] len The number of bytes that should be stored from `str`. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateSharedString(const char *str, size_t len) { + FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); + if (!string_pool) + string_pool = new StringOffsetMap(StringOffsetCompare(buf_)); + auto size_before_string = buf_.size(); + // Must first serialize the string, since the set is all offsets into + // buffer. + auto off = CreateString(str, len); + auto it = string_pool->find(off); + // If it exists we reuse existing serialized data! + if (it != string_pool->end()) { + // We can remove the string we serialized. + buf_.pop(buf_.size() - size_before_string); + return *it; + } + // Record this string for future use. + string_pool->insert(off); + return off; + } + +#ifdef FLATBUFFERS_HAS_STRING_VIEW + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. This uses a map + /// stored on the heap, but only stores the numerical offsets. + /// @param[in] str A const std::string_view to store in the buffer. + /// @return Returns the offset in the buffer where the string starts + Offset CreateSharedString(const flatbuffers::string_view str) { + return CreateSharedString(str.data(), str.size()); + } +#else + /// @brief Store a string in the buffer, which null-terminated. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. This uses a map + /// stored on the heap, but only stores the numerical offsets. + /// @param[in] str A const char pointer to a C-string to add to the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateSharedString(const char *str) { + return CreateSharedString(str, strlen(str)); + } + + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. This uses a map + /// stored on the heap, but only stores the numerical offsets. + /// @param[in] str A const reference to a std::string to store in the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateSharedString(const std::string &str) { + return CreateSharedString(str.c_str(), str.length()); + } +#endif + + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. This uses a map + /// stored on the heap, but only stores the numerical offsets. + /// @param[in] str A const pointer to a `String` struct to add to the buffer. + /// @return Returns the offset in the buffer where the string starts + Offset CreateSharedString(const String *str) { + return CreateSharedString(str->c_str(), str->size()); + } + + /// @cond FLATBUFFERS_INTERNAL + uoffset_t EndVector(size_t len) { + FLATBUFFERS_ASSERT(nested); // Hit if no corresponding StartVector. + nested = false; + return PushElement(static_cast(len)); + } + + void StartVector(size_t len, size_t elemsize) { + NotNested(); + nested = true; + PreAlign(len * elemsize); + PreAlign(len * elemsize, elemsize); // Just in case elemsize > uoffset_t. + } + + // Call this right before StartVector/CreateVector if you want to force the + // alignment to be something different than what the element size would + // normally dictate. + // This is useful when storing a nested_flatbuffer in a vector of bytes, + // or when storing SIMD floats, etc. + void ForceVectorAlignment(size_t len, size_t elemsize, size_t alignment) { + FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); + PreAlign(len * elemsize, alignment); + } + + // Similar to ForceVectorAlignment but for String fields. + void ForceStringAlignment(size_t len, size_t alignment) { + FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); + PreAlign((len + 1) * sizeof(char), alignment); + } + + /// @endcond + + /// @brief Serialize an array into a FlatBuffer `vector`. + /// @tparam T The data type of the array elements. + /// @param[in] v A pointer to the array of type `T` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template Offset> CreateVector(const T *v, size_t len) { + // If this assert hits, you're specifying a template argument that is + // causing the wrong overload to be selected, remove it. + AssertScalarT(); + StartVector(len, sizeof(T)); + if (len == 0) { return Offset>(EndVector(len)); } + // clang-format off + #if FLATBUFFERS_LITTLEENDIAN + PushBytes(reinterpret_cast(v), len * sizeof(T)); + #else + if (sizeof(T) == 1) { + PushBytes(reinterpret_cast(v), len); + } else { + for (auto i = len; i > 0; ) { + PushElement(v[--i]); + } + } + #endif + // clang-format on + return Offset>(EndVector(len)); + } + + template + Offset>> CreateVector(const Offset *v, size_t len) { + StartVector(len, sizeof(Offset)); + for (auto i = len; i > 0;) { PushElement(v[--i]); } + return Offset>>(EndVector(len)); + } + + /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. + /// @tparam T The data type of the `std::vector` elements. + /// @param v A const reference to the `std::vector` to serialize into the + /// buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVector(const std::vector &v) { + return CreateVector(data(v), v.size()); + } + + // vector may be implemented using a bit-set, so we can't access it as + // an array. Instead, read elements manually. + // Background: https://isocpp.org/blog/2012/11/on-vectorbool + Offset> CreateVector(const std::vector &v) { + StartVector(v.size(), sizeof(uint8_t)); + for (auto i = v.size(); i > 0;) { + PushElement(static_cast(v[--i])); + } + return Offset>(EndVector(v.size())); + } + + /// @brief Serialize values returned by a function into a FlatBuffer `vector`. + /// This is a convenience function that takes care of iteration for you. + /// @tparam T The data type of the `std::vector` elements. + /// @param f A function that takes the current iteration 0..vector_size-1 and + /// returns any type that you can construct a FlatBuffers vector out of. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVector(size_t vector_size, + const std::function &f) { + FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); + std::vector elems(vector_size); + for (size_t i = 0; i < vector_size; i++) elems[i] = f(i); + return CreateVector(elems); + } + + /// @brief Serialize values returned by a function into a FlatBuffer `vector`. + /// This is a convenience function that takes care of iteration for you. This + /// uses a vector stored on the heap to store the intermediate results of the + /// iteration. + /// @tparam T The data type of the `std::vector` elements. + /// @param f A function that takes the current iteration 0..vector_size-1, + /// and the state parameter returning any type that you can construct a + /// FlatBuffers vector out of. + /// @param state State passed to f. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVector(size_t vector_size, F f, S *state) { + FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); + std::vector elems(vector_size); + for (size_t i = 0; i < vector_size; i++) elems[i] = f(i, state); + return CreateVector(elems); + } + + /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. + /// This is a convenience function for a common case. + /// @param v A const reference to the `std::vector` to serialize into the + /// buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset>> CreateVectorOfStrings( + const std::vector &v) { + return CreateVectorOfStrings(v.cbegin(), v.cend()); + } + + /// @brief Serialize a collection of Strings into a FlatBuffer `vector`. + /// This is a convenience function for a common case. + /// @param begin The begining iterator of the collection + /// @param end The ending iterator of the collection + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset>> CreateVectorOfStrings(It begin, It end) { + auto size = std::distance(begin, end); + auto scratch_buffer_usage = size * sizeof(Offset); + // If there is not enough space to store the offsets, there definitely won't + // be enough space to store all the strings. So ensuring space for the + // scratch region is OK, for it it fails, it would have failed later. + buf_.ensure_space(scratch_buffer_usage); + for (auto it = begin; it != end; ++it) { + buf_.scratch_push_small(CreateString(*it)); + } + StartVector(size, sizeof(Offset)); + for (auto i = 1; i <= size; i++) { + // Note we re-evaluate the buf location each iteration to account for any + // underlying buffer resizing that may occur. + PushElement(*reinterpret_cast *>( + buf_.scratch_end() - i * sizeof(Offset))); + } + buf_.scratch_pop(scratch_buffer_usage); + return Offset>>(EndVector(size)); + } + + /// @brief Serialize an array of structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @param[in] v A pointer to the array of type `T` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfStructs(const T *v, size_t len) { + StartVector(len * sizeof(T) / AlignOf(), AlignOf()); + if (len > 0) { + PushBytes(reinterpret_cast(v), sizeof(T) * len); + } + return Offset>(EndVector(len)); + } + + /// @brief Serialize an array of native structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @tparam S The data type of the native struct array elements. + /// @param[in] v A pointer to the array of type `S` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @param[in] pack_func Pointer to a function to convert the native struct + /// to the FlatBuffer struct. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs( + const S *v, size_t len, T (*const pack_func)(const S &)) { + FLATBUFFERS_ASSERT(pack_func); + auto structs = StartVectorOfStructs(len); + for (size_t i = 0; i < len; i++) { structs[i] = pack_func(v[i]); } + return EndVectorOfStructs(len); + } + + /// @brief Serialize an array of native structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @tparam S The data type of the native struct array elements. + /// @param[in] v A pointer to the array of type `S` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs(const S *v, + size_t len) { + extern T Pack(const S &); + return CreateVectorOfNativeStructs(v, len, Pack); + } + + /// @brief Serialize an array of structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @param[in] filler A function that takes the current iteration + /// 0..vector_size-1 and a pointer to the struct that must be filled. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + /// This is mostly useful when flatbuffers are generated with mutation + /// accessors. + template + Offset> CreateVectorOfStructs( + size_t vector_size, const std::function &filler) { + T *structs = StartVectorOfStructs(vector_size); + for (size_t i = 0; i < vector_size; i++) { + filler(i, structs); + structs++; + } + return EndVectorOfStructs(vector_size); + } + + /// @brief Serialize an array of structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @param[in] f A function that takes the current iteration 0..vector_size-1, + /// a pointer to the struct that must be filled and the state argument. + /// @param[in] state Arbitrary state to pass to f. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + /// This is mostly useful when flatbuffers are generated with mutation + /// accessors. + template + Offset> CreateVectorOfStructs(size_t vector_size, F f, + S *state) { + T *structs = StartVectorOfStructs(vector_size); + for (size_t i = 0; i < vector_size; i++) { + f(i, structs, state); + structs++; + } + return EndVectorOfStructs(vector_size); + } + + /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector`. + /// @tparam T The data type of the `std::vector` struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfStructs( + const std::vector &v) { + return CreateVectorOfStructs(data(v), v.size()); + } + + /// @brief Serialize a `std::vector` of native structs into a FlatBuffer + /// `vector`. + /// @tparam T The data type of the `std::vector` struct elements. + /// @tparam S The data type of the `std::vector` native struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @param[in] pack_func Pointer to a function to convert the native struct + /// to the FlatBuffer struct. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs( + const std::vector &v, T (*const pack_func)(const S &)) { + return CreateVectorOfNativeStructs(data(v), v.size(), pack_func); + } + + /// @brief Serialize a `std::vector` of native structs into a FlatBuffer + /// `vector`. + /// @tparam T The data type of the `std::vector` struct elements. + /// @tparam S The data type of the `std::vector` native struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs( + const std::vector &v) { + return CreateVectorOfNativeStructs(data(v), v.size()); + } + + /// @cond FLATBUFFERS_INTERNAL + template struct StructKeyComparator { + bool operator()(const T &a, const T &b) const { + return a.KeyCompareLessThan(&b); + } + }; + /// @endcond + + /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector` + /// in sorted order. + /// @tparam T The data type of the `std::vector` struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfSortedStructs( + std::vector *v) { + return CreateVectorOfSortedStructs(data(*v), v->size()); + } + + /// @brief Serialize a `std::vector` of native structs into a FlatBuffer + /// `vector` in sorted order. + /// @tparam T The data type of the `std::vector` struct elements. + /// @tparam S The data type of the `std::vector` native struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfSortedNativeStructs( + std::vector *v) { + return CreateVectorOfSortedNativeStructs(data(*v), v->size()); + } + + /// @brief Serialize an array of structs into a FlatBuffer `vector` in sorted + /// order. + /// @tparam T The data type of the struct array elements. + /// @param[in] v A pointer to the array of type `T` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfSortedStructs(T *v, size_t len) { + std::sort(v, v + len, StructKeyComparator()); + return CreateVectorOfStructs(v, len); + } + + /// @brief Serialize an array of native structs into a FlatBuffer `vector` in + /// sorted order. + /// @tparam T The data type of the struct array elements. + /// @tparam S The data type of the native struct array elements. + /// @param[in] v A pointer to the array of type `S` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfSortedNativeStructs(S *v, + size_t len) { + extern T Pack(const S &); + auto structs = StartVectorOfStructs(len); + for (size_t i = 0; i < len; i++) { structs[i] = Pack(v[i]); } + std::sort(structs, structs + len, StructKeyComparator()); + return EndVectorOfStructs(len); + } + + /// @cond FLATBUFFERS_INTERNAL + template struct TableKeyComparator { + TableKeyComparator(vector_downward &buf) : buf_(buf) {} + TableKeyComparator(const TableKeyComparator &other) : buf_(other.buf_) {} + bool operator()(const Offset &a, const Offset &b) const { + auto table_a = reinterpret_cast(buf_.data_at(a.o)); + auto table_b = reinterpret_cast(buf_.data_at(b.o)); + return table_a->KeyCompareLessThan(table_b); + } + vector_downward &buf_; + + private: + FLATBUFFERS_DELETE_FUNC( + TableKeyComparator &operator=(const TableKeyComparator &other)); + }; + /// @endcond + + /// @brief Serialize an array of `table` offsets as a `vector` in the buffer + /// in sorted order. + /// @tparam T The data type that the offset refers to. + /// @param[in] v An array of type `Offset` that contains the `table` + /// offsets to store in the buffer in sorted order. + /// @param[in] len The number of elements to store in the `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset>> CreateVectorOfSortedTables(Offset *v, + size_t len) { + std::sort(v, v + len, TableKeyComparator(buf_)); + return CreateVector(v, len); + } + + /// @brief Serialize an array of `table` offsets as a `vector` in the buffer + /// in sorted order. + /// @tparam T The data type that the offset refers to. + /// @param[in] v An array of type `Offset` that contains the `table` + /// offsets to store in the buffer in sorted order. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset>> CreateVectorOfSortedTables( + std::vector, Alloc> *v) { + return CreateVectorOfSortedTables(data(*v), v->size()); + } + + /// @brief Specialized version of `CreateVector` for non-copying use cases. + /// Write the data any time later to the returned buffer pointer `buf`. + /// @param[in] len The number of elements to store in the `vector`. + /// @param[in] elemsize The size of each element in the `vector`. + /// @param[out] buf A pointer to a `uint8_t` pointer that can be + /// written to at a later time to serialize the data into a `vector` + /// in the buffer. + uoffset_t CreateUninitializedVector(size_t len, size_t elemsize, + uint8_t **buf) { + NotNested(); + StartVector(len, elemsize); + buf_.make_space(len * elemsize); + auto vec_start = GetSize(); + auto vec_end = EndVector(len); + *buf = buf_.data_at(vec_start); + return vec_end; + } + + /// @brief Specialized version of `CreateVector` for non-copying use cases. + /// Write the data any time later to the returned buffer pointer `buf`. + /// @tparam T The data type of the data that will be stored in the buffer + /// as a `vector`. + /// @param[in] len The number of elements to store in the `vector`. + /// @param[out] buf A pointer to a pointer of type `T` that can be + /// written to at a later time to serialize the data into a `vector` + /// in the buffer. + template + Offset> CreateUninitializedVector(size_t len, T **buf) { + AssertScalarT(); + return CreateUninitializedVector(len, sizeof(T), + reinterpret_cast(buf)); + } + + template + Offset> CreateUninitializedVectorOfStructs(size_t len, + T **buf) { + return CreateUninitializedVector(len, sizeof(T), + reinterpret_cast(buf)); + } + + // @brief Create a vector of scalar type T given as input a vector of scalar + // type U, useful with e.g. pre "enum class" enums, or any existing scalar + // data of the wrong type. + template + Offset> CreateVectorScalarCast(const U *v, size_t len) { + AssertScalarT(); + AssertScalarT(); + StartVector(len, sizeof(T)); + for (auto i = len; i > 0;) { PushElement(static_cast(v[--i])); } + return Offset>(EndVector(len)); + } + + /// @brief Write a struct by itself, typically to be part of a union. + template Offset CreateStruct(const T &structobj) { + NotNested(); + Align(AlignOf()); + buf_.push_small(structobj); + return Offset(GetSize()); + } + + /// @brief Finish serializing a buffer by writing the root offset. + /// @param[in] file_identifier If a `file_identifier` is given, the buffer + /// will be prefixed with a standard FlatBuffers file header. + template + void Finish(Offset root, const char *file_identifier = nullptr) { + Finish(root.o, file_identifier, false); + } + + /// @brief Finish a buffer with a 32 bit size field pre-fixed (size of the + /// buffer following the size field). These buffers are NOT compatible + /// with standard buffers created by Finish, i.e. you can't call GetRoot + /// on them, you have to use GetSizePrefixedRoot instead. + /// All >32 bit quantities in this buffer will be aligned when the whole + /// size pre-fixed buffer is aligned. + /// These kinds of buffers are useful for creating a stream of FlatBuffers. + template + void FinishSizePrefixed(Offset root, + const char *file_identifier = nullptr) { + Finish(root.o, file_identifier, true); + } + + void SwapBufAllocator(FlatBufferBuilder &other) { + buf_.swap_allocator(other.buf_); + } + + /// @brief The length of a FlatBuffer file header. + static const size_t kFileIdentifierLength = + ::flatbuffers::kFileIdentifierLength; + + protected: + // You shouldn't really be copying instances of this class. + FlatBufferBuilder(const FlatBufferBuilder &); + FlatBufferBuilder &operator=(const FlatBufferBuilder &); + + void Finish(uoffset_t root, const char *file_identifier, bool size_prefix) { + NotNested(); + buf_.clear_scratch(); + // This will cause the whole buffer to be aligned. + PreAlign((size_prefix ? sizeof(uoffset_t) : 0) + sizeof(uoffset_t) + + (file_identifier ? kFileIdentifierLength : 0), + minalign_); + if (file_identifier) { + FLATBUFFERS_ASSERT(strlen(file_identifier) == kFileIdentifierLength); + PushBytes(reinterpret_cast(file_identifier), + kFileIdentifierLength); + } + PushElement(ReferTo(root)); // Location of root. + if (size_prefix) { PushElement(GetSize()); } + finished = true; + } + + struct FieldLoc { + uoffset_t off; + voffset_t id; + }; + + vector_downward buf_; + + // Accumulating offsets of table members while it is being built. + // We store these in the scratch pad of buf_, after the vtable offsets. + uoffset_t num_field_loc; + // Track how much of the vtable is in use, so we can output the most compact + // possible vtable. + voffset_t max_voffset_; + + // Ensure objects are not nested. + bool nested; + + // Ensure the buffer is finished before it is being accessed. + bool finished; + + size_t minalign_; + + bool force_defaults_; // Serialize values equal to their defaults anyway. + + bool dedup_vtables_; + + struct StringOffsetCompare { + StringOffsetCompare(const vector_downward &buf) : buf_(&buf) {} + bool operator()(const Offset &a, const Offset &b) const { + auto stra = reinterpret_cast(buf_->data_at(a.o)); + auto strb = reinterpret_cast(buf_->data_at(b.o)); + return StringLessThan(stra->data(), stra->size(), strb->data(), + strb->size()); + } + const vector_downward *buf_; + }; + + // For use with CreateSharedString. Instantiated on first use only. + typedef std::set, StringOffsetCompare> StringOffsetMap; + StringOffsetMap *string_pool; + + private: + // Allocates space for a vector of structures. + // Must be completed with EndVectorOfStructs(). + template T *StartVectorOfStructs(size_t vector_size) { + StartVector(vector_size * sizeof(T) / AlignOf(), AlignOf()); + return reinterpret_cast(buf_.make_space(vector_size * sizeof(T))); + } + + // End the vector of structures in the flatbuffers. + // Vector should have previously be started with StartVectorOfStructs(). + template + Offset> EndVectorOfStructs(size_t vector_size) { + return Offset>(EndVector(vector_size)); + } +}; +/// @} + +/// Helpers to get a typed pointer to objects that are currently being built. +/// @warning Creating new objects will lead to reallocations and invalidates +/// the pointer! +template +T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb, Offset offset) { + return reinterpret_cast(fbb.GetCurrentBufferPointer() + fbb.GetSize() - + offset.o); +} + +template +const T *GetTemporaryPointer(FlatBufferBuilder &fbb, Offset offset) { + return GetMutableTemporaryPointer(fbb, offset); +} + +template +void FlatBufferBuilder::Required(Offset table, voffset_t field) { + auto table_ptr = reinterpret_cast(buf_.data_at(table.o)); + bool ok = table_ptr->GetOptionalFieldOffset(field) != 0; + // If this fails, the caller will show what field needs to be set. + FLATBUFFERS_ASSERT(ok); + (void)ok; +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_VECTOR_DOWNWARD_H_ diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flatbuffers.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flatbuffers.h new file mode 100644 index 00000000..c903d646 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flatbuffers.h @@ -0,0 +1,284 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_H_ +#define FLATBUFFERS_H_ + +// TODO: These includes are for mitigating the pains of users editing their +// source because they relied on flatbuffers.h to include everything for them. +#include "flatbuffers/array.h" +#include "flatbuffers/base.h" +#include "flatbuffers/buffer.h" +#include "flatbuffers/buffer_ref.h" +#include "flatbuffers/detached_buffer.h" +#include "flatbuffers/flatbuffer_builder.h" +#include "flatbuffers/stl_emulation.h" +#include "flatbuffers/string.h" +#include "flatbuffers/struct.h" +#include "flatbuffers/table.h" +#include "flatbuffers/vector.h" +#include "flatbuffers/vector_downward.h" +#include "flatbuffers/verifier.h" + +namespace flatbuffers { + +/// @brief This can compute the start of a FlatBuffer from a root pointer, i.e. +/// it is the opposite transformation of GetRoot(). +/// This may be useful if you want to pass on a root and have the recipient +/// delete the buffer afterwards. +inline const uint8_t *GetBufferStartFromRootPointer(const void *root) { + auto table = reinterpret_cast(root); + auto vtable = table->GetVTable(); + // Either the vtable is before the root or after the root. + auto start = (std::min)(vtable, reinterpret_cast(root)); + // Align to at least sizeof(uoffset_t). + start = reinterpret_cast(reinterpret_cast(start) & + ~(sizeof(uoffset_t) - 1)); + // Additionally, there may be a file_identifier in the buffer, and the root + // offset. The buffer may have been aligned to any size between + // sizeof(uoffset_t) and FLATBUFFERS_MAX_ALIGNMENT (see "force_align"). + // Sadly, the exact alignment is only known when constructing the buffer, + // since it depends on the presence of values with said alignment properties. + // So instead, we simply look at the next uoffset_t values (root, + // file_identifier, and alignment padding) to see which points to the root. + // None of the other values can "impersonate" the root since they will either + // be 0 or four ASCII characters. + static_assert(flatbuffers::kFileIdentifierLength == sizeof(uoffset_t), + "file_identifier is assumed to be the same size as uoffset_t"); + for (auto possible_roots = FLATBUFFERS_MAX_ALIGNMENT / sizeof(uoffset_t) + 1; + possible_roots; possible_roots--) { + start -= sizeof(uoffset_t); + if (ReadScalar(start) + start == + reinterpret_cast(root)) + return start; + } + // We didn't find the root, either the "root" passed isn't really a root, + // or the buffer is corrupt. + // Assert, because calling this function with bad data may cause reads + // outside of buffer boundaries. + FLATBUFFERS_ASSERT(false); + return nullptr; +} + +/// @brief This return the prefixed size of a FlatBuffer. +inline uoffset_t GetPrefixedSize(const uint8_t *buf) { + return ReadScalar(buf); +} + +// Base class for native objects (FlatBuffer data de-serialized into native +// C++ data structures). +// Contains no functionality, purely documentative. +struct NativeTable {}; + +/// @brief Function types to be used with resolving hashes into objects and +/// back again. The resolver gets a pointer to a field inside an object API +/// object that is of the type specified in the schema using the attribute +/// `cpp_type` (it is thus important whatever you write to this address +/// matches that type). The value of this field is initially null, so you +/// may choose to implement a delayed binding lookup using this function +/// if you wish. The resolver does the opposite lookup, for when the object +/// is being serialized again. +typedef uint64_t hash_value_t; +typedef std::function + resolver_function_t; +typedef std::function rehasher_function_t; + +// Helper function to test if a field is present, using any of the field +// enums in the generated code. +// `table` must be a generated table type. Since this is a template parameter, +// this is not typechecked to be a subclass of Table, so beware! +// Note: this function will return false for fields equal to the default +// value, since they're not stored in the buffer (unless force_defaults was +// used). +template +bool IsFieldPresent(const T *table, typename T::FlatBuffersVTableOffset field) { + // Cast, since Table is a private baseclass of any table types. + return reinterpret_cast(table)->CheckField( + static_cast(field)); +} + +// Utility function for reverse lookups on the EnumNames*() functions +// (in the generated C++ code) +// names must be NULL terminated. +inline int LookupEnum(const char **names, const char *name) { + for (const char **p = names; *p; p++) + if (!strcmp(*p, name)) return static_cast(p - names); + return -1; +} + +// These macros allow us to layout a struct with a guarantee that they'll end +// up looking the same on different compilers and platforms. +// It does this by disallowing the compiler to do any padding, and then +// does padding itself by inserting extra padding fields that make every +// element aligned to its own size. +// Additionally, it manually sets the alignment of the struct as a whole, +// which is typically its largest element, or a custom size set in the schema +// by the force_align attribute. +// These are used in the generated code only. + +// clang-format off +#if defined(_MSC_VER) + #define FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(alignment) \ + __pragma(pack(1)) \ + struct __declspec(align(alignment)) + #define FLATBUFFERS_STRUCT_END(name, size) \ + __pragma(pack()) \ + static_assert(sizeof(name) == size, "compiler breaks packing rules") +#elif defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__) + #define FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(alignment) \ + _Pragma("pack(1)") \ + struct __attribute__((aligned(alignment))) + #define FLATBUFFERS_STRUCT_END(name, size) \ + _Pragma("pack()") \ + static_assert(sizeof(name) == size, "compiler breaks packing rules") +#else + #error Unknown compiler, please define structure alignment macros +#endif +// clang-format on + +// Minimal reflection via code generation. +// Besides full-fat reflection (see reflection.h) and parsing/printing by +// loading schemas (see idl.h), we can also have code generation for minimal +// reflection data which allows pretty-printing and other uses without needing +// a schema or a parser. +// Generate code with --reflect-types (types only) or --reflect-names (names +// also) to enable. +// See minireflect.h for utilities using this functionality. + +// These types are organized slightly differently as the ones in idl.h. +enum SequenceType { ST_TABLE, ST_STRUCT, ST_UNION, ST_ENUM }; + +// Scalars have the same order as in idl.h +// clang-format off +#define FLATBUFFERS_GEN_ELEMENTARY_TYPES(ET) \ + ET(ET_UTYPE) \ + ET(ET_BOOL) \ + ET(ET_CHAR) \ + ET(ET_UCHAR) \ + ET(ET_SHORT) \ + ET(ET_USHORT) \ + ET(ET_INT) \ + ET(ET_UINT) \ + ET(ET_LONG) \ + ET(ET_ULONG) \ + ET(ET_FLOAT) \ + ET(ET_DOUBLE) \ + ET(ET_STRING) \ + ET(ET_SEQUENCE) // See SequenceType. + +enum ElementaryType { + #define FLATBUFFERS_ET(E) E, + FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET) + #undef FLATBUFFERS_ET +}; + +inline const char * const *ElementaryTypeNames() { + static const char * const names[] = { + #define FLATBUFFERS_ET(E) #E, + FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET) + #undef FLATBUFFERS_ET + }; + return names; +} +// clang-format on + +// Basic type info cost just 16bits per field! +// We're explicitly defining the signedness since the signedness of integer +// bitfields is otherwise implementation-defined and causes warnings on older +// GCC compilers. +struct TypeCode { + // ElementaryType + unsigned short base_type : 4; + // Either vector (in table) or array (in struct) + unsigned short is_repeating : 1; + // Index into type_refs below, or -1 for none. + signed short sequence_ref : 11; +}; + +static_assert(sizeof(TypeCode) == 2, "TypeCode"); + +struct TypeTable; + +// Signature of the static method present in each type. +typedef const TypeTable *(*TypeFunction)(); + +struct TypeTable { + SequenceType st; + size_t num_elems; // of type_codes, values, names (but not type_refs). + const TypeCode *type_codes; // num_elems count + const TypeFunction *type_refs; // less than num_elems entries (see TypeCode). + const int16_t *array_sizes; // less than num_elems entries (see TypeCode). + const int64_t *values; // Only set for non-consecutive enum/union or structs. + const char *const *names; // Only set if compiled with --reflect-names. +}; + +// String which identifies the current version of FlatBuffers. +// flatbuffer_version_string is used by Google developers to identify which +// applications uploaded to Google Play are using this library. This allows +// the development team at Google to determine the popularity of the library. +// How it works: Applications that are uploaded to the Google Play Store are +// scanned for this version string. We track which applications are using it +// to measure popularity. You are free to remove it (of course) but we would +// appreciate if you left it in. + +// Weak linkage is culled by VS & doesn't work on cygwin. +// clang-format off +#if !defined(_WIN32) && !defined(__CYGWIN__) + +extern volatile __attribute__((weak)) const char *flatbuffer_version_string; +volatile __attribute__((weak)) const char *flatbuffer_version_string = + "FlatBuffers " + FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MAJOR) "." + FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MINOR) "." + FLATBUFFERS_STRING(FLATBUFFERS_VERSION_REVISION); + +#endif // !defined(_WIN32) && !defined(__CYGWIN__) + +#define FLATBUFFERS_DEFINE_BITMASK_OPERATORS(E, T)\ + inline E operator | (E lhs, E rhs){\ + return E(T(lhs) | T(rhs));\ + }\ + inline E operator & (E lhs, E rhs){\ + return E(T(lhs) & T(rhs));\ + }\ + inline E operator ^ (E lhs, E rhs){\ + return E(T(lhs) ^ T(rhs));\ + }\ + inline E operator ~ (E lhs){\ + return E(~T(lhs));\ + }\ + inline E operator |= (E &lhs, E rhs){\ + lhs = lhs | rhs;\ + return lhs;\ + }\ + inline E operator &= (E &lhs, E rhs){\ + lhs = lhs & rhs;\ + return lhs;\ + }\ + inline E operator ^= (E &lhs, E rhs){\ + lhs = lhs ^ rhs;\ + return lhs;\ + }\ + inline bool operator !(E rhs) \ + {\ + return !bool(T(rhs)); \ + } +/// @endcond +} // namespace flatbuffers + +// clang-format on + +#endif // FLATBUFFERS_H_ diff --git a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/flexbuffers.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flexbuffers.h similarity index 81% rename from code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/flexbuffers.h rename to code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flexbuffers.h index a4401f8b..a51fcc92 100644 --- a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/flexbuffers.h +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flexbuffers.h @@ -53,7 +53,7 @@ enum Type { FBT_INT = 1, FBT_UINT = 2, FBT_FLOAT = 3, - // Types above stored inline, types below store an offset. + // Types above stored inline, types below (except FBT_BOOL) store an offset. FBT_KEY = 4, FBT_STRING = 5, FBT_INDIRECT_INT = 6, @@ -81,6 +81,8 @@ enum Type { FBT_BOOL = 26, FBT_VECTOR_BOOL = 36, // To Allow the same type of conversion of type to vector type + + FBT_MAX_TYPE = 37 }; inline bool IsInline(Type t) { return t <= FBT_FLOAT || t == FBT_BOOL; } @@ -155,7 +157,8 @@ inline uint64_t ReadUInt64(const uint8_t *data, uint8_t byte_width) { // constant, which here it isn't. Test if memcpy is still faster than // the conditionals in ReadSizedScalar. Can also use inline asm. // clang-format off - #if defined(_MSC_VER) && (defined(_M_X64) || defined _M_IX86) + #if defined(_MSC_VER) && defined(_M_X64) && !defined(_M_ARM64EC) + // This is 64-bit Windows only, __movsb does not work on 32-bit Windows. uint64_t u = 0; __movsb(reinterpret_cast(&u), reinterpret_cast(data), byte_width); @@ -319,8 +322,8 @@ class FixedTypedVector : public Object { return data_ == FixedTypedVector::EmptyFixedTypedVector().data_; } - Type ElementType() { return type_; } - uint8_t size() { return len_; } + Type ElementType() const { return type_; } + uint8_t size() const { return len_; } private: Type type_; @@ -370,7 +373,7 @@ class Reference { Reference() : data_(nullptr), parent_width_(0), - byte_width_(BIT_WIDTH_8), + byte_width_(0), type_(FBT_NULL) {} Reference(const uint8_t *data, uint8_t parent_width, uint8_t byte_width, @@ -494,12 +497,17 @@ class Reference { case FBT_NULL: return 0.0; case FBT_STRING: { #if 1 +#if !defined( _MSC_VER) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnull-dereference" - // TODO(b/173239141): Patched via micro/tools/make/flexbuffers_download.sh +#endif + // See b/173239141 for additional context. Patched via + // micro/tools/make/flexbuffers_download.sh // Introduce a segfault for an unsupported code path for TFLM. return *(static_cast(nullptr)); +#if !defined( _MSC_VER) #pragma GCC diagnostic pop +#endif #else // This is the original code double d; @@ -766,6 +774,8 @@ class Reference { return false; } + friend class Verifier; + const uint8_t *data_; uint8_t parent_width_; uint8_t byte_width_; @@ -860,6 +870,7 @@ inline Reference Map::operator[](const char *key) const { case 2: comp = KeyCompare; break; case 4: comp = KeyCompare; break; case 8: comp = KeyCompare; break; + default: FLATBUFFERS_ASSERT(false); return Reference(); } auto res = std::bsearch(key, keys.data_, keys.size(), keys.byte_width_, comp); if (!res) return Reference(nullptr, 1, NullPackedType()); @@ -882,7 +893,7 @@ inline Reference GetRoot(const uint8_t *buffer, size_t size) { } inline Reference GetRoot(const std::vector &buffer) { - return GetRoot(flatbuffers::vector_data(buffer), buffer.size()); + return GetRoot(buffer.data(), buffer.size()); } // Flags that configure how the Builder behaves. @@ -910,6 +921,7 @@ class Builder FLATBUFFERS_FINAL_CLASS { BuilderFlag flags = BUILDER_FLAG_SHARE_KEYS) : buf_(initial_size), finished_(false), + has_duplicate_keys_(false), flags_(flags), force_min_bit_width_(BIT_WIDTH_8), key_pool(KeyOffsetCompare(buf_)), @@ -917,6 +929,11 @@ class Builder FLATBUFFERS_FINAL_CLASS { buf_.clear(); } +#ifdef FLATBUFFERS_DEFAULT_DECLARATION + Builder(Builder &&) = default; + Builder &operator=(Builder &&) = default; +#endif + /// @brief Get the serialized buffer (after you call `Finish()`). /// @return Returns a vector owned by this class. const std::vector &GetBuffer() const { @@ -1072,7 +1089,16 @@ class Builder FLATBUFFERS_FINAL_CLASS { return CreateBlob(data, len, 0, FBT_BLOB); } size_t Blob(const std::vector &v) { - return CreateBlob(flatbuffers::vector_data(v), v.size(), 0, FBT_BLOB); + return CreateBlob(v.data(), v.size(), 0, FBT_BLOB); + } + + void Blob(const char *key, const void *data, size_t len) { + Key(key); + Blob(data, len); + } + void Blob(const char *key, const std::vector &v) { + Key(key); + Blob(v); } // TODO(wvo): support all the FlexBuffer types (like flexbuffers::String), @@ -1090,7 +1116,7 @@ class Builder FLATBUFFERS_FINAL_CLASS { return stack_.size(); } - // TODO(wvo): allow this to specify an aligment greater than the natural + // TODO(wvo): allow this to specify an alignment greater than the natural // alignment. size_t EndVector(size_t start, bool typed, bool fixed) { auto vec = CreateVector(start, stack_.size() - start, 1, typed, fixed); @@ -1125,23 +1151,24 @@ class Builder FLATBUFFERS_FINAL_CLASS { // step automatically when appliccable, and encourage people to write in // sorted fashion. // std::sort is typically already a lot faster on sorted data though. - auto dict = - reinterpret_cast(flatbuffers::vector_data(stack_) + start); - std::sort(dict, dict + len, - [&](const TwoValue &a, const TwoValue &b) -> bool { - auto as = reinterpret_cast( - flatbuffers::vector_data(buf_) + a.key.u_); - auto bs = reinterpret_cast( - flatbuffers::vector_data(buf_) + b.key.u_); - auto comp = strcmp(as, bs); - // If this assertion hits, you've added two keys with the same - // value to this map. - // TODO: Have to check for pointer equality, as some sort - // implementation apparently call this function with the same - // element?? Why? - FLATBUFFERS_ASSERT(comp || &a == &b); - return comp < 0; - }); + auto dict = reinterpret_cast(stack_.data() + start); + std::sort( + dict, dict + len, [&](const TwoValue &a, const TwoValue &b) -> bool { + auto as = reinterpret_cast(buf_.data() + a.key.u_); + auto bs = reinterpret_cast(buf_.data() + b.key.u_); + auto comp = strcmp(as, bs); + // We want to disallow duplicate keys, since this results in a + // map where values cannot be found. + // But we can't assert here (since we don't want to fail on + // random JSON input) or have an error mechanism. + // Instead, we set has_duplicate_keys_ in the builder to + // signal this. + // TODO: Have to check for pointer equality, as some sort + // implementation apparently call this function with the same + // element?? Why? + if (!comp && &a != &b) has_duplicate_keys_ = true; + return comp < 0; + }); // First create a vector out of all keys. // TODO(wvo): if kBuilderFlagShareKeyVectors is true, see if we can share // the first vector. @@ -1153,6 +1180,10 @@ class Builder FLATBUFFERS_FINAL_CLASS { return static_cast(vec.u_); } + // Call this after EndMap to see if the map had any duplicate keys. + // Any map with such keys won't be able to retrieve all values. + bool HasDuplicateKeys() const { return has_duplicate_keys_; } + template size_t Vector(F f) { auto start = StartVector(); f(); @@ -1191,7 +1222,7 @@ class Builder FLATBUFFERS_FINAL_CLASS { Vector(elems, len); } template void Vector(const std::vector &vec) { - Vector(flatbuffers::vector_data(vec), vec.size()); + Vector(vec.data(), vec.size()); } template size_t TypedVector(F f) { @@ -1393,12 +1424,10 @@ class Builder FLATBUFFERS_FINAL_CLASS { template static Type GetScalarType() { static_assert(flatbuffers::is_scalar::value, "Unrelated types"); - return flatbuffers::is_floating_point::value - ? FBT_FLOAT - : flatbuffers::is_same::value - ? FBT_BOOL - : (flatbuffers::is_unsigned::value ? FBT_UINT - : FBT_INT); + return flatbuffers::is_floating_point::value ? FBT_FLOAT + : flatbuffers::is_same::value + ? FBT_BOOL + : (flatbuffers::is_unsigned::value ? FBT_UINT : FBT_INT); } public: @@ -1548,9 +1577,9 @@ class Builder FLATBUFFERS_FINAL_CLASS { } } } - // If you get this assert, your fixed types are not one of: + // If you get this assert, your typed types are not one of: // Int / UInt / Float / Key. - FLATBUFFERS_ASSERT(!fixed || IsTypedVectorElementType(vector_type)); + FLATBUFFERS_ASSERT(!typed || IsTypedVectorElementType(vector_type)); auto byte_width = Align(bit_width); // Write vector. First the keys width/offset if available, and size. if (keys) { @@ -1584,6 +1613,7 @@ class Builder FLATBUFFERS_FINAL_CLASS { std::vector stack_; bool finished_; + bool has_duplicate_keys_; BuilderFlag flags_; @@ -1592,10 +1622,8 @@ class Builder FLATBUFFERS_FINAL_CLASS { struct KeyOffsetCompare { explicit KeyOffsetCompare(const std::vector &buf) : buf_(&buf) {} bool operator()(size_t a, size_t b) const { - auto stra = - reinterpret_cast(flatbuffers::vector_data(*buf_) + a); - auto strb = - reinterpret_cast(flatbuffers::vector_data(*buf_) + b); + auto stra = reinterpret_cast(buf_->data() + a); + auto strb = reinterpret_cast(buf_->data() + b); return strcmp(stra, strb) < 0; } const std::vector *buf_; @@ -1606,11 +1634,10 @@ class Builder FLATBUFFERS_FINAL_CLASS { explicit StringOffsetCompare(const std::vector &buf) : buf_(&buf) {} bool operator()(const StringOffset &a, const StringOffset &b) const { - auto stra = reinterpret_cast( - flatbuffers::vector_data(*buf_) + a.first); - auto strb = reinterpret_cast( - flatbuffers::vector_data(*buf_) + b.first); - return strncmp(stra, strb, (std::min)(a.second, b.second) + 1) < 0; + auto stra = buf_->data() + a.first; + auto strb = buf_->data() + b.first; + auto cr = memcmp(stra, strb, (std::min)(a.second, b.second) + 1); + return cr < 0 || (cr == 0 && a.second < b.second); } const std::vector *buf_; }; @@ -1620,8 +1647,270 @@ class Builder FLATBUFFERS_FINAL_CLASS { KeyOffsetMap key_pool; StringOffsetMap string_pool; + + friend class Verifier; }; +// Helper class to verify the integrity of a FlexBuffer +class Verifier FLATBUFFERS_FINAL_CLASS { + public: + Verifier(const uint8_t *buf, size_t buf_len, + // Supplying this vector likely results in faster verification + // of larger buffers with many shared keys/strings, but + // comes at the cost of using additional memory the same size of + // the buffer being verified, so it is by default off. + std::vector *reuse_tracker = nullptr, + bool _check_alignment = true, + size_t max_depth = 64) + : buf_(buf), + size_(buf_len), + depth_(0), + max_depth_(max_depth), + num_vectors_(0), + max_vectors_(buf_len), + check_alignment_(_check_alignment), + reuse_tracker_(reuse_tracker) { + FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE); + if (reuse_tracker_) { + reuse_tracker_->clear(); + reuse_tracker_->resize(size_, PackedType(BIT_WIDTH_8, FBT_NULL)); + } + } + + private: + // Central location where any verification failures register. + bool Check(bool ok) const { + // clang-format off + #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE + FLATBUFFERS_ASSERT(ok); + #endif + // clang-format on + return ok; + } + + // Verify any range within the buffer. + bool VerifyFrom(size_t elem, size_t elem_len) const { + return Check(elem_len < size_ && elem <= size_ - elem_len); + } + bool VerifyBefore(size_t elem, size_t elem_len) const { + return Check(elem_len <= elem); + } + + bool VerifyFromPointer(const uint8_t *p, size_t len) { + auto o = static_cast(p - buf_); + return VerifyFrom(o, len); + } + bool VerifyBeforePointer(const uint8_t *p, size_t len) { + auto o = static_cast(p - buf_); + return VerifyBefore(o, len); + } + + bool VerifyByteWidth(size_t width) { + return Check(width == 1 || width == 2 || width == 4 || width == 8); + } + + bool VerifyType(int type) { + return Check(type >= 0 && type < FBT_MAX_TYPE); + } + + bool VerifyOffset(uint64_t off, const uint8_t *p) { + return Check(off <= static_cast(size_)) && + off <= static_cast(p - buf_); + } + + bool VerifyAlignment(const uint8_t *p, size_t size) const { + auto o = static_cast(p - buf_); + return Check((o & (size - 1)) == 0 || !check_alignment_); + } + + // Macro, since we want to escape from parent function & use lazy args. + #define FLEX_CHECK_VERIFIED(P, PACKED_TYPE) \ + if (reuse_tracker_) { \ + auto packed_type = PACKED_TYPE; \ + auto existing = (*reuse_tracker_)[P - buf_]; \ + if (existing == packed_type) return true; \ + /* Fail verification if already set with different type! */ \ + if (!Check(existing == 0)) return false; \ + (*reuse_tracker_)[P - buf_] = packed_type; \ + } + + bool VerifyVector(Reference r, const uint8_t *p, Type elem_type) { + // Any kind of nesting goes thru this function, so guard against that + // here, both with simple nesting checks, and the reuse tracker if on. + depth_++; + num_vectors_++; + if (!Check(depth_ <= max_depth_ && num_vectors_ <= max_vectors_)) + return false; + auto size_byte_width = r.byte_width_; + FLEX_CHECK_VERIFIED(p, PackedType(Builder::WidthB(size_byte_width), r.type_)); + if (!VerifyBeforePointer(p, size_byte_width)) + return false; + auto sized = Sized(p, size_byte_width); + auto num_elems = sized.size(); + auto elem_byte_width = + r.type_ == FBT_STRING || r.type_ == FBT_BLOB ? uint8_t(1) : r.byte_width_; + auto max_elems = SIZE_MAX / elem_byte_width; + if (!Check(num_elems < max_elems)) + return false; // Protect against byte_size overflowing. + auto byte_size = num_elems * elem_byte_width; + if (!VerifyFromPointer(p, byte_size)) + return false; + if (elem_type == FBT_NULL) { + // Verify type bytes after the vector. + if (!VerifyFromPointer(p + byte_size, num_elems)) return false; + auto v = Vector(p, size_byte_width); + for (size_t i = 0; i < num_elems; i++) + if (!VerifyRef(v[i])) return false; + } else if (elem_type == FBT_KEY) { + auto v = TypedVector(p, elem_byte_width, FBT_KEY); + for (size_t i = 0; i < num_elems; i++) + if (!VerifyRef(v[i])) return false; + } else { + FLATBUFFERS_ASSERT(IsInline(elem_type)); + } + depth_--; + return true; + } + + bool VerifyKeys(const uint8_t *p, uint8_t byte_width) { + // The vector part of the map has already been verified. + const size_t num_prefixed_fields = 3; + if (!VerifyBeforePointer(p, byte_width * num_prefixed_fields)) + return false; + p -= byte_width * num_prefixed_fields; + auto off = ReadUInt64(p, byte_width); + if (!VerifyOffset(off, p)) + return false; + auto key_byte_with = + static_cast(ReadUInt64(p + byte_width, byte_width)); + if (!VerifyByteWidth(key_byte_with)) + return false; + return VerifyVector(Reference(p, byte_width, key_byte_with, FBT_VECTOR_KEY), + p - off, FBT_KEY); + } + + bool VerifyKey(const uint8_t* p) { + FLEX_CHECK_VERIFIED(p, PackedType(BIT_WIDTH_8, FBT_KEY)); + while (p < buf_ + size_) + if (*p++) return true; + return false; + } + + #undef FLEX_CHECK_VERIFIED + + bool VerifyTerminator(const String &s) { + return VerifyFromPointer(reinterpret_cast(s.c_str()), + s.size() + 1); + } + + bool VerifyRef(Reference r) { + // r.parent_width_ and r.data_ already verified. + if (!VerifyByteWidth(r.byte_width_) || !VerifyType(r.type_)) { + return false; + } + if (IsInline(r.type_)) { + // Inline scalars, don't require further verification. + return true; + } + // All remaining types are an offset. + auto off = ReadUInt64(r.data_, r.parent_width_); + if (!VerifyOffset(off, r.data_)) + return false; + auto p = r.Indirect(); + if (!VerifyAlignment(p, r.byte_width_)) + return false; + switch (r.type_) { + case FBT_INDIRECT_INT: + case FBT_INDIRECT_UINT: + case FBT_INDIRECT_FLOAT: + return VerifyFromPointer(p, r.byte_width_); + case FBT_KEY: + return VerifyKey(p); + case FBT_MAP: + return VerifyVector(r, p, FBT_NULL) && + VerifyKeys(p, r.byte_width_); + case FBT_VECTOR: + return VerifyVector(r, p, FBT_NULL); + case FBT_VECTOR_INT: + return VerifyVector(r, p, FBT_INT); + case FBT_VECTOR_BOOL: + case FBT_VECTOR_UINT: + return VerifyVector(r, p, FBT_UINT); + case FBT_VECTOR_FLOAT: + return VerifyVector(r, p, FBT_FLOAT); + case FBT_VECTOR_KEY: + return VerifyVector(r, p, FBT_KEY); + case FBT_VECTOR_STRING_DEPRECATED: + // Use of FBT_KEY here intentional, see elsewhere. + return VerifyVector(r, p, FBT_KEY); + case FBT_BLOB: + return VerifyVector(r, p, FBT_UINT); + case FBT_STRING: + return VerifyVector(r, p, FBT_UINT) && + VerifyTerminator(String(p, r.byte_width_)); + case FBT_VECTOR_INT2: + case FBT_VECTOR_UINT2: + case FBT_VECTOR_FLOAT2: + case FBT_VECTOR_INT3: + case FBT_VECTOR_UINT3: + case FBT_VECTOR_FLOAT3: + case FBT_VECTOR_INT4: + case FBT_VECTOR_UINT4: + case FBT_VECTOR_FLOAT4: { + uint8_t len = 0; + auto vtype = ToFixedTypedVectorElementType(r.type_, &len); + if (!VerifyType(vtype)) + return false; + return VerifyFromPointer(p, r.byte_width_ * len); + } + default: + return false; + } + } + + public: + bool VerifyBuffer() { + if (!Check(size_ >= 3)) return false; + auto end = buf_ + size_; + auto byte_width = *--end; + auto packed_type = *--end; + return VerifyByteWidth(byte_width) && + Check(end - buf_ >= byte_width) && + VerifyRef(Reference(end - byte_width, byte_width, packed_type)); + } + + private: + const uint8_t *buf_; + size_t size_; + size_t depth_; + const size_t max_depth_; + size_t num_vectors_; + const size_t max_vectors_; + bool check_alignment_; + std::vector *reuse_tracker_; +}; + +// Utility function that contructs the Verifier for you, see above for parameters. +inline bool VerifyBuffer(const uint8_t *buf, size_t buf_len, + std::vector *reuse_tracker = nullptr) { + Verifier verifier(buf, buf_len, reuse_tracker); + return verifier.VerifyBuffer(); +} + + +#ifdef FLATBUFFERS_H_ +// This is a verifier utility function that works together with the +// FlatBuffers verifier, which should only be present if flatbuffer.h +// has been included (which it typically is in generated code). +inline bool VerifyNestedFlexBuffer(const flatbuffers::Vector *nv, + flatbuffers::Verifier &verifier) { + if (!nv) return true; + return verifier.Check( + flexbuffers::VerifyBuffer(nv->data(), nv->size(), + verifier.GetFlexReuseTracker())); +} +#endif + } // namespace flexbuffers #if defined(_MSC_VER) diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/stl_emulation.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/stl_emulation.h new file mode 100644 index 00000000..75d13b29 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/stl_emulation.h @@ -0,0 +1,509 @@ +/* + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_STL_EMULATION_H_ +#define FLATBUFFERS_STL_EMULATION_H_ + +// clang-format off +#include "flatbuffers/base.h" + +#include +#include +#include +#include +#include + +// Detect C++17 compatible compiler. +// __cplusplus >= 201703L - a compiler has support of 'static inline' variables. +#if defined(FLATBUFFERS_USE_STD_OPTIONAL) \ + || (defined(__cplusplus) && __cplusplus >= 201703L) \ + || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)) + #include + #ifndef FLATBUFFERS_USE_STD_OPTIONAL + #define FLATBUFFERS_USE_STD_OPTIONAL + #endif +#endif // defined(FLATBUFFERS_USE_STD_OPTIONAL) ... + +// The __cpp_lib_span is the predefined feature macro. +#if defined(FLATBUFFERS_USE_STD_SPAN) + #include +#elif defined(__cpp_lib_span) && defined(__has_include) + #if __has_include() + #include + #define FLATBUFFERS_USE_STD_SPAN + #endif +#else + // Disable non-trivial ctors if FLATBUFFERS_SPAN_MINIMAL defined. + #if !defined(FLATBUFFERS_TEMPLATES_ALIASES) + #define FLATBUFFERS_SPAN_MINIMAL + #else + // Enable implicit construction of a span from a std::array. + #include + #endif +#endif // defined(FLATBUFFERS_USE_STD_SPAN) + +// This header provides backwards compatibility for older versions of the STL. +namespace flatbuffers { + +#if defined(FLATBUFFERS_TEMPLATES_ALIASES) + template + using numeric_limits = std::numeric_limits; +#else + template class numeric_limits : + public std::numeric_limits {}; +#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) + +#if defined(FLATBUFFERS_TEMPLATES_ALIASES) + template using is_scalar = std::is_scalar; + template using is_same = std::is_same; + template using is_floating_point = std::is_floating_point; + template using is_unsigned = std::is_unsigned; + template using is_enum = std::is_enum; + template using make_unsigned = std::make_unsigned; + template + using conditional = std::conditional; + template + using integral_constant = std::integral_constant; + template + using bool_constant = integral_constant; + using true_type = std::true_type; + using false_type = std::false_type; +#else + // MSVC 2010 doesn't support C++11 aliases. + template struct is_scalar : public std::is_scalar {}; + template struct is_same : public std::is_same {}; + template struct is_floating_point : + public std::is_floating_point {}; + template struct is_unsigned : public std::is_unsigned {}; + template struct is_enum : public std::is_enum {}; + template struct make_unsigned : public std::make_unsigned {}; + template + struct conditional : public std::conditional {}; + template + struct integral_constant : public std::integral_constant {}; + template + struct bool_constant : public integral_constant {}; + typedef bool_constant true_type; + typedef bool_constant false_type; +#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) + +#if defined(FLATBUFFERS_TEMPLATES_ALIASES) + template using unique_ptr = std::unique_ptr; +#else + // MSVC 2010 doesn't support C++11 aliases. + // We're manually "aliasing" the class here as we want to bring unique_ptr + // into the flatbuffers namespace. We have unique_ptr in the flatbuffers + // namespace we have a completely independent implementation (see below) + // for C++98 STL implementations. + template class unique_ptr : public std::unique_ptr { + public: + unique_ptr() {} + explicit unique_ptr(T* p) : std::unique_ptr(p) {} + unique_ptr(std::unique_ptr&& u) { *this = std::move(u); } + unique_ptr(unique_ptr&& u) { *this = std::move(u); } + unique_ptr& operator=(std::unique_ptr&& u) { + std::unique_ptr::reset(u.release()); + return *this; + } + unique_ptr& operator=(unique_ptr&& u) { + std::unique_ptr::reset(u.release()); + return *this; + } + unique_ptr& operator=(T* p) { + return std::unique_ptr::operator=(p); + } + }; +#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) + +#ifdef FLATBUFFERS_USE_STD_OPTIONAL +template +using Optional = std::optional; +using nullopt_t = std::nullopt_t; +inline constexpr nullopt_t nullopt = std::nullopt; + +#else +// Limited implementation of Optional type for a scalar T. +// This implementation limited by trivial types compatible with +// std::is_arithmetic or std::is_enum type traits. + +// A tag to indicate an empty flatbuffers::optional. +struct nullopt_t { + explicit FLATBUFFERS_CONSTEXPR_CPP11 nullopt_t(int) {} +}; + +#if defined(FLATBUFFERS_CONSTEXPR_DEFINED) + namespace internal { + template struct nullopt_holder { + static constexpr nullopt_t instance_ = nullopt_t(0); + }; + template + constexpr nullopt_t nullopt_holder::instance_; + } + static constexpr const nullopt_t &nullopt = internal::nullopt_holder::instance_; + +#else + namespace internal { + template struct nullopt_holder { + static const nullopt_t instance_; + }; + template + const nullopt_t nullopt_holder::instance_ = nullopt_t(0); + } + static const nullopt_t &nullopt = internal::nullopt_holder::instance_; + +#endif + +template +class Optional FLATBUFFERS_FINAL_CLASS { + // Non-scalar 'T' would extremely complicated Optional. + // Use is_scalar checking because flatbuffers flatbuffers::is_arithmetic + // isn't implemented. + static_assert(flatbuffers::is_scalar::value, "unexpected type T"); + + public: + ~Optional() {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional() FLATBUFFERS_NOEXCEPT + : value_(), has_value_(false) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(nullopt_t) FLATBUFFERS_NOEXCEPT + : value_(), has_value_(false) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(T val) FLATBUFFERS_NOEXCEPT + : value_(val), has_value_(true) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(const Optional &other) FLATBUFFERS_NOEXCEPT + : value_(other.value_), has_value_(other.has_value_) {} + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(const Optional &other) FLATBUFFERS_NOEXCEPT { + value_ = other.value_; + has_value_ = other.has_value_; + return *this; + } + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(nullopt_t) FLATBUFFERS_NOEXCEPT { + value_ = T(); + has_value_ = false; + return *this; + } + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(T val) FLATBUFFERS_NOEXCEPT { + value_ = val; + has_value_ = true; + return *this; + } + + void reset() FLATBUFFERS_NOEXCEPT { + *this = nullopt; + } + + void swap(Optional &other) FLATBUFFERS_NOEXCEPT { + std::swap(value_, other.value_); + std::swap(has_value_, other.has_value_); + } + + FLATBUFFERS_CONSTEXPR_CPP11 FLATBUFFERS_EXPLICIT_CPP11 operator bool() const FLATBUFFERS_NOEXCEPT { + return has_value_; + } + + FLATBUFFERS_CONSTEXPR_CPP11 bool has_value() const FLATBUFFERS_NOEXCEPT { + return has_value_; + } + + FLATBUFFERS_CONSTEXPR_CPP11 const T& operator*() const FLATBUFFERS_NOEXCEPT { + return value_; + } + + const T& value() const { + FLATBUFFERS_ASSERT(has_value()); + return value_; + } + + T value_or(T default_value) const FLATBUFFERS_NOEXCEPT { + return has_value() ? value_ : default_value; + } + + private: + T value_; + bool has_value_; +}; + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& opt, nullopt_t) FLATBUFFERS_NOEXCEPT { + return !opt; +} +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(nullopt_t, const Optional& opt) FLATBUFFERS_NOEXCEPT { + return !opt; +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& lhs, const U& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(lhs) && (*lhs == rhs); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const T& lhs, const Optional& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(rhs) && (lhs == *rhs); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& lhs, const Optional& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(lhs) != static_cast(rhs) + ? false + : !static_cast(lhs) ? false : (*lhs == *rhs); +} +#endif // FLATBUFFERS_USE_STD_OPTIONAL + + +// Very limited and naive partial implementation of C++20 std::span. +#if defined(FLATBUFFERS_USE_STD_SPAN) + inline constexpr std::size_t dynamic_extent = std::dynamic_extent; + template + using span = std::span; + +#else // !defined(FLATBUFFERS_USE_STD_SPAN) +FLATBUFFERS_CONSTEXPR std::size_t dynamic_extent = static_cast(-1); + +// Exclude this code if MSVC2010 or non-STL Android is active. +// The non-STL Android doesn't have `std::is_convertible` required for SFINAE. +#if !defined(FLATBUFFERS_SPAN_MINIMAL) +namespace internal { + // This is SFINAE helper class for checking of a common condition: + // > This overload only participates in overload resolution + // > Check whether a pointer to an array of U can be converted + // > to a pointer to an array of E. + // This helper is used for checking of 'U -> const U'. + template + struct is_span_convertable { + using type = + typename std::conditional::value + && (Extent == dynamic_extent || N == Extent), + int, void>::type; + }; + + template + struct SpanIterator { + // TODO: upgrade to std::random_access_iterator_tag. + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = typename std::remove_cv::type; + using reference = T&; + using pointer = T*; + + // Convince MSVC compiler that this iterator is trusted (it is verified). + #ifdef _MSC_VER + using _Unchecked_type = pointer; + #endif // _MSC_VER + + SpanIterator(pointer ptr) : ptr_(ptr) {} + reference operator*() const { return *ptr_; } + pointer operator->() { return ptr_; } + SpanIterator& operator++() { ptr_++; return *this; } + SpanIterator operator++(int) { auto tmp = *this; ++(*this); return tmp; } + + friend bool operator== (const SpanIterator& lhs, const SpanIterator& rhs) { return lhs.ptr_ == rhs.ptr_; } + friend bool operator!= (const SpanIterator& lhs, const SpanIterator& rhs) { return lhs.ptr_ != rhs.ptr_; } + + private: + pointer ptr_; + }; +} // namespace internal +#endif // !defined(FLATBUFFERS_SPAN_MINIMAL) + +// T - element type; must be a complete type that is not an abstract +// class type. +// Extent - the number of elements in the sequence, or dynamic. +template +class span FLATBUFFERS_FINAL_CLASS { + public: + typedef T element_type; + typedef T& reference; + typedef const T& const_reference; + typedef T* pointer; + typedef const T* const_pointer; + typedef std::size_t size_type; + + static FLATBUFFERS_CONSTEXPR size_type extent = Extent; + + // Returns the number of elements in the span. + FLATBUFFERS_CONSTEXPR_CPP11 size_type size() const FLATBUFFERS_NOEXCEPT { + return count_; + } + + // Returns the size of the sequence in bytes. + FLATBUFFERS_CONSTEXPR_CPP11 + size_type size_bytes() const FLATBUFFERS_NOEXCEPT { + return size() * sizeof(element_type); + } + + // Checks if the span is empty. + FLATBUFFERS_CONSTEXPR_CPP11 bool empty() const FLATBUFFERS_NOEXCEPT { + return size() == 0; + } + + // Returns a pointer to the beginning of the sequence. + FLATBUFFERS_CONSTEXPR_CPP11 pointer data() const FLATBUFFERS_NOEXCEPT { + return data_; + } + + #if !defined(FLATBUFFERS_SPAN_MINIMAL) + using Iterator = internal::SpanIterator; + using ConstIterator = internal::SpanIterator; + + Iterator begin() const { return Iterator(data()); } + Iterator end() const { return Iterator(data() + size()); } + + ConstIterator cbegin() const { return ConstIterator(data()); } + ConstIterator cend() const { return ConstIterator(data() + size()); } + #endif + + // Returns a reference to the idx-th element of the sequence. + // The behavior is undefined if the idx is greater than or equal to size(). + FLATBUFFERS_CONSTEXPR_CPP11 reference operator[](size_type idx) const { + return data()[idx]; + } + + FLATBUFFERS_CONSTEXPR_CPP11 span(const span &other) FLATBUFFERS_NOEXCEPT + : data_(other.data_), count_(other.count_) {} + + FLATBUFFERS_CONSTEXPR_CPP14 span &operator=(const span &other) + FLATBUFFERS_NOEXCEPT { + data_ = other.data_; + count_ = other.count_; + } + + // Limited implementation of + // `template constexpr std::span(It first, size_type count);`. + // + // Constructs a span that is a view over the range [first, first + count); + // the resulting span has: data() == first and size() == count. + // The behavior is undefined if [first, first + count) is not a valid range, + // or if (extent != flatbuffers::dynamic_extent && count != extent). + FLATBUFFERS_CONSTEXPR_CPP11 + explicit span(pointer first, size_type count) FLATBUFFERS_NOEXCEPT + : data_ (Extent == dynamic_extent ? first : (Extent == count ? first : nullptr)), + count_(Extent == dynamic_extent ? count : (Extent == count ? Extent : 0)) { + // Make span empty if the count argument is incompatible with span. + } + + // Exclude this code if MSVC2010 is active. The MSVC2010 isn't C++11 + // compliant, it doesn't support default template arguments for functions. + #if defined(FLATBUFFERS_SPAN_MINIMAL) + FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr), + count_(0) { + static_assert(extent == 0 || extent == dynamic_extent, "invalid span"); + } + + #else + // Constructs an empty span whose data() == nullptr and size() == 0. + // This overload only participates in overload resolution if + // extent == 0 || extent == flatbuffers::dynamic_extent. + // A dummy template argument N is need dependency for SFINAE. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr), + count_(0) { + static_assert(extent == 0 || extent == dynamic_extent, "invalid span"); + } + + // Constructs a span that is a view over the array arr; the resulting span + // has size() == N and data() == std::data(arr). These overloads only + // participate in overload resolution if + // extent == std::dynamic_extent || N == extent is true and + // std::remove_pointer_t(*)[] + // is convertible to element_type (*)[]. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(element_type (&arr)[N]) FLATBUFFERS_NOEXCEPT + : data_(arr), count_(N) {} + + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(std::array &arr) FLATBUFFERS_NOEXCEPT + : data_(arr.data()), count_(N) {} + + //template + //FLATBUFFERS_CONSTEXPR_CPP11 span(std::array &arr) FLATBUFFERS_NOEXCEPT + // : data_(arr.data()), count_(N) {} + + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(const std::array &arr) FLATBUFFERS_NOEXCEPT + : data_(arr.data()), count_(N) {} + + // Converting constructor from another span s; + // the resulting span has size() == s.size() and data() == s.data(). + // This overload only participates in overload resolution + // if extent == std::dynamic_extent || N == extent is true and U (*)[] + // is convertible to element_type (*)[]. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(const flatbuffers::span &s) FLATBUFFERS_NOEXCEPT + : span(s.data(), s.size()) { + } + + #endif // !defined(FLATBUFFERS_SPAN_MINIMAL) + + private: + // This is a naive implementation with 'count_' member even if (Extent != dynamic_extent). + pointer const data_; + const size_type count_; +}; +#endif // defined(FLATBUFFERS_USE_STD_SPAN) + +#if !defined(FLATBUFFERS_SPAN_MINIMAL) +template +FLATBUFFERS_CONSTEXPR_CPP11 +flatbuffers::span make_span(U(&arr)[N]) FLATBUFFERS_NOEXCEPT { + return span(arr); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 +flatbuffers::span make_span(const U(&arr)[N]) FLATBUFFERS_NOEXCEPT { + return span(arr); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 +flatbuffers::span make_span(std::array &arr) FLATBUFFERS_NOEXCEPT { + return span(arr); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 +flatbuffers::span make_span(const std::array &arr) FLATBUFFERS_NOEXCEPT { + return span(arr); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 +flatbuffers::span make_span(U *first, std::size_t count) FLATBUFFERS_NOEXCEPT { + return span(first, count); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 +flatbuffers::span make_span(const U *first, std::size_t count) FLATBUFFERS_NOEXCEPT { + return span(first, count); +} +#endif // !defined(FLATBUFFERS_SPAN_MINIMAL) + +} // namespace flatbuffers + +#endif // FLATBUFFERS_STL_EMULATION_H_ diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/string.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/string.h new file mode 100644 index 00000000..3db95fce --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/string.h @@ -0,0 +1,64 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_STRING_H_ +#define FLATBUFFERS_STRING_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/vector.h" + +namespace flatbuffers { + +struct String : public Vector { + const char *c_str() const { return reinterpret_cast(Data()); } + std::string str() const { return std::string(c_str(), size()); } + + // clang-format off + #ifdef FLATBUFFERS_HAS_STRING_VIEW + flatbuffers::string_view string_view() const { + return flatbuffers::string_view(c_str(), size()); + } + #endif // FLATBUFFERS_HAS_STRING_VIEW + // clang-format on + + bool operator<(const String &o) const { + return StringLessThan(this->data(), this->size(), o.data(), o.size()); + } +}; + +// Convenience function to get std::string from a String returning an empty +// string on null pointer. +static inline std::string GetString(const String *str) { + return str ? str->str() : ""; +} + +// Convenience function to get char* from a String returning an empty string on +// null pointer. +static inline const char *GetCstring(const String *str) { + return str ? str->c_str() : ""; +} + +#ifdef FLATBUFFERS_HAS_STRING_VIEW +// Convenience function to get string_view from a String returning an empty +// string_view on null pointer. +static inline flatbuffers::string_view GetStringView(const String *str) { + return str ? str->string_view() : flatbuffers::string_view(); +} +#endif // FLATBUFFERS_HAS_STRING_VIEW + +} // namespace flatbuffers + +#endif // FLATBUFFERS_STRING_H_ \ No newline at end of file diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/struct.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/struct.h new file mode 100644 index 00000000..d8753c84 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/struct.h @@ -0,0 +1,53 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_STRUCT_H_ +#define FLATBUFFERS_STRUCT_H_ + +#include "flatbuffers/base.h" + +namespace flatbuffers { + +// "structs" are flat structures that do not have an offset table, thus +// always have all members present and do not support forwards/backwards +// compatible extensions. + +class Struct FLATBUFFERS_FINAL_CLASS { + public: + template T GetField(uoffset_t o) const { + return ReadScalar(&data_[o]); + } + + template T GetStruct(uoffset_t o) const { + return reinterpret_cast(&data_[o]); + } + + const uint8_t *GetAddressOf(uoffset_t o) const { return &data_[o]; } + uint8_t *GetAddressOf(uoffset_t o) { return &data_[o]; } + + private: + // private constructor & copy constructor: you obtain instances of this + // class by pointing to existing data only + Struct(); + Struct(const Struct &); + Struct &operator=(const Struct &); + + uint8_t data_[1]; +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_STRUCT_H_ \ No newline at end of file diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/table.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/table.h new file mode 100644 index 00000000..42470693 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/table.h @@ -0,0 +1,166 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_TABLE_H_ +#define FLATBUFFERS_TABLE_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/verifier.h" + +namespace flatbuffers { + +// "tables" use an offset table (possibly shared) that allows fields to be +// omitted and added at will, but uses an extra indirection to read. +class Table { + public: + const uint8_t *GetVTable() const { + return data_ - ReadScalar(data_); + } + + // This gets the field offset for any of the functions below it, or 0 + // if the field was not present. + voffset_t GetOptionalFieldOffset(voffset_t field) const { + // The vtable offset is always at the start. + auto vtable = GetVTable(); + // The first element is the size of the vtable (fields + type id + itself). + auto vtsize = ReadScalar(vtable); + // If the field we're accessing is outside the vtable, we're reading older + // data, so it's the same as if the offset was 0 (not present). + return field < vtsize ? ReadScalar(vtable + field) : 0; + } + + template T GetField(voffset_t field, T defaultval) const { + auto field_offset = GetOptionalFieldOffset(field); + return field_offset ? ReadScalar(data_ + field_offset) : defaultval; + } + + template P GetPointer(voffset_t field) { + auto field_offset = GetOptionalFieldOffset(field); + auto p = data_ + field_offset; + return field_offset ? reinterpret_cast

(p + ReadScalar(p)) + : nullptr; + } + template P GetPointer(voffset_t field) const { + return const_cast(this)->GetPointer

(field); + } + + template P GetStruct(voffset_t field) const { + auto field_offset = GetOptionalFieldOffset(field); + auto p = const_cast(data_ + field_offset); + return field_offset ? reinterpret_cast

(p) : nullptr; + } + + template + flatbuffers::Optional GetOptional(voffset_t field) const { + auto field_offset = GetOptionalFieldOffset(field); + auto p = data_ + field_offset; + return field_offset ? Optional(static_cast(ReadScalar(p))) + : Optional(); + } + + template bool SetField(voffset_t field, T val, T def) { + auto field_offset = GetOptionalFieldOffset(field); + if (!field_offset) return IsTheSameAs(val, def); + WriteScalar(data_ + field_offset, val); + return true; + } + template bool SetField(voffset_t field, T val) { + auto field_offset = GetOptionalFieldOffset(field); + if (!field_offset) return false; + WriteScalar(data_ + field_offset, val); + return true; + } + + bool SetPointer(voffset_t field, const uint8_t *val) { + auto field_offset = GetOptionalFieldOffset(field); + if (!field_offset) return false; + WriteScalar(data_ + field_offset, + static_cast(val - (data_ + field_offset))); + return true; + } + + uint8_t *GetAddressOf(voffset_t field) { + auto field_offset = GetOptionalFieldOffset(field); + return field_offset ? data_ + field_offset : nullptr; + } + const uint8_t *GetAddressOf(voffset_t field) const { + return const_cast

(this)->GetAddressOf(field); + } + + bool CheckField(voffset_t field) const { + return GetOptionalFieldOffset(field) != 0; + } + + // Verify the vtable of this table. + // Call this once per table, followed by VerifyField once per field. + bool VerifyTableStart(Verifier &verifier) const { + return verifier.VerifyTableStart(data_); + } + + // Verify a particular field. + template + bool VerifyField(const Verifier &verifier, voffset_t field) const { + // Calling GetOptionalFieldOffset should be safe now thanks to + // VerifyTable(). + auto field_offset = GetOptionalFieldOffset(field); + // Check the actual field. + return !field_offset || verifier.Verify(data_, field_offset); + } + + // VerifyField for required fields. + template + bool VerifyFieldRequired(const Verifier &verifier, voffset_t field) const { + auto field_offset = GetOptionalFieldOffset(field); + return verifier.Check(field_offset != 0) && + verifier.Verify(data_, field_offset); + } + + // Versions for offsets. + bool VerifyOffset(const Verifier &verifier, voffset_t field) const { + auto field_offset = GetOptionalFieldOffset(field); + return !field_offset || verifier.VerifyOffset(data_, field_offset); + } + + bool VerifyOffsetRequired(const Verifier &verifier, voffset_t field) const { + auto field_offset = GetOptionalFieldOffset(field); + return verifier.Check(field_offset != 0) && + verifier.VerifyOffset(data_, field_offset); + } + + private: + // private constructor & copy constructor: you obtain instances of this + // class by pointing to existing data only + Table(); + Table(const Table &other); + Table &operator=(const Table &); + + uint8_t data_[1]; +}; + +// This specialization allows avoiding warnings like: +// MSVC C4800: type: forcing value to bool 'true' or 'false'. +template<> +inline flatbuffers::Optional Table::GetOptional( + voffset_t field) const { + auto field_offset = GetOptionalFieldOffset(field); + auto p = data_ + field_offset; + return field_offset ? Optional(ReadScalar(p) != 0) + : Optional(); +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_TABLE_H_ \ No newline at end of file diff --git a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/util.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/util.h similarity index 96% rename from code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/util.h rename to code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/util.h index 2aafa482..e0392a95 100644 --- a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/util.h +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/util.h @@ -18,18 +18,25 @@ #define FLATBUFFERS_UTIL_H_ #include +#include #include "flatbuffers/base.h" #include "flatbuffers/stl_emulation.h" +// For TFLM we always want to use FLATBUFFERS_PREFER_PRINTF=1. See +// http://b/211811553 for more context. +#ifndef FLATBUFFERS_PREFER_PRINTF +#define FLATBUFFERS_PREFER_PRINTF 1 +#endif + #ifndef FLATBUFFERS_PREFER_PRINTF # include +# include #else // FLATBUFFERS_PREFER_PRINTF # include # include #endif // FLATBUFFERS_PREFER_PRINTF -#include #include namespace flatbuffers { @@ -51,6 +58,9 @@ inline bool is_alpha(char c) { return check_ascii_range(c & 0xDF, 'a' & 0xDF, 'z' & 0xDF); } +// Check for uppercase alpha +inline bool is_alpha_upper(char c) { return check_ascii_range(c, 'A', 'Z'); } + // Check (case-insensitive) that `c` is equal to alpha. inline bool is_alpha_char(char c, char alpha) { FLATBUFFERS_ASSERT(is_alpha(alpha)); @@ -91,7 +101,7 @@ template size_t IntToDigitCount(T t) { // Count a single 0 left of the dot for fractional numbers if (-1 < t && t < 1) digit_count++; // Count digits until fractional part - T eps = std::numeric_limits::epsilon(); + T eps = std::numeric_limits::epsilon(); while (t <= (-1 + eps) || (1 - eps) <= t) { t /= 10; digit_count++; @@ -142,20 +152,6 @@ template<> inline std::string NumToString(unsigned char t) { template<> inline std::string NumToString(char t) { return NumToString(static_cast(t)); } -#if defined(FLATBUFFERS_CPP98_STL) -template<> inline std::string NumToString(long long t) { - char buf[21]; // (log((1 << 63) - 1) / log(10)) + 2 - snprintf(buf, sizeof(buf), "%lld", t); - return std::string(buf); -} - -template<> -inline std::string NumToString(unsigned long long t) { - char buf[22]; // (log((1 << 63) - 1) / log(10)) + 1 - snprintf(buf, sizeof(buf), "%llu", t); - return std::string(buf); -} -#endif // defined(FLATBUFFERS_CPP98_STL) // Special versions for floats/doubles. template std::string FloatToString(T t, int precision) { @@ -332,6 +328,9 @@ inline bool StringToFloatImpl(T *val, const char *const str) { // - If the converted value falls out of range of corresponding return type, a // range error occurs. In this case value MAX(T)/MIN(T) is returned. template inline bool StringToNumber(const char *s, T *val) { + // Assert on `unsigned long` and `signed long` on LP64. + // If it is necessary, it could be solved with flatbuffers::enable_if. + static_assert(sizeof(T) < sizeof(int64_t), "unexpected type T"); FLATBUFFERS_ASSERT(s && val); int64_t i64; // The errno check isn't needed, will return MAX/MIN on overflow. @@ -462,6 +461,7 @@ std::string ConCatPathFileName(const std::string &path, // Replaces any '\\' separators with '/' std::string PosixPath(const char *path); +std::string PosixPath(const std::string &path); // This function ensure a directory exists, by recursively // creating dirs for any parts of the path that don't exist yet. @@ -471,6 +471,10 @@ void EnsureDirExists(const std::string &filepath); // Returns the input path if the absolute path couldn't be resolved. std::string AbsolutePath(const std::string &filepath); +// Returns files relative to the --project_root path, prefixed with `//`. +std::string RelativeToRootPath(const std::string &project, + const std::string &filepath); + // To and from UTF-8 unicode conversion functions // Convert a unicode code point into a UTF-8 representation by appending it diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/vector.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/vector.h new file mode 100644 index 00000000..f8a5d88e --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/vector.h @@ -0,0 +1,370 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_VECTOR_H_ +#define FLATBUFFERS_VECTOR_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/buffer.h" + +namespace flatbuffers { + +struct String; + +// An STL compatible iterator implementation for Vector below, effectively +// calling Get() for every element. +template struct VectorIterator { + typedef std::random_access_iterator_tag iterator_category; + typedef IT value_type; + typedef ptrdiff_t difference_type; + typedef IT *pointer; + typedef IT &reference; + + VectorIterator(const uint8_t *data, uoffset_t i) + : data_(data + IndirectHelper::element_stride * i) {} + VectorIterator(const VectorIterator &other) : data_(other.data_) {} + VectorIterator() : data_(nullptr) {} + + VectorIterator &operator=(const VectorIterator &other) { + data_ = other.data_; + return *this; + } + + VectorIterator &operator=(VectorIterator &&other) { + data_ = other.data_; + return *this; + } + + bool operator==(const VectorIterator &other) const { + return data_ == other.data_; + } + + bool operator<(const VectorIterator &other) const { + return data_ < other.data_; + } + + bool operator!=(const VectorIterator &other) const { + return data_ != other.data_; + } + + difference_type operator-(const VectorIterator &other) const { + return (data_ - other.data_) / IndirectHelper::element_stride; + } + + // Note: return type is incompatible with the standard + // `reference operator*()`. + IT operator*() const { return IndirectHelper::Read(data_, 0); } + + // Note: return type is incompatible with the standard + // `pointer operator->()`. + IT operator->() const { return IndirectHelper::Read(data_, 0); } + + VectorIterator &operator++() { + data_ += IndirectHelper::element_stride; + return *this; + } + + VectorIterator operator++(int) { + VectorIterator temp(data_, 0); + data_ += IndirectHelper::element_stride; + return temp; + } + + VectorIterator operator+(const uoffset_t &offset) const { + return VectorIterator(data_ + offset * IndirectHelper::element_stride, + 0); + } + + VectorIterator &operator+=(const uoffset_t &offset) { + data_ += offset * IndirectHelper::element_stride; + return *this; + } + + VectorIterator &operator--() { + data_ -= IndirectHelper::element_stride; + return *this; + } + + VectorIterator operator--(int) { + VectorIterator temp(data_, 0); + data_ -= IndirectHelper::element_stride; + return temp; + } + + VectorIterator operator-(const uoffset_t &offset) const { + return VectorIterator(data_ - offset * IndirectHelper::element_stride, + 0); + } + + VectorIterator &operator-=(const uoffset_t &offset) { + data_ -= offset * IndirectHelper::element_stride; + return *this; + } + + private: + const uint8_t *data_; +}; + +template +struct VectorReverseIterator : public std::reverse_iterator { + explicit VectorReverseIterator(Iterator iter) + : std::reverse_iterator(iter) {} + + // Note: return type is incompatible with the standard + // `reference operator*()`. + typename Iterator::value_type operator*() const { + auto tmp = std::reverse_iterator::current; + return *--tmp; + } + + // Note: return type is incompatible with the standard + // `pointer operator->()`. + typename Iterator::value_type operator->() const { + auto tmp = std::reverse_iterator::current; + return *--tmp; + } +}; + +// This is used as a helper type for accessing vectors. +// Vector::data() assumes the vector elements start after the length field. +template class Vector { + public: + typedef VectorIterator::mutable_return_type> + iterator; + typedef VectorIterator::return_type> + const_iterator; + typedef VectorReverseIterator reverse_iterator; + typedef VectorReverseIterator const_reverse_iterator; + + typedef typename flatbuffers::bool_constant::value> + scalar_tag; + + static FLATBUFFERS_CONSTEXPR bool is_span_observable = + scalar_tag::value && (FLATBUFFERS_LITTLEENDIAN || sizeof(T) == 1); + + uoffset_t size() const { return EndianScalar(length_); } + + // Deprecated: use size(). Here for backwards compatibility. + FLATBUFFERS_ATTRIBUTE([[deprecated("use size() instead")]]) + uoffset_t Length() const { return size(); } + + typedef typename IndirectHelper::return_type return_type; + typedef typename IndirectHelper::mutable_return_type mutable_return_type; + typedef return_type value_type; + + return_type Get(uoffset_t i) const { + FLATBUFFERS_ASSERT(i < size()); + return IndirectHelper::Read(Data(), i); + } + + return_type operator[](uoffset_t i) const { return Get(i); } + + // If this is a Vector of enums, T will be its storage type, not the enum + // type. This function makes it convenient to retrieve value with enum + // type E. + template E GetEnum(uoffset_t i) const { + return static_cast(Get(i)); + } + + // If this a vector of unions, this does the cast for you. There's no check + // to make sure this is the right type! + template const U *GetAs(uoffset_t i) const { + return reinterpret_cast(Get(i)); + } + + // If this a vector of unions, this does the cast for you. There's no check + // to make sure this is actually a string! + const String *GetAsString(uoffset_t i) const { + return reinterpret_cast(Get(i)); + } + + const void *GetStructFromOffset(size_t o) const { + return reinterpret_cast(Data() + o); + } + + iterator begin() { return iterator(Data(), 0); } + const_iterator begin() const { return const_iterator(Data(), 0); } + + iterator end() { return iterator(Data(), size()); } + const_iterator end() const { return const_iterator(Data(), size()); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + const_iterator cbegin() const { return begin(); } + + const_iterator cend() const { return end(); } + + const_reverse_iterator crbegin() const { return rbegin(); } + + const_reverse_iterator crend() const { return rend(); } + + // Change elements if you have a non-const pointer to this object. + // Scalars only. See reflection.h, and the documentation. + void Mutate(uoffset_t i, const T &val) { + FLATBUFFERS_ASSERT(i < size()); + WriteScalar(data() + i, val); + } + + // Change an element of a vector of tables (or strings). + // "val" points to the new table/string, as you can obtain from + // e.g. reflection::AddFlatBuffer(). + void MutateOffset(uoffset_t i, const uint8_t *val) { + FLATBUFFERS_ASSERT(i < size()); + static_assert(sizeof(T) == sizeof(uoffset_t), "Unrelated types"); + WriteScalar(data() + i, + static_cast(val - (Data() + i * sizeof(uoffset_t)))); + } + + // Get a mutable pointer to tables/strings inside this vector. + mutable_return_type GetMutableObject(uoffset_t i) const { + FLATBUFFERS_ASSERT(i < size()); + return const_cast(IndirectHelper::Read(Data(), i)); + } + + // The raw data in little endian format. Use with care. + const uint8_t *Data() const { + return reinterpret_cast(&length_ + 1); + } + + uint8_t *Data() { return reinterpret_cast(&length_ + 1); } + + // Similarly, but typed, much like std::vector::data + const T *data() const { return reinterpret_cast(Data()); } + T *data() { return reinterpret_cast(Data()); } + + template return_type LookupByKey(K key) const { + void *search_result = std::bsearch( + &key, Data(), size(), IndirectHelper::element_stride, KeyCompare); + + if (!search_result) { + return nullptr; // Key not found. + } + + const uint8_t *element = reinterpret_cast(search_result); + + return IndirectHelper::Read(element, 0); + } + + template mutable_return_type MutableLookupByKey(K key) { + return const_cast(LookupByKey(key)); + } + + protected: + // This class is only used to access pre-existing data. Don't ever + // try to construct these manually. + Vector(); + + uoffset_t length_; + + private: + // This class is a pointer. Copying will therefore create an invalid object. + // Private and unimplemented copy constructor. + Vector(const Vector &); + Vector &operator=(const Vector &); + + template static int KeyCompare(const void *ap, const void *bp) { + const K *key = reinterpret_cast(ap); + const uint8_t *data = reinterpret_cast(bp); + auto table = IndirectHelper::Read(data, 0); + + // std::bsearch compares with the operands transposed, so we negate the + // result here. + return -table->KeyCompareWithValue(*key); + } +}; + +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_span(Vector &vec) + FLATBUFFERS_NOEXCEPT { + static_assert(Vector::is_span_observable, + "wrong type U, only LE-scalar, or byte types are allowed"); + return span(vec.data(), vec.size()); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_span( + const Vector &vec) FLATBUFFERS_NOEXCEPT { + static_assert(Vector::is_span_observable, + "wrong type U, only LE-scalar, or byte types are allowed"); + return span(vec.data(), vec.size()); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_bytes_span( + Vector &vec) FLATBUFFERS_NOEXCEPT { + static_assert(Vector::scalar_tag::value, + "wrong type U, only LE-scalar, or byte types are allowed"); + return span(vec.Data(), vec.size() * sizeof(U)); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_bytes_span( + const Vector &vec) FLATBUFFERS_NOEXCEPT { + static_assert(Vector::scalar_tag::value, + "wrong type U, only LE-scalar, or byte types are allowed"); + return span(vec.Data(), vec.size() * sizeof(U)); +} + +// Represent a vector much like the template above, but in this case we +// don't know what the element types are (used with reflection.h). +class VectorOfAny { + public: + uoffset_t size() const { return EndianScalar(length_); } + + const uint8_t *Data() const { + return reinterpret_cast(&length_ + 1); + } + uint8_t *Data() { return reinterpret_cast(&length_ + 1); } + + protected: + VectorOfAny(); + + uoffset_t length_; + + private: + VectorOfAny(const VectorOfAny &); + VectorOfAny &operator=(const VectorOfAny &); +}; + +template +Vector> *VectorCast(Vector> *ptr) { + static_assert(std::is_base_of::value, "Unrelated types"); + return reinterpret_cast> *>(ptr); +} + +template +const Vector> *VectorCast(const Vector> *ptr) { + static_assert(std::is_base_of::value, "Unrelated types"); + return reinterpret_cast> *>(ptr); +} + +// Convenient helper function to get the length of any vector, regardless +// of whether it is null or not (the field is not set). +template static inline size_t VectorLength(const Vector *v) { + return v ? v->size() : 0; +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_VERIFIER_H_ diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/vector_downward.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/vector_downward.h new file mode 100644 index 00000000..33913918 --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/vector_downward.h @@ -0,0 +1,271 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_VECTOR_DOWNWARD_H_ +#define FLATBUFFERS_VECTOR_DOWNWARD_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/default_allocator.h" +#include "flatbuffers/detached_buffer.h" + +namespace flatbuffers { + +// This is a minimal replication of std::vector functionality, +// except growing from higher to lower addresses. i.e push_back() inserts data +// in the lowest address in the vector. +// Since this vector leaves the lower part unused, we support a "scratch-pad" +// that can be stored there for temporary data, to share the allocated space. +// Essentially, this supports 2 std::vectors in a single buffer. +class vector_downward { + public: + explicit vector_downward(size_t initial_size, Allocator *allocator, + bool own_allocator, size_t buffer_minalign) + : allocator_(allocator), + own_allocator_(own_allocator), + initial_size_(initial_size), + buffer_minalign_(buffer_minalign), + reserved_(0), + size_(0), + buf_(nullptr), + cur_(nullptr), + scratch_(nullptr) {} + + vector_downward(vector_downward &&other) + // clang-format on + : allocator_(other.allocator_), + own_allocator_(other.own_allocator_), + initial_size_(other.initial_size_), + buffer_minalign_(other.buffer_minalign_), + reserved_(other.reserved_), + size_(other.size_), + buf_(other.buf_), + cur_(other.cur_), + scratch_(other.scratch_) { + // No change in other.allocator_ + // No change in other.initial_size_ + // No change in other.buffer_minalign_ + other.own_allocator_ = false; + other.reserved_ = 0; + other.buf_ = nullptr; + other.cur_ = nullptr; + other.scratch_ = nullptr; + } + + vector_downward &operator=(vector_downward &&other) { + // Move construct a temporary and swap idiom + vector_downward temp(std::move(other)); + swap(temp); + return *this; + } + + ~vector_downward() { + clear_buffer(); + clear_allocator(); + } + + void reset() { + clear_buffer(); + clear(); + } + + void clear() { + if (buf_) { + cur_ = buf_ + reserved_; + } else { + reserved_ = 0; + cur_ = nullptr; + } + size_ = 0; + clear_scratch(); + } + + void clear_scratch() { scratch_ = buf_; } + + void clear_allocator() { + if (own_allocator_ && allocator_) { delete allocator_; } + allocator_ = nullptr; + own_allocator_ = false; + } + + void clear_buffer() { + if (buf_) Deallocate(allocator_, buf_, reserved_); + buf_ = nullptr; + } + + // Relinquish the pointer to the caller. + uint8_t *release_raw(size_t &allocated_bytes, size_t &offset) { + auto *buf = buf_; + allocated_bytes = reserved_; + offset = static_cast(cur_ - buf_); + + // release_raw only relinquishes the buffer ownership. + // Does not deallocate or reset the allocator. Destructor will do that. + buf_ = nullptr; + clear(); + return buf; + } + + // Relinquish the pointer to the caller. + DetachedBuffer release() { + // allocator ownership (if any) is transferred to DetachedBuffer. + DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_, + size()); + if (own_allocator_) { + allocator_ = nullptr; + own_allocator_ = false; + } + buf_ = nullptr; + clear(); + return fb; + } + + size_t ensure_space(size_t len) { + FLATBUFFERS_ASSERT(cur_ >= scratch_ && scratch_ >= buf_); + if (len > static_cast(cur_ - scratch_)) { reallocate(len); } + // Beyond this, signed offsets may not have enough range: + // (FlatBuffers > 2GB not supported). + FLATBUFFERS_ASSERT(size() < FLATBUFFERS_MAX_BUFFER_SIZE); + return len; + } + + inline uint8_t *make_space(size_t len) { + if (len) { + ensure_space(len); + cur_ -= len; + size_ += static_cast(len); + } + return cur_; + } + + // Returns nullptr if using the DefaultAllocator. + Allocator *get_custom_allocator() { return allocator_; } + + inline uoffset_t size() const { return size_; } + + uoffset_t scratch_size() const { + return static_cast(scratch_ - buf_); + } + + size_t capacity() const { return reserved_; } + + uint8_t *data() const { + FLATBUFFERS_ASSERT(cur_); + return cur_; + } + + uint8_t *scratch_data() const { + FLATBUFFERS_ASSERT(buf_); + return buf_; + } + + uint8_t *scratch_end() const { + FLATBUFFERS_ASSERT(scratch_); + return scratch_; + } + + uint8_t *data_at(size_t offset) const { return buf_ + reserved_ - offset; } + + void push(const uint8_t *bytes, size_t num) { + if (num > 0) { memcpy(make_space(num), bytes, num); } + } + + // Specialized version of push() that avoids memcpy call for small data. + template void push_small(const T &little_endian_t) { + make_space(sizeof(T)); + *reinterpret_cast(cur_) = little_endian_t; + } + + template void scratch_push_small(const T &t) { + ensure_space(sizeof(T)); + *reinterpret_cast(scratch_) = t; + scratch_ += sizeof(T); + } + + // fill() is most frequently called with small byte counts (<= 4), + // which is why we're using loops rather than calling memset. + void fill(size_t zero_pad_bytes) { + make_space(zero_pad_bytes); + for (size_t i = 0; i < zero_pad_bytes; i++) cur_[i] = 0; + } + + // Version for when we know the size is larger. + // Precondition: zero_pad_bytes > 0 + void fill_big(size_t zero_pad_bytes) { + memset(make_space(zero_pad_bytes), 0, zero_pad_bytes); + } + + void pop(size_t bytes_to_remove) { + cur_ += bytes_to_remove; + size_ -= static_cast(bytes_to_remove); + } + + void scratch_pop(size_t bytes_to_remove) { scratch_ -= bytes_to_remove; } + + void swap(vector_downward &other) { + using std::swap; + swap(allocator_, other.allocator_); + swap(own_allocator_, other.own_allocator_); + swap(initial_size_, other.initial_size_); + swap(buffer_minalign_, other.buffer_minalign_); + swap(reserved_, other.reserved_); + swap(size_, other.size_); + swap(buf_, other.buf_); + swap(cur_, other.cur_); + swap(scratch_, other.scratch_); + } + + void swap_allocator(vector_downward &other) { + using std::swap; + swap(allocator_, other.allocator_); + swap(own_allocator_, other.own_allocator_); + } + + private: + // You shouldn't really be copying instances of this class. + FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &)); + FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &)); + + Allocator *allocator_; + bool own_allocator_; + size_t initial_size_; + size_t buffer_minalign_; + size_t reserved_; + uoffset_t size_; + uint8_t *buf_; + uint8_t *cur_; // Points at location between empty (below) and used (above). + uint8_t *scratch_; // Points to the end of the scratchpad in use. + + void reallocate(size_t len) { + auto old_reserved = reserved_; + auto old_size = size(); + auto old_scratch_size = scratch_size(); + reserved_ += + (std::max)(len, old_reserved ? old_reserved / 2 : initial_size_); + reserved_ = (reserved_ + buffer_minalign_ - 1) & ~(buffer_minalign_ - 1); + if (buf_) { + buf_ = ReallocateDownward(allocator_, buf_, old_reserved, reserved_, + old_size, old_scratch_size); + } else { + buf_ = Allocate(allocator_, reserved_); + } + cur_ = buf_ + reserved_ - old_size; + scratch_ = buf_ + old_scratch_size; + } +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_VECTOR_DOWNWARD_H_ diff --git a/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/verifier.h b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/verifier.h new file mode 100644 index 00000000..dfa3da8a --- /dev/null +++ b/code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/verifier.h @@ -0,0 +1,280 @@ +/* + * Copyright 2021 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_VERIFIER_H_ +#define FLATBUFFERS_VERIFIER_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/util.h" +#include "flatbuffers/vector.h" + +namespace flatbuffers { + +// Helper class to verify the integrity of a FlatBuffer +class Verifier FLATBUFFERS_FINAL_CLASS { + public: + Verifier(const uint8_t *buf, size_t buf_len, uoffset_t _max_depth = 64, + uoffset_t _max_tables = 1000000, bool _check_alignment = true) + : buf_(buf), + size_(buf_len), + depth_(0), + max_depth_(_max_depth), + num_tables_(0), + max_tables_(_max_tables), + upper_bound_(0), + check_alignment_(_check_alignment), + flex_reuse_tracker_(nullptr) { + FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE); + } + + // Central location where any verification failures register. + bool Check(bool ok) const { + // clang-format off + #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE + FLATBUFFERS_ASSERT(ok); + #endif + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + if (!ok) + upper_bound_ = 0; + #endif + // clang-format on + return ok; + } + + // Verify any range within the buffer. + bool Verify(size_t elem, size_t elem_len) const { + // clang-format off + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + auto upper_bound = elem + elem_len; + if (upper_bound_ < upper_bound) + upper_bound_ = upper_bound; + #endif + // clang-format on + return Check(elem_len < size_ && elem <= size_ - elem_len); + } + + template bool VerifyAlignment(size_t elem) const { + return Check((elem & (sizeof(T) - 1)) == 0 || !check_alignment_); + } + + // Verify a range indicated by sizeof(T). + template bool Verify(size_t elem) const { + return VerifyAlignment(elem) && Verify(elem, sizeof(T)); + } + + bool VerifyFromPointer(const uint8_t *p, size_t len) { + auto o = static_cast(p - buf_); + return Verify(o, len); + } + + // Verify relative to a known-good base pointer. + bool Verify(const uint8_t *base, voffset_t elem_off, size_t elem_len) const { + return Verify(static_cast(base - buf_) + elem_off, elem_len); + } + + template + bool Verify(const uint8_t *base, voffset_t elem_off) const { + return Verify(static_cast(base - buf_) + elem_off, sizeof(T)); + } + + // Verify a pointer (may be NULL) of a table type. + template bool VerifyTable(const T *table) { + return !table || table->Verify(*this); + } + + // Verify a pointer (may be NULL) of any vector type. + template bool VerifyVector(const Vector *vec) const { + return !vec || VerifyVectorOrString(reinterpret_cast(vec), + sizeof(T)); + } + + // Verify a pointer (may be NULL) of a vector to struct. + template bool VerifyVector(const Vector *vec) const { + return VerifyVector(reinterpret_cast *>(vec)); + } + + // Verify a pointer (may be NULL) to string. + bool VerifyString(const String *str) const { + size_t end; + return !str || (VerifyVectorOrString(reinterpret_cast(str), + 1, &end) && + Verify(end, 1) && // Must have terminator + Check(buf_[end] == '\0')); // Terminating byte must be 0. + } + + // Common code between vectors and strings. + bool VerifyVectorOrString(const uint8_t *vec, size_t elem_size, + size_t *end = nullptr) const { + auto veco = static_cast(vec - buf_); + // Check we can read the size field. + if (!Verify(veco)) return false; + // Check the whole array. If this is a string, the byte past the array + // must be 0. + auto size = ReadScalar(vec); + auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size; + if (!Check(size < max_elems)) + return false; // Protect against byte_size overflowing. + auto byte_size = sizeof(size) + elem_size * size; + if (end) *end = veco + byte_size; + return Verify(veco, byte_size); + } + + // Special case for string contents, after the above has been called. + bool VerifyVectorOfStrings(const Vector> *vec) const { + if (vec) { + for (uoffset_t i = 0; i < vec->size(); i++) { + if (!VerifyString(vec->Get(i))) return false; + } + } + return true; + } + + // Special case for table contents, after the above has been called. + template bool VerifyVectorOfTables(const Vector> *vec) { + if (vec) { + for (uoffset_t i = 0; i < vec->size(); i++) { + if (!vec->Get(i)->Verify(*this)) return false; + } + } + return true; + } + + __supress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart( + const uint8_t *table) { + // Check the vtable offset. + auto tableo = static_cast(table - buf_); + if (!Verify(tableo)) return false; + // This offset may be signed, but doing the subtraction unsigned always + // gives the result we want. + auto vtableo = tableo - static_cast(ReadScalar(table)); + // Check the vtable size field, then check vtable fits in its entirety. + return VerifyComplexity() && Verify(vtableo) && + VerifyAlignment(ReadScalar(buf_ + vtableo)) && + Verify(vtableo, ReadScalar(buf_ + vtableo)); + } + + template + bool VerifyBufferFromStart(const char *identifier, size_t start) { + if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) && + BufferHasIdentifier(buf_ + start, identifier)))) { + return false; + } + + // Call T::Verify, which must be in the generated code for this type. + auto o = VerifyOffset(start); + return o && reinterpret_cast(buf_ + start + o)->Verify(*this) + // clang-format off + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + && GetComputedSize() + #endif + ; + // clang-format on + } + + template + bool VerifyNestedFlatBuffer(const Vector *buf, + const char *identifier) { + if (!buf) return true; + Verifier nested_verifier(buf->data(), buf->size()); + return nested_verifier.VerifyBuffer(identifier); + } + + // Verify this whole buffer, starting with root type T. + template bool VerifyBuffer() { return VerifyBuffer(nullptr); } + + template bool VerifyBuffer(const char *identifier) { + return VerifyBufferFromStart(identifier, 0); + } + + template bool VerifySizePrefixedBuffer(const char *identifier) { + return Verify(0U) && + ReadScalar(buf_) == size_ - sizeof(uoffset_t) && + VerifyBufferFromStart(identifier, sizeof(uoffset_t)); + } + + uoffset_t VerifyOffset(size_t start) const { + if (!Verify(start)) return 0; + auto o = ReadScalar(buf_ + start); + // May not point to itself. + if (!Check(o != 0)) return 0; + // Can't wrap around / buffers are max 2GB. + if (!Check(static_cast(o) >= 0)) return 0; + // Must be inside the buffer to create a pointer from it (pointer outside + // buffer is UB). + if (!Verify(start + o, 1)) return 0; + return o; + } + + uoffset_t VerifyOffset(const uint8_t *base, voffset_t start) const { + return VerifyOffset(static_cast(base - buf_) + start); + } + + // Called at the start of a table to increase counters measuring data + // structure depth and amount, and possibly bails out with false if + // limits set by the constructor have been hit. Needs to be balanced + // with EndTable(). + bool VerifyComplexity() { + depth_++; + num_tables_++; + return Check(depth_ <= max_depth_ && num_tables_ <= max_tables_); + } + + // Called at the end of a table to pop the depth count. + bool EndTable() { + depth_--; + return true; + } + + // Returns the message size in bytes + size_t GetComputedSize() const { + // clang-format off + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + uintptr_t size = upper_bound_; + // Align the size to uoffset_t + size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1); + return (size > size_) ? 0 : size; + #else + // Must turn on FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE for this to work. + (void)upper_bound_; + FLATBUFFERS_ASSERT(false); + return 0; + #endif + // clang-format on + } + + std::vector *GetFlexReuseTracker() { + return flex_reuse_tracker_; + } + + void SetFlexReuseTracker(std::vector *rt) { + flex_reuse_tracker_ = rt; + } + + private: + const uint8_t *buf_; + size_t size_; + uoffset_t depth_; + uoffset_t max_depth_; + uoffset_t num_tables_; + uoffset_t max_tables_; + mutable size_t upper_bound_; + bool check_alignment_; + std::vector *flex_reuse_tracker_; +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_VERIFIER_H_ diff --git a/code/components/tfmicro/third_party/gemmlowp/LICENSE b/code/components/tflite-lib/third_party/gemmlowp/LICENSE similarity index 100% rename from code/components/tfmicro/third_party/gemmlowp/LICENSE rename to code/components/tflite-lib/third_party/gemmlowp/LICENSE diff --git a/code/components/tfmicro/third_party/gemmlowp/fixedpoint/fixedpoint.h b/code/components/tflite-lib/third_party/gemmlowp/fixedpoint/fixedpoint.h similarity index 100% rename from code/components/tfmicro/third_party/gemmlowp/fixedpoint/fixedpoint.h rename to code/components/tflite-lib/third_party/gemmlowp/fixedpoint/fixedpoint.h diff --git a/code/components/tfmicro/third_party/gemmlowp/fixedpoint/fixedpoint_neon.h b/code/components/tflite-lib/third_party/gemmlowp/fixedpoint/fixedpoint_neon.h similarity index 100% rename from code/components/tfmicro/third_party/gemmlowp/fixedpoint/fixedpoint_neon.h rename to code/components/tflite-lib/third_party/gemmlowp/fixedpoint/fixedpoint_neon.h diff --git a/code/components/tfmicro/third_party/gemmlowp/fixedpoint/fixedpoint_sse.h b/code/components/tflite-lib/third_party/gemmlowp/fixedpoint/fixedpoint_sse.h similarity index 100% rename from code/components/tfmicro/third_party/gemmlowp/fixedpoint/fixedpoint_sse.h rename to code/components/tflite-lib/third_party/gemmlowp/fixedpoint/fixedpoint_sse.h diff --git a/code/components/tfmicro/third_party/gemmlowp/internal/detect_platform.h b/code/components/tflite-lib/third_party/gemmlowp/internal/detect_platform.h similarity index 100% rename from code/components/tfmicro/third_party/gemmlowp/internal/detect_platform.h rename to code/components/tflite-lib/third_party/gemmlowp/internal/detect_platform.h diff --git a/code/components/tflite-lib/third_party/kissfft/COPYING b/code/components/tflite-lib/third_party/kissfft/COPYING new file mode 100644 index 00000000..2fc6685a --- /dev/null +++ b/code/components/tflite-lib/third_party/kissfft/COPYING @@ -0,0 +1,11 @@ +Copyright (c) 2003-2010 Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/components/tflite-lib/third_party/kissfft/_kiss_fft_guts.h b/code/components/tflite-lib/third_party/kissfft/_kiss_fft_guts.h new file mode 100644 index 00000000..1a0f4c26 --- /dev/null +++ b/code/components/tflite-lib/third_party/kissfft/_kiss_fft_guts.h @@ -0,0 +1,168 @@ +#ifndef _KISS_FFT_GUTS_H +#define _KISS_FFT_GUTS_H + +/* +Copyright (c) 2003-2010, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* kiss_fft.h + defines kiss_fft_scalar as either short or a float type + and defines + typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ +#include "kiss_fft.h" +#include + +#define MAXFACTORS 32 +/* e.g. an fft of length 128 has 4 factors + as far as kissfft is concerned + 4*4*4*2 + */ + +struct kiss_fft_state{ + int nfft; + int inverse; + int factors[2*MAXFACTORS]; + kiss_fft_cpx twiddles[1]; +}; + +/* + Explanation of macros dealing with complex math: + + C_MUL(m,a,b) : m = a*b + C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise + C_SUB( res, a,b) : res = a - b + C_SUBFROM( res , a) : res -= a + C_ADDTO( res , a) : res += a + * */ +#ifdef FIXED_POINT +#if (FIXED_POINT==32) +# define FRACBITS 31 +# define SAMPPROD int64_t +#define SAMP_MAX 2147483647 +#else +# define FRACBITS 15 +# define SAMPPROD int32_t +#define SAMP_MAX 32767 +#endif + +#define SAMP_MIN -SAMP_MAX + +#if defined(CHECK_OVERFLOW) +# define CHECK_OVERFLOW_OP(a,op,b) \ + if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ + fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } +#endif + + +# define smul(a,b) ( (SAMPPROD)(a)*(b) ) +# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS ) + +# define S_MUL(a,b) sround( smul(a,b) ) + +# define C_MUL(m,a,b) \ + do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ + (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) + +# define DIVSCALAR(x,k) \ + (x) = sround( smul( x, SAMP_MAX/k ) ) + +# define C_FIXDIV(c,div) \ + do { DIVSCALAR( (c).r , div); \ + DIVSCALAR( (c).i , div); }while (0) + +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r = sround( smul( (c).r , s ) ) ;\ + (c).i = sround( smul( (c).i , s ) ) ; }while(0) + +#else /* not FIXED_POINT*/ + +# define S_MUL(a,b) ( (a)*(b) ) +#define C_MUL(m,a,b) \ + do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ + (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) +# define C_FIXDIV(c,div) /* NOOP */ +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r *= (s);\ + (c).i *= (s); }while(0) +#endif + +#ifndef CHECK_OVERFLOW_OP +# define CHECK_OVERFLOW_OP(a,op,b) /* noop */ +#endif + +#define C_ADD( res, a,b)\ + do { \ + CHECK_OVERFLOW_OP((a).r,+,(b).r)\ + CHECK_OVERFLOW_OP((a).i,+,(b).i)\ + (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ + }while(0) +#define C_SUB( res, a,b)\ + do { \ + CHECK_OVERFLOW_OP((a).r,-,(b).r)\ + CHECK_OVERFLOW_OP((a).i,-,(b).i)\ + (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ + }while(0) +#define C_ADDTO( res , a)\ + do { \ + CHECK_OVERFLOW_OP((res).r,+,(a).r)\ + CHECK_OVERFLOW_OP((res).i,+,(a).i)\ + (res).r += (a).r; (res).i += (a).i;\ + }while(0) + +#define C_SUBFROM( res , a)\ + do {\ + CHECK_OVERFLOW_OP((res).r,-,(a).r)\ + CHECK_OVERFLOW_OP((res).i,-,(a).i)\ + (res).r -= (a).r; (res).i -= (a).i; \ + }while(0) + + +#ifdef FIXED_POINT +# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase)) +# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase)) +# define HALF_OF(x) ((x)>>1) +#elif defined(USE_SIMD) +# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) +# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) +# define HALF_OF(x) ((x)*_mm_set1_ps(.5)) +#else +# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) +# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) +# define HALF_OF(x) ((x)*(kiss_fft_scalar).5) +#endif + +#define kf_cexp(x,phase) \ + do{ \ + (x)->r = KISS_FFT_COS(phase);\ + (x)->i = KISS_FFT_SIN(phase);\ + }while(0) + + +/* a debugging function */ +#define pcpx(c)\ + fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) + + +#ifdef KISS_FFT_USE_ALLOCA +// define this to allow use of alloca instead of malloc for temporary buffers +// Temporary buffers are used in two case: +// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5 +// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform. +#include +#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes) +#define KISS_FFT_TMP_FREE(ptr) +#else +#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes) +#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr) +#endif +#endif // _KISS_FFT_GUTS_H diff --git a/code/components/tflite-lib/third_party/kissfft/kiss_fft.c b/code/components/tflite-lib/third_party/kissfft/kiss_fft.c new file mode 100644 index 00000000..9133a013 --- /dev/null +++ b/code/components/tflite-lib/third_party/kissfft/kiss_fft.c @@ -0,0 +1,408 @@ +/* +Copyright (c) 2003-2010, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "_kiss_fft_guts.h" +/* The guts header contains all the multiplication and addition macros that are defined for + fixed or floating point complex numbers. It also delares the kf_ internal functions. + */ + +static void kf_bfly2( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m + ) +{ + kiss_fft_cpx * Fout2; + kiss_fft_cpx * tw1 = st->twiddles; + kiss_fft_cpx t; + Fout2 = Fout + m; + do{ + C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); + + C_MUL (t, *Fout2 , *tw1); + tw1 += fstride; + C_SUB( *Fout2 , *Fout , t ); + C_ADDTO( *Fout , t ); + ++Fout2; + ++Fout; + }while (--m); +} + +static void kf_bfly4( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + const size_t m + ) +{ + kiss_fft_cpx *tw1,*tw2,*tw3; + kiss_fft_cpx scratch[6]; + size_t k=m; + const size_t m2=2*m; + const size_t m3=3*m; + + + tw3 = tw2 = tw1 = st->twiddles; + + do { + C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); + + C_MUL(scratch[0],Fout[m] , *tw1 ); + C_MUL(scratch[1],Fout[m2] , *tw2 ); + C_MUL(scratch[2],Fout[m3] , *tw3 ); + + C_SUB( scratch[5] , *Fout, scratch[1] ); + C_ADDTO(*Fout, scratch[1]); + C_ADD( scratch[3] , scratch[0] , scratch[2] ); + C_SUB( scratch[4] , scratch[0] , scratch[2] ); + C_SUB( Fout[m2], *Fout, scratch[3] ); + tw1 += fstride; + tw2 += fstride*2; + tw3 += fstride*3; + C_ADDTO( *Fout , scratch[3] ); + + if(st->inverse) { + Fout[m].r = scratch[5].r - scratch[4].i; + Fout[m].i = scratch[5].i + scratch[4].r; + Fout[m3].r = scratch[5].r + scratch[4].i; + Fout[m3].i = scratch[5].i - scratch[4].r; + }else{ + Fout[m].r = scratch[5].r + scratch[4].i; + Fout[m].i = scratch[5].i - scratch[4].r; + Fout[m3].r = scratch[5].r - scratch[4].i; + Fout[m3].i = scratch[5].i + scratch[4].r; + } + ++Fout; + }while(--k); +} + +static void kf_bfly3( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + size_t m + ) +{ + size_t k=m; + const size_t m2 = 2*m; + kiss_fft_cpx *tw1,*tw2; + kiss_fft_cpx scratch[5]; + kiss_fft_cpx epi3; + epi3 = st->twiddles[fstride*m]; + + tw1=tw2=st->twiddles; + + do{ + C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); + + C_MUL(scratch[1],Fout[m] , *tw1); + C_MUL(scratch[2],Fout[m2] , *tw2); + + C_ADD(scratch[3],scratch[1],scratch[2]); + C_SUB(scratch[0],scratch[1],scratch[2]); + tw1 += fstride; + tw2 += fstride*2; + + Fout[m].r = Fout->r - HALF_OF(scratch[3].r); + Fout[m].i = Fout->i - HALF_OF(scratch[3].i); + + C_MULBYSCALAR( scratch[0] , epi3.i ); + + C_ADDTO(*Fout,scratch[3]); + + Fout[m2].r = Fout[m].r + scratch[0].i; + Fout[m2].i = Fout[m].i - scratch[0].r; + + Fout[m].r -= scratch[0].i; + Fout[m].i += scratch[0].r; + + ++Fout; + }while(--k); +} + +static void kf_bfly5( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m + ) +{ + kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; + int u; + kiss_fft_cpx scratch[13]; + kiss_fft_cpx * twiddles = st->twiddles; + kiss_fft_cpx *tw; + kiss_fft_cpx ya,yb; + ya = twiddles[fstride*m]; + yb = twiddles[fstride*2*m]; + + Fout0=Fout; + Fout1=Fout0+m; + Fout2=Fout0+2*m; + Fout3=Fout0+3*m; + Fout4=Fout0+4*m; + + tw=st->twiddles; + for ( u=0; ur += scratch[7].r + scratch[8].r; + Fout0->i += scratch[7].i + scratch[8].i; + + scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); + scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); + + scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); + scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); + + C_SUB(*Fout1,scratch[5],scratch[6]); + C_ADD(*Fout4,scratch[5],scratch[6]); + + scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); + scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); + scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); + scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); + + C_ADD(*Fout2,scratch[11],scratch[12]); + C_SUB(*Fout3,scratch[11],scratch[12]); + + ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; + } +} + +/* perform the butterfly for one stage of a mixed radix FFT */ +static void kf_bfly_generic( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m, + int p + ) +{ + int u,k,q1,q; + kiss_fft_cpx * twiddles = st->twiddles; + kiss_fft_cpx t; + int Norig = st->nfft; + + kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p); + + for ( u=0; u=Norig) twidx-=Norig; + C_MUL(t,scratch[q] , twiddles[twidx] ); + C_ADDTO( Fout[ k ] ,t); + } + k += m; + } + } + KISS_FFT_TMP_FREE(scratch); +} + +static +void kf_work( + kiss_fft_cpx * Fout, + const kiss_fft_cpx * f, + const size_t fstride, + int in_stride, + int * factors, + const kiss_fft_cfg st + ) +{ + kiss_fft_cpx * Fout_beg=Fout; + const int p=*factors++; /* the radix */ + const int m=*factors++; /* stage's fft length/p */ + const kiss_fft_cpx * Fout_end = Fout + p*m; + +#ifdef _OPENMP + // use openmp extensions at the + // top-level (not recursive) + if (fstride==1 && p<=5) + { + int k; + + // execute the p different work units in different threads +# pragma omp parallel for + for (k=0;k floor_sqrt) + p = n; /* no more factors, skip to end */ + } + n /= p; + *facbuf++ = p; + *facbuf++ = n; + } while (n > 1); +} + +/* + * + * User-callable function to allocate all necessary storage space for the fft. + * + * The return value is a contiguous block of memory, allocated with malloc. As such, + * It can be freed with free(), rather than a kiss_fft-specific function. + * */ +kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) +{ + kiss_fft_cfg st=NULL; + size_t memneeded = sizeof(struct kiss_fft_state) + + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/ + + if ( lenmem==NULL ) { + st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded ); + }else{ + if (mem != NULL && *lenmem >= memneeded) + st = (kiss_fft_cfg)mem; + *lenmem = memneeded; + } + if (st) { + int i; + st->nfft=nfft; + st->inverse = inverse_fft; + + for (i=0;iinverse) + phase *= -1; + kf_cexp(st->twiddles+i, phase ); + } + + kf_factor(nfft,st->factors); + } + return st; +} + + +void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) +{ + if (fin == fout) { + //NOTE: this is not really an in-place FFT algorithm. + //It just performs an out-of-place FFT into a temp buffer + kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft); + kf_work(tmpbuf,fin,1,in_stride, st->factors,st); + /* memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); */ + KISS_FFT_TMP_FREE(tmpbuf); + }else{ + kf_work( fout, fin, 1,in_stride, st->factors,st ); + } +} + +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) +{ + kiss_fft_stride(cfg,fin,fout,1); +} + + +void kiss_fft_cleanup(void) +{ + // nothing needed any more +} + +int kiss_fft_next_fast_size(int n) +{ + while(1) { + int m=n; + while ( (m%2) == 0 ) m/=2; + while ( (m%3) == 0 ) m/=3; + while ( (m%5) == 0 ) m/=5; + if (m<=1) + break; /* n is completely factorable by twos, threes, and fives */ + n++; + } + return n; +} diff --git a/code/components/tflite-lib/third_party/kissfft/kiss_fft.h b/code/components/tflite-lib/third_party/kissfft/kiss_fft.h new file mode 100644 index 00000000..24e4d0c0 --- /dev/null +++ b/code/components/tflite-lib/third_party/kissfft/kiss_fft.h @@ -0,0 +1,124 @@ +#ifndef KISS_FFT_H +#define KISS_FFT_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C++" { +#endif + +/* + ATTENTION! + If you would like a : + -- a utility that will handle the caching of fft objects + -- real-only (no imaginary time component ) FFT + -- a multi-dimensional FFT + -- a command-line utility to perform ffts + -- a command-line utility to perform fast-convolution filtering + + Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c + in the tools/ directory. +*/ + +#ifdef USE_SIMD +# include +# define kiss_fft_scalar __m128 +#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) +#define KISS_FFT_FREE _mm_free +#else +#define KISS_FFT_MALLOC(X) (void*)(0x0) /* Patched. */ +#define KISS_FFT_FREE(X) /* Patched. */ +#endif + + +#ifdef FIXED_POINT +#include /* Patched. */ +# if (FIXED_POINT == 32) +# define kiss_fft_scalar int32_t +# else +# define kiss_fft_scalar int16_t +# endif +#else +# ifndef kiss_fft_scalar +/* default is float */ +# define kiss_fft_scalar float +# endif +#endif + +typedef struct { + kiss_fft_scalar r; + kiss_fft_scalar i; +}kiss_fft_cpx; + +typedef struct kiss_fft_state* kiss_fft_cfg; + +/* + * kiss_fft_alloc + * + * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. + * + * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); + * + * The return value from fft_alloc is a cfg buffer used internally + * by the fft routine or NULL. + * + * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. + * The returned value should be free()d when done to avoid memory leaks. + * + * The state can be placed in a user supplied buffer 'mem': + * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, + * then the function places the cfg in mem and the size used in *lenmem + * and returns mem. + * + * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), + * then the function returns NULL and places the minimum cfg + * buffer size in *lenmem. + * */ + +kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); + +/* + * kiss_fft(cfg,in_out_buf) + * + * Perform an FFT on a complex input buffer. + * for a forward FFT, + * fin should be f[0] , f[1] , ... ,f[nfft-1] + * fout will be F[0] , F[1] , ... ,F[nfft-1] + * Note that each element is complex and can be accessed like + f[k].r and f[k].i + * */ +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); + +/* + A more generic version of the above function. It reads its input from every Nth sample. + * */ +void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); + +/* If kiss_fft_alloc allocated a buffer, it is one contiguous + buffer and can be simply free()d when no longer needed*/ +#define kiss_fft_free free + +/* + Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up + your compiler output to call this before you exit. +*/ +void kiss_fft_cleanup(void); + + +/* + * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) + */ +int kiss_fft_next_fast_size(int n); + +/* for real ffts, we need an even size */ +#define kiss_fftr_next_fast_size_real(n) \ + (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/code/components/tflite-lib/third_party/kissfft/tools/kiss_fftr.c b/code/components/tflite-lib/third_party/kissfft/tools/kiss_fftr.c new file mode 100644 index 00000000..0d22a04d --- /dev/null +++ b/code/components/tflite-lib/third_party/kissfft/tools/kiss_fftr.c @@ -0,0 +1,159 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "kiss_fftr.h" +#include "_kiss_fft_guts.h" + +struct kiss_fftr_state{ + kiss_fft_cfg substate; + kiss_fft_cpx * tmpbuf; + kiss_fft_cpx * super_twiddles; +#ifdef USE_SIMD + void * pad; +#endif +}; + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) +{ + int i; + kiss_fftr_cfg st = NULL; + size_t subsize, memneeded; + + if (nfft & 1) { + /* fprintf(stderr,"Real FFT optimization must be even.\n"); */ + return NULL; + } + nfft >>= 1; + + kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); + memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2); + + if (lenmem == NULL) { + st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); + } else { + if (*lenmem >= memneeded) + st = (kiss_fftr_cfg) mem; + *lenmem = memneeded; + } + if (!st) + return NULL; + + st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ + st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); + st->super_twiddles = st->tmpbuf + nfft; + kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); + + for (i = 0; i < nfft/2; ++i) { + double phase = + -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5); + if (inverse_fft) + phase *= -1; + kf_cexp (st->super_twiddles+i,phase); + } + return st; +} + +void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) +{ + /* input buffer timedata is stored row-wise */ + int k,ncfft; + kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; + + if ( st->substate->inverse) { + /* fprintf(stderr,"kiss fft usage error: improper alloc\n"); */ + return; /* exit(1); */ + } + + ncfft = st->substate->nfft; + + /*perform the parallel fft of two real signals packed in real,imag*/ + kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); + /* The real part of the DC element of the frequency spectrum in st->tmpbuf + * contains the sum of the even-numbered elements of the input time sequence + * The imag part is the sum of the odd-numbered elements + * + * The sum of tdc.r and tdc.i is the sum of the input time sequence. + * yielding DC of input time sequence + * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... + * yielding Nyquist bin of input time sequence + */ + + tdc.r = st->tmpbuf[0].r; + tdc.i = st->tmpbuf[0].i; + C_FIXDIV(tdc,2); + CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); + CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); + freqdata[0].r = tdc.r + tdc.i; + freqdata[ncfft].r = tdc.r - tdc.i; +#ifdef USE_SIMD + freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); +#else + freqdata[ncfft].i = freqdata[0].i = 0; +#endif + + for ( k=1;k <= ncfft/2 ; ++k ) { + fpk = st->tmpbuf[k]; + fpnk.r = st->tmpbuf[ncfft-k].r; + fpnk.i = - st->tmpbuf[ncfft-k].i; + C_FIXDIV(fpk,2); + C_FIXDIV(fpnk,2); + + C_ADD( f1k, fpk , fpnk ); + C_SUB( f2k, fpk , fpnk ); + C_MUL( tw , f2k , st->super_twiddles[k-1]); + + freqdata[k].r = HALF_OF(f1k.r + tw.r); + freqdata[k].i = HALF_OF(f1k.i + tw.i); + freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r); + freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i); + } +} + +void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata) +{ + /* input buffer timedata is stored row-wise */ + int k, ncfft; + + if (st->substate->inverse == 0) { + /* fprintf (stderr, "kiss fft usage error: improper alloc\n"); */ + return; /* exit (1); */ + } + + ncfft = st->substate->nfft; + + st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r; + st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r; + C_FIXDIV(st->tmpbuf[0],2); + + for (k = 1; k <= ncfft / 2; ++k) { + kiss_fft_cpx fk, fnkc, fek, fok, tmp; + fk = freqdata[k]; + fnkc.r = freqdata[ncfft - k].r; + fnkc.i = -freqdata[ncfft - k].i; + C_FIXDIV( fk , 2 ); + C_FIXDIV( fnkc , 2 ); + + C_ADD (fek, fk, fnkc); + C_SUB (tmp, fk, fnkc); + C_MUL (fok, tmp, st->super_twiddles[k-1]); + C_ADD (st->tmpbuf[k], fek, fok); + C_SUB (st->tmpbuf[ncfft - k], fek, fok); +#ifdef USE_SIMD + st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); +#else + st->tmpbuf[ncfft - k].i *= -1; +#endif + } + kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); +} diff --git a/code/components/tflite-lib/third_party/kissfft/tools/kiss_fftr.h b/code/components/tflite-lib/third_party/kissfft/tools/kiss_fftr.h new file mode 100644 index 00000000..b888a28b --- /dev/null +++ b/code/components/tflite-lib/third_party/kissfft/tools/kiss_fftr.h @@ -0,0 +1,46 @@ +#ifndef KISS_FTR_H +#define KISS_FTR_H + +#include "kiss_fft.h" +#ifdef __cplusplus +extern "C++" { +#endif + + +/* + + Real optimized version can save about 45% cpu time vs. complex fft of a real seq. + + + + */ + +typedef struct kiss_fftr_state *kiss_fftr_cfg; + + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); +/* + nfft must be even + + If you don't care to allocate space, use mem = lenmem = NULL +*/ + + +void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); +/* + input timedata has nfft scalar points + output freqdata has nfft/2+1 complex points +*/ + +void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); +/* + input freqdata has nfft/2+1 complex points + output timedata has nfft scalar points +*/ + +#define kiss_fftr_free free + +#ifdef __cplusplus +} +#endif +#endif diff --git a/code/components/tfmicro/third_party/ruy/ruy/profiler/instrumentation.h b/code/components/tflite-lib/third_party/ruy/ruy/profiler/instrumentation.h similarity index 100% rename from code/components/tfmicro/third_party/ruy/ruy/profiler/instrumentation.h rename to code/components/tflite-lib/third_party/ruy/ruy/profiler/instrumentation.h diff --git a/code/components/tfmicro.zip b/code/components/tfmicro.zip new file mode 100644 index 00000000..5e57aa05 Binary files /dev/null and b/code/components/tfmicro.zip differ diff --git a/code/components/tfmicro/CMakeLists.txt b/code/components/tfmicro/CMakeLists.txt deleted file mode 100644 index d95f431e..00000000 --- a/code/components/tfmicro/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ - -# Copyright 2019 The TensorFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# This component was generated for the 'hello_world' TF Micro example. -# - -# Make sure that the IDF Path environment variable is defined -if(NOT DEFINED ENV{IDF_PATH}) - message(FATAL_ERROR "The IDF_PATH environment variable must point to the location of the ESP-IDF.") -endif() - -idf_component_register( - SRCS tensorflow/lite/micro/simple_memory_allocator.cc tensorflow/lite/micro/debug_log.cc tensorflow/lite/micro/micro_error_reporter.cc tensorflow/lite/micro/memory_helpers.cc tensorflow/lite/micro/test_helpers.cc tensorflow/lite/micro/recording_micro_allocator.cc tensorflow/lite/micro/micro_time.cc tensorflow/lite/micro/recording_simple_memory_allocator.cc tensorflow/lite/micro/micro_string.cc tensorflow/lite/micro/micro_profiler.cc tensorflow/lite/micro/flatbuffer_utils.cc tensorflow/lite/micro/micro_graph.cc tensorflow/lite/micro/mock_micro_graph.cc tensorflow/lite/micro/all_ops_resolver.cc tensorflow/lite/micro/micro_utils.cc tensorflow/lite/micro/micro_interpreter.cc tensorflow/lite/micro/micro_allocator.cc tensorflow/lite/micro/system_setup.cc tensorflow/lite/micro/memory_planner/linear_memory_planner.cc tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc tensorflow/lite/schema/schema_utils.cc tensorflow/lite/c/common.c tensorflow/lite/core/api/tensor_utils.cc tensorflow/lite/core/api/error_reporter.cc tensorflow/lite/core/api/flatbuffer_conversions.cc tensorflow/lite/core/api/op_resolver.cc tensorflow/lite/kernels/kernel_util.cc tensorflow/lite/kernels/internal/quantization_util.cc tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc tensorflow/lite/micro/kernels/activations.cc tensorflow/lite/micro/kernels/activations_common.cc tensorflow/lite/micro/kernels/add.cc tensorflow/lite/micro/kernels/add_n.cc tensorflow/lite/micro/kernels/arg_min_max.cc tensorflow/lite/micro/kernels/batch_to_space_nd.cc tensorflow/lite/micro/kernels/cast.cc tensorflow/lite/micro/kernels/ceil.cc tensorflow/lite/micro/kernels/circular_buffer.cc tensorflow/lite/micro/kernels/comparisons.cc tensorflow/lite/micro/kernels/concatenation.cc tensorflow/lite/micro/kernels/conv.cc tensorflow/lite/micro/kernels/conv_common.cc tensorflow/lite/micro/kernels/cumsum.cc tensorflow/lite/micro/kernels/depth_to_space.cc tensorflow/lite/micro/kernels/depthwise_conv.cc tensorflow/lite/micro/kernels/depthwise_conv_common.cc tensorflow/lite/micro/kernels/dequantize.cc tensorflow/lite/micro/kernels/detection_postprocess.cc tensorflow/lite/micro/kernels/elementwise.cc tensorflow/lite/micro/kernels/elu.cc tensorflow/lite/micro/kernels/ethosu.cc tensorflow/lite/micro/kernels/exp.cc tensorflow/lite/micro/kernels/expand_dims.cc tensorflow/lite/micro/kernels/fill.cc tensorflow/lite/micro/kernels/floor.cc tensorflow/lite/micro/kernels/floor_div.cc tensorflow/lite/micro/kernels/floor_mod.cc tensorflow/lite/micro/kernels/fully_connected.cc tensorflow/lite/micro/kernels/fully_connected_common.cc tensorflow/lite/micro/kernels/gather.cc tensorflow/lite/micro/kernels/gather_nd.cc tensorflow/lite/micro/kernels/hard_swish.cc tensorflow/lite/micro/kernels/hard_swish_common.cc tensorflow/lite/micro/kernels/if.cc tensorflow/lite/micro/kernels/kernel_runner.cc tensorflow/lite/micro/kernels/kernel_util.cc tensorflow/lite/micro/kernels/l2norm.cc tensorflow/lite/micro/kernels/l2_pool_2d.cc tensorflow/lite/micro/kernels/leaky_relu.cc tensorflow/lite/micro/kernels/logical.cc tensorflow/lite/micro/kernels/logical_common.cc tensorflow/lite/micro/kernels/logistic.cc tensorflow/lite/micro/kernels/logistic_common.cc tensorflow/lite/micro/kernels/log_softmax.cc tensorflow/lite/micro/kernels/maximum_minimum.cc tensorflow/lite/micro/kernels/mul.cc tensorflow/lite/micro/kernels/neg.cc tensorflow/lite/micro/kernels/pack.cc tensorflow/lite/micro/kernels/pad.cc tensorflow/lite/micro/kernels/pooling.cc tensorflow/lite/micro/kernels/pooling_common.cc tensorflow/lite/micro/kernels/prelu.cc tensorflow/lite/micro/kernels/quantize.cc tensorflow/lite/micro/kernels/quantize_common.cc tensorflow/lite/micro/kernels/reduce.cc tensorflow/lite/micro/kernels/reshape.cc tensorflow/lite/micro/kernels/resize_bilinear.cc tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc tensorflow/lite/micro/kernels/round.cc tensorflow/lite/micro/kernels/shape.cc tensorflow/lite/micro/kernels/softmax.cc tensorflow/lite/micro/kernels/softmax_common.cc tensorflow/lite/micro/kernels/space_to_batch_nd.cc tensorflow/lite/micro/kernels/space_to_depth.cc tensorflow/lite/micro/kernels/split.cc tensorflow/lite/micro/kernels/split_v.cc tensorflow/lite/micro/kernels/squeeze.cc tensorflow/lite/micro/kernels/strided_slice.cc tensorflow/lite/micro/kernels/sub.cc tensorflow/lite/micro/kernels/svdf.cc tensorflow/lite/micro/kernels/svdf_common.cc tensorflow/lite/micro/kernels/tanh.cc tensorflow/lite/micro/kernels/transpose.cc tensorflow/lite/micro/kernels/transpose_conv.cc tensorflow/lite/micro/kernels/unpack.cc tensorflow/lite/micro/kernels/zeros_like.cc - INCLUDE_DIRS . third_party/gemmlowp third_party/flatbuffers/include third_party/ruy) - -# Reduce the level of paranoia to be able to compile TF sources -target_compile_options(${COMPONENT_LIB} PRIVATE - -Wno-maybe-uninitialized - -Wno-missing-field-initializers - -Wno-type-limits) - -target_compile_options(${COMPONENT_LIB} PRIVATE -Wimplicit-function-declaration -Werror -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP) -target_compile_options(${COMPONENT_LIB} PRIVATE $<$: -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -Werror -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP >) -target_compile_options(${COMPONENT_LIB} INTERFACE $<$>:-DTF_LITE_STATIC_MEMORY>) -target_link_libraries(${COMPONENT_LIB} PRIVATE -lm) diff --git a/code/components/tfmicro/tensorflow/lite/micro/kernels/dequantize.cc b/code/components/tfmicro/tensorflow/lite/micro/kernels/dequantize.cc deleted file mode 100644 index b488c41a..00000000 --- a/code/components/tfmicro/tensorflow/lite/micro/kernels/dequantize.cc +++ /dev/null @@ -1,139 +0,0 @@ -/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -#include "tensorflow/lite/kernels/internal/reference/dequantize.h" - -#include "tensorflow/lite/c/builtin_op_data.h" -#include "tensorflow/lite/c/common.h" -#include "tensorflow/lite/kernels/internal/quantization_util.h" -#include "tensorflow/lite/kernels/internal/reference/quantize.h" -#include "tensorflow/lite/kernels/internal/reference/requantize.h" -#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" -#include "tensorflow/lite/kernels/kernel_util.h" -#include "tensorflow/lite/micro/kernels/kernel_util.h" - -namespace tflite { -namespace ops { -namespace micro { -namespace dequantize { - -struct OpData { - tflite::DequantizationParams quantization_params; - // The scaling factor from input to output (aka the 'real multiplier') can - // be represented as a fixed point multiplier plus a left shift. - int32_t output_multiplier; - int output_shift; - int32_t output_zero_point; -}; - -void* Init(TfLiteContext* context, const char* buffer, size_t length) { - TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr); - return context->AllocatePersistentBuffer(context, sizeof(OpData)); -} - -TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { - TFLITE_DCHECK(node->user_data != nullptr); - OpData* data = static_cast(node->user_data); - - TF_LITE_ENSURE_EQ(context, NumInputs(node), 1); - TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); - - // TODO(b/140515557): Add cached dequant to improve hybrid model performance. - const TfLiteTensor* input = GetInput(context, node, 0); - TF_LITE_ENSURE(context, input != nullptr); - TfLiteTensor* output = GetOutput(context, node, 0); - TF_LITE_ENSURE(context, output != nullptr); - - TF_LITE_ENSURE(context, input->type == kTfLiteUInt8 || - input->type == kTfLiteInt8 || - input->type == kTfLiteInt16); - TF_LITE_ENSURE(context, output->type == kTfLiteFloat32); - - if (output->type == kTfLiteInt32) { - const double effective_output_scale = - static_cast(input->params.scale) / - static_cast(output->params.scale); - QuantizeMultiplier(effective_output_scale, &data->output_multiplier, - &data->output_shift); - } - - data->quantization_params.zero_point = input->params.zero_point; - data->quantization_params.scale = static_cast(input->params.scale); - data->output_zero_point = output->params.zero_point; - return kTfLiteOk; -} - -TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { - TFLITE_DCHECK(node->user_data != nullptr); - OpData* data = static_cast(node->user_data); - - const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0); - TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0); - - if (output->type == kTfLiteFloat32) { - switch (input->type) { - case kTfLiteUInt8: - reference_ops::Dequantize(data->quantization_params, - tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - break; - case kTfLiteInt8: - reference_ops::Dequantize(data->quantization_params, - tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - break; - case kTfLiteInt16: - reference_ops::Dequantize(data->quantization_params, - tflite::micro::GetTensorShape(input), - tflite::micro::GetTensorData(input), - tflite::micro::GetTensorShape(output), - tflite::micro::GetTensorData(output)); - break; - default: - TF_LITE_KERNEL_LOG(context, "Input %s, output %s not supported.", - TfLiteTypeGetName(input->type), - TfLiteTypeGetName(output->type)); - return kTfLiteError; - } - } else { - TF_LITE_KERNEL_LOG(context, "Input %s, output %s not supported.", - TfLiteTypeGetName(input->type), - TfLiteTypeGetName(output->type)); - return kTfLiteError; - } - - return kTfLiteOk; -} - -} // namespace dequantize - -TfLiteRegistration Register_DEQUANTIZE() { - return {/*init=*/dequantize::Init, - /*free=*/nullptr, - /*prepare=*/dequantize::Prepare, - /*invoke=*/dequantize::Eval, - /*profiling_string=*/nullptr, - /*builtin_code=*/0, - /*custom_name=*/nullptr, - /*version=*/0}; -} - -} // namespace micro -} // namespace ops -} // namespace tflite diff --git a/code/components/tfmicro/tensorflow/lite/micro/testing/micro_test.h b/code/components/tfmicro/tensorflow/lite/micro/testing/micro_test.h deleted file mode 100644 index 229dfa6f..00000000 --- a/code/components/tfmicro/tensorflow/lite/micro/testing/micro_test.h +++ /dev/null @@ -1,240 +0,0 @@ -/* Copyright 2018 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -// An ultra-lightweight testing framework designed for use with microcontroller -// applications. Its only dependency is on TensorFlow Lite's ErrorReporter -// interface, where log messages are output. This is designed to be usable even -// when no standard C or C++ libraries are available, and without any dynamic -// memory allocation or reliance on global constructors. -// -// To build a test, you use syntax similar to gunit, but with some extra -// decoration to create a hidden 'main' function containing each of the tests to -// be run. Your code should look something like: -// ---------------------------------------------------------------------------- -// #include "path/to/this/header" -// -// TF_LITE_MICRO_TESTS_BEGIN -// -// TF_LITE_MICRO_TEST(SomeTest) { -// TF_LITE_LOG_EXPECT_EQ(true, true); -// } -// -// TF_LITE_MICRO_TESTS_END -// ---------------------------------------------------------------------------- -// If you compile this for your platform, you'll get a normal binary that you -// should be able to run. Executing it will output logging information like this -// to stderr (or whatever equivalent is available and written to by -// ErrorReporter): -// ---------------------------------------------------------------------------- -// Testing SomeTest -// 1/1 tests passed -// ~~~ALL TESTS PASSED~~~ -// ---------------------------------------------------------------------------- -// This is designed to be human-readable, so you can just run tests manually, -// but the string "~~~ALL TESTS PASSED~~~" should only appear if all of the -// tests do pass. This makes it possible to integrate with automated test -// systems by scanning the output logs and looking for that magic value. -// -// This framework is intended to be a rudimentary alternative to no testing at -// all on systems that struggle to run more conventional approaches, so use with -// caution! - -#ifndef TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_ -#define TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_ - -#include "tensorflow/lite/c/common.h" -#include "tensorflow/lite/micro/micro_error_reporter.h" -#include "tensorflow/lite/micro/system_setup.h" - -namespace micro_test { -extern int tests_passed; -extern int tests_failed; -extern bool is_test_complete; -extern bool did_test_fail; -} // namespace micro_test - -namespace tflite { - -// This additional helper function is used (instead of directly calling -// tflite::InitializeTarget from the TF_LITE_MICRO_TESTS_BEGIN macro) to avoid -// adding a dependency from every bazel test target to micro:system_setp (which -// is the target that implements InitializeTarget(). -// -// The underlying issue here is that the use of the macros results in -// dependencies that can be containted within the micro/testing:micro_test -// target bleeding on to all the tests. -inline void InitializeTest() { InitializeTarget(); } -} // namespace tflite - -#define TF_LITE_MICRO_TESTS_BEGIN \ - namespace micro_test { \ - int tests_passed; \ - int tests_failed; \ - bool is_test_complete; \ - bool did_test_fail; \ - } \ - \ - int main(int argc, char** argv) { \ - micro_test::tests_passed = 0; \ - micro_test::tests_failed = 0; \ - tflite::InitializeTest(); - -#define TF_LITE_MICRO_TESTS_END \ - MicroPrintf("%d/%d tests passed", micro_test::tests_passed, \ - (micro_test::tests_failed + micro_test::tests_passed)); \ - if (micro_test::tests_failed == 0) { \ - MicroPrintf("~~~ALL TESTS PASSED~~~\n"); \ - return kTfLiteOk; \ - } else { \ - MicroPrintf("~~~SOME TESTS FAILED~~~\n"); \ - return kTfLiteError; \ - } \ - } - -// TODO(petewarden): I'm going to hell for what I'm doing to this poor for loop. -#define TF_LITE_MICRO_TEST(name) \ - MicroPrintf("Testing " #name); \ - for (micro_test::is_test_complete = false, \ - micro_test::did_test_fail = false; \ - !micro_test::is_test_complete; micro_test::is_test_complete = true, \ - micro_test::tests_passed += (micro_test::did_test_fail) ? 0 : 1, \ - micro_test::tests_failed += (micro_test::did_test_fail) ? 1 : 0) - -#define TF_LITE_MICRO_EXPECT(x) \ - do { \ - if (!(x)) { \ - MicroPrintf(#x " failed at %s:%d", __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -// TODO(b/139142772): this macro is used with types other than ints even though -// the printf specifier is %d. -#define TF_LITE_MICRO_EXPECT_EQ(x, y) \ - do { \ - auto vx = x; \ - auto vy = y; \ - if ((vx) != (vy)) { \ - MicroPrintf(#x " == " #y " failed at %s:%d (%d vs %d)", __FILE__, \ - __LINE__, static_cast(vx), static_cast(vy)); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -#define TF_LITE_MICRO_EXPECT_NE(x, y) \ - do { \ - if ((x) == (y)) { \ - MicroPrintf(#x " != " #y " failed at %s:%d", __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -// TODO(wangtz): Making it more generic once needed. -#define TF_LITE_MICRO_ARRAY_ELEMENT_EXPECT_NEAR(arr1, idx1, arr2, idx2, \ - epsilon) \ - do { \ - auto delta = ((arr1)[(idx1)] > (arr2)[(idx2)]) \ - ? ((arr1)[(idx1)] - (arr2)[(idx2)]) \ - : ((arr2)[(idx2)] - (arr1)[(idx1)]); \ - if (delta > epsilon) { \ - MicroPrintf(#arr1 "[%d] (%f) near " #arr2 "[%d] (%f) failed at %s:%d", \ - static_cast(idx1), static_cast((arr1)[(idx1)]), \ - static_cast(idx2), static_cast((arr2)[(idx2)]), \ - __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -// The check vx != vy is needed to properly handle the case where both -// x and y evaluate to infinity. See #46960 for more details. -#define TF_LITE_MICRO_EXPECT_NEAR(x, y, epsilon) \ - do { \ - auto vx = (x); \ - auto vy = (y); \ - auto delta = ((vx) > (vy)) ? ((vx) - (vy)) : ((vy) - (vx)); \ - if (vx != vy && delta > epsilon) { \ - MicroPrintf(#x " (%f) near " #y " (%f) failed at %s:%d", \ - static_cast(vx), static_cast(vy), __FILE__, \ - __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -#define TF_LITE_MICRO_EXPECT_GT(x, y) \ - do { \ - if ((x) <= (y)) { \ - MicroPrintf(#x " > " #y " failed at %s:%d", __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -#define TF_LITE_MICRO_EXPECT_LT(x, y) \ - do { \ - if ((x) >= (y)) { \ - MicroPrintf(#x " < " #y " failed at %s:%d", __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -#define TF_LITE_MICRO_EXPECT_GE(x, y) \ - do { \ - if ((x) < (y)) { \ - MicroPrintf(#x " >= " #y " failed at %s:%d", __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -#define TF_LITE_MICRO_EXPECT_LE(x, y) \ - do { \ - if ((x) > (y)) { \ - MicroPrintf(#x " <= " #y " failed at %s:%d", __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -#define TF_LITE_MICRO_EXPECT_TRUE(x) \ - do { \ - if (!(x)) { \ - MicroPrintf(#x " was not true failed at %s:%d", __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -#define TF_LITE_MICRO_EXPECT_FALSE(x) \ - do { \ - if (x) { \ - MicroPrintf(#x " was not false failed at %s:%d", __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } while (false) - -#define TF_LITE_MICRO_FAIL(msg) \ - do { \ - MicroPrintf("FAIL: %s", msg, __FILE__, __LINE__); \ - micro_test::did_test_fail = true; \ - } while (false) - -#define TF_LITE_MICRO_EXPECT_STRING_EQ(string1, string2) \ - do { \ - for (int i = 0; string1[i] != '\0' && string2[i] != '\0'; i++) { \ - if (string1[i] != string2[i]) { \ - MicroPrintf("FAIL: %s did not match %s", string1, string2, __FILE__, \ - __LINE__); \ - micro_test::did_test_fail = true; \ - } \ - } \ - } while (false) - -#endif // TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_ diff --git a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/flatbuffers.h b/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/flatbuffers.h deleted file mode 100644 index 799f6478..00000000 --- a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/flatbuffers.h +++ /dev/null @@ -1,2827 +0,0 @@ -/* - * Copyright 2014 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FLATBUFFERS_H_ -#define FLATBUFFERS_H_ - -#include "flatbuffers/base.h" -#include "flatbuffers/stl_emulation.h" - -#ifndef FLATBUFFERS_CPP98_STL - #include -#endif - -#if defined(FLATBUFFERS_NAN_DEFAULTS) -# include -#endif - -namespace flatbuffers { -// Generic 'operator==' with conditional specialisations. -// T e - new value of a scalar field. -// T def - default of scalar (is known at compile-time). -template inline bool IsTheSameAs(T e, T def) { return e == def; } - -#if defined(FLATBUFFERS_NAN_DEFAULTS) && \ - defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) -// Like `operator==(e, def)` with weak NaN if T=(float|double). -template inline bool IsFloatTheSameAs(T e, T def) { - return (e == def) || ((def != def) && (e != e)); -} -template<> inline bool IsTheSameAs(float e, float def) { - return IsFloatTheSameAs(e, def); -} -template<> inline bool IsTheSameAs(double e, double def) { - return IsFloatTheSameAs(e, def); -} -#endif - -// Check 'v' is out of closed range [low; high]. -// Workaround for GCC warning [-Werror=type-limits]: -// comparison is always true due to limited range of data type. -template -inline bool IsOutRange(const T &v, const T &low, const T &high) { - return (v < low) || (high < v); -} - -// Check 'v' is in closed range [low; high]. -template -inline bool IsInRange(const T &v, const T &low, const T &high) { - return !IsOutRange(v, low, high); -} - -// Wrapper for uoffset_t to allow safe template specialization. -// Value is allowed to be 0 to indicate a null object (see e.g. AddOffset). -template struct Offset { - uoffset_t o; - Offset() : o(0) {} - Offset(uoffset_t _o) : o(_o) {} - Offset Union() const { return Offset(o); } - bool IsNull() const { return !o; } -}; - -inline void EndianCheck() { - int endiantest = 1; - // If this fails, see FLATBUFFERS_LITTLEENDIAN above. - FLATBUFFERS_ASSERT(*reinterpret_cast(&endiantest) == - FLATBUFFERS_LITTLEENDIAN); - (void)endiantest; -} - -template FLATBUFFERS_CONSTEXPR size_t AlignOf() { - // clang-format off - #ifdef _MSC_VER - return __alignof(T); - #else - #ifndef alignof - return __alignof__(T); - #else - return alignof(T); - #endif - #endif - // clang-format on -} - -// When we read serialized data from memory, in the case of most scalars, -// we want to just read T, but in the case of Offset, we want to actually -// perform the indirection and return a pointer. -// The template specialization below does just that. -// It is wrapped in a struct since function templates can't overload on the -// return type like this. -// The typedef is for the convenience of callers of this function -// (avoiding the need for a trailing return decltype) -template struct IndirectHelper { - typedef T return_type; - typedef T mutable_return_type; - static const size_t element_stride = sizeof(T); - static return_type Read(const uint8_t *p, uoffset_t i) { - return EndianScalar((reinterpret_cast(p))[i]); - } -}; -template struct IndirectHelper> { - typedef const T *return_type; - typedef T *mutable_return_type; - static const size_t element_stride = sizeof(uoffset_t); - static return_type Read(const uint8_t *p, uoffset_t i) { - p += i * sizeof(uoffset_t); - return reinterpret_cast(p + ReadScalar(p)); - } -}; -template struct IndirectHelper { - typedef const T *return_type; - typedef T *mutable_return_type; - static const size_t element_stride = sizeof(T); - static return_type Read(const uint8_t *p, uoffset_t i) { - return reinterpret_cast(p + i * sizeof(T)); - } -}; - -// An STL compatible iterator implementation for Vector below, effectively -// calling Get() for every element. -template struct VectorIterator { - typedef std::random_access_iterator_tag iterator_category; - typedef IT value_type; - typedef ptrdiff_t difference_type; - typedef IT *pointer; - typedef IT &reference; - - VectorIterator(const uint8_t *data, uoffset_t i) - : data_(data + IndirectHelper::element_stride * i) {} - VectorIterator(const VectorIterator &other) : data_(other.data_) {} - VectorIterator() : data_(nullptr) {} - - VectorIterator &operator=(const VectorIterator &other) { - data_ = other.data_; - return *this; - } - - // clang-format off - #if !defined(FLATBUFFERS_CPP98_STL) - VectorIterator &operator=(VectorIterator &&other) { - data_ = other.data_; - return *this; - } - #endif // !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - - bool operator==(const VectorIterator &other) const { - return data_ == other.data_; - } - - bool operator<(const VectorIterator &other) const { - return data_ < other.data_; - } - - bool operator!=(const VectorIterator &other) const { - return data_ != other.data_; - } - - difference_type operator-(const VectorIterator &other) const { - return (data_ - other.data_) / IndirectHelper::element_stride; - } - - IT operator*() const { return IndirectHelper::Read(data_, 0); } - - IT operator->() const { return IndirectHelper::Read(data_, 0); } - - VectorIterator &operator++() { - data_ += IndirectHelper::element_stride; - return *this; - } - - VectorIterator operator++(int) { - VectorIterator temp(data_, 0); - data_ += IndirectHelper::element_stride; - return temp; - } - - VectorIterator operator+(const uoffset_t &offset) const { - return VectorIterator(data_ + offset * IndirectHelper::element_stride, - 0); - } - - VectorIterator &operator+=(const uoffset_t &offset) { - data_ += offset * IndirectHelper::element_stride; - return *this; - } - - VectorIterator &operator--() { - data_ -= IndirectHelper::element_stride; - return *this; - } - - VectorIterator operator--(int) { - VectorIterator temp(data_, 0); - data_ -= IndirectHelper::element_stride; - return temp; - } - - VectorIterator operator-(const uoffset_t &offset) const { - return VectorIterator(data_ - offset * IndirectHelper::element_stride, - 0); - } - - VectorIterator &operator-=(const uoffset_t &offset) { - data_ -= offset * IndirectHelper::element_stride; - return *this; - } - - private: - const uint8_t *data_; -}; - -template -struct VectorReverseIterator : public std::reverse_iterator { - explicit VectorReverseIterator(Iterator iter) - : std::reverse_iterator(iter) {} - - typename Iterator::value_type operator*() const { - return *(std::reverse_iterator::current); - } - - typename Iterator::value_type operator->() const { - return *(std::reverse_iterator::current); - } -}; - -struct String; - -// This is used as a helper type for accessing vectors. -// Vector::data() assumes the vector elements start after the length field. -template class Vector { - public: - typedef VectorIterator::mutable_return_type> - iterator; - typedef VectorIterator::return_type> - const_iterator; - typedef VectorReverseIterator reverse_iterator; - typedef VectorReverseIterator const_reverse_iterator; - - uoffset_t size() const { return EndianScalar(length_); } - - // Deprecated: use size(). Here for backwards compatibility. - FLATBUFFERS_ATTRIBUTE(deprecated("use size() instead")) - uoffset_t Length() const { return size(); } - - typedef typename IndirectHelper::return_type return_type; - typedef typename IndirectHelper::mutable_return_type mutable_return_type; - - return_type Get(uoffset_t i) const { - FLATBUFFERS_ASSERT(i < size()); - return IndirectHelper::Read(Data(), i); - } - - return_type operator[](uoffset_t i) const { return Get(i); } - - // If this is a Vector of enums, T will be its storage type, not the enum - // type. This function makes it convenient to retrieve value with enum - // type E. - template E GetEnum(uoffset_t i) const { - return static_cast(Get(i)); - } - - // If this a vector of unions, this does the cast for you. There's no check - // to make sure this is the right type! - template const U *GetAs(uoffset_t i) const { - return reinterpret_cast(Get(i)); - } - - // If this a vector of unions, this does the cast for you. There's no check - // to make sure this is actually a string! - const String *GetAsString(uoffset_t i) const { - return reinterpret_cast(Get(i)); - } - - const void *GetStructFromOffset(size_t o) const { - return reinterpret_cast(Data() + o); - } - - iterator begin() { return iterator(Data(), 0); } - const_iterator begin() const { return const_iterator(Data(), 0); } - - iterator end() { return iterator(Data(), size()); } - const_iterator end() const { return const_iterator(Data(), size()); } - - reverse_iterator rbegin() { return reverse_iterator(end() - 1); } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end() - 1); - } - - reverse_iterator rend() { return reverse_iterator(begin() - 1); } - const_reverse_iterator rend() const { - return const_reverse_iterator(begin() - 1); - } - - const_iterator cbegin() const { return begin(); } - - const_iterator cend() const { return end(); } - - const_reverse_iterator crbegin() const { return rbegin(); } - - const_reverse_iterator crend() const { return rend(); } - - // Change elements if you have a non-const pointer to this object. - // Scalars only. See reflection.h, and the documentation. - void Mutate(uoffset_t i, const T &val) { - FLATBUFFERS_ASSERT(i < size()); - WriteScalar(data() + i, val); - } - - // Change an element of a vector of tables (or strings). - // "val" points to the new table/string, as you can obtain from - // e.g. reflection::AddFlatBuffer(). - void MutateOffset(uoffset_t i, const uint8_t *val) { - FLATBUFFERS_ASSERT(i < size()); - static_assert(sizeof(T) == sizeof(uoffset_t), "Unrelated types"); - WriteScalar(data() + i, - static_cast(val - (Data() + i * sizeof(uoffset_t)))); - } - - // Get a mutable pointer to tables/strings inside this vector. - mutable_return_type GetMutableObject(uoffset_t i) const { - FLATBUFFERS_ASSERT(i < size()); - return const_cast(IndirectHelper::Read(Data(), i)); - } - - // The raw data in little endian format. Use with care. - const uint8_t *Data() const { - return reinterpret_cast(&length_ + 1); - } - - uint8_t *Data() { return reinterpret_cast(&length_ + 1); } - - // Similarly, but typed, much like std::vector::data - const T *data() const { return reinterpret_cast(Data()); } - T *data() { return reinterpret_cast(Data()); } - - template return_type LookupByKey(K key) const { - void *search_result = std::bsearch( - &key, Data(), size(), IndirectHelper::element_stride, KeyCompare); - - if (!search_result) { - return nullptr; // Key not found. - } - - const uint8_t *element = reinterpret_cast(search_result); - - return IndirectHelper::Read(element, 0); - } - - protected: - // This class is only used to access pre-existing data. Don't ever - // try to construct these manually. - Vector(); - - uoffset_t length_; - - private: - // This class is a pointer. Copying will therefore create an invalid object. - // Private and unimplemented copy constructor. - Vector(const Vector &); - Vector &operator=(const Vector &); - - template static int KeyCompare(const void *ap, const void *bp) { - const K *key = reinterpret_cast(ap); - const uint8_t *data = reinterpret_cast(bp); - auto table = IndirectHelper::Read(data, 0); - - // std::bsearch compares with the operands transposed, so we negate the - // result here. - return -table->KeyCompareWithValue(*key); - } -}; - -// Represent a vector much like the template above, but in this case we -// don't know what the element types are (used with reflection.h). -class VectorOfAny { - public: - uoffset_t size() const { return EndianScalar(length_); } - - const uint8_t *Data() const { - return reinterpret_cast(&length_ + 1); - } - uint8_t *Data() { return reinterpret_cast(&length_ + 1); } - - protected: - VectorOfAny(); - - uoffset_t length_; - - private: - VectorOfAny(const VectorOfAny &); - VectorOfAny &operator=(const VectorOfAny &); -}; - -#ifndef FLATBUFFERS_CPP98_STL -template -Vector> *VectorCast(Vector> *ptr) { - static_assert(std::is_base_of::value, "Unrelated types"); - return reinterpret_cast> *>(ptr); -} - -template -const Vector> *VectorCast(const Vector> *ptr) { - static_assert(std::is_base_of::value, "Unrelated types"); - return reinterpret_cast> *>(ptr); -} -#endif - -// Convenient helper function to get the length of any vector, regardless -// of whether it is null or not (the field is not set). -template static inline size_t VectorLength(const Vector *v) { - return v ? v->size() : 0; -} - -// This is used as a helper type for accessing arrays. -template class Array { - typedef - typename flatbuffers::integral_constant::value> - scalar_tag; - typedef - typename flatbuffers::conditional::type - IndirectHelperType; - - public: - typedef typename IndirectHelper::return_type return_type; - typedef VectorIterator const_iterator; - typedef VectorReverseIterator const_reverse_iterator; - - FLATBUFFERS_CONSTEXPR uint16_t size() const { return length; } - - return_type Get(uoffset_t i) const { - FLATBUFFERS_ASSERT(i < size()); - return IndirectHelper::Read(Data(), i); - } - - return_type operator[](uoffset_t i) const { return Get(i); } - - // If this is a Vector of enums, T will be its storage type, not the enum - // type. This function makes it convenient to retrieve value with enum - // type E. - template E GetEnum(uoffset_t i) const { - return static_cast(Get(i)); - } - - const_iterator begin() const { return const_iterator(Data(), 0); } - const_iterator end() const { return const_iterator(Data(), size()); } - - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - const_reverse_iterator rend() const { return const_reverse_iterator(end()); } - - const_iterator cbegin() const { return begin(); } - const_iterator cend() const { return end(); } - - const_reverse_iterator crbegin() const { return rbegin(); } - const_reverse_iterator crend() const { return rend(); } - - // Get a mutable pointer to elements inside this array. - // This method used to mutate arrays of structs followed by a @p Mutate - // operation. For primitive types use @p Mutate directly. - // @warning Assignments and reads to/from the dereferenced pointer are not - // automatically converted to the correct endianness. - typename flatbuffers::conditional::type - GetMutablePointer(uoffset_t i) const { - FLATBUFFERS_ASSERT(i < size()); - return const_cast(&data()[i]); - } - - // Change elements if you have a non-const pointer to this object. - void Mutate(uoffset_t i, const T &val) { MutateImpl(scalar_tag(), i, val); } - - // The raw data in little endian format. Use with care. - const uint8_t *Data() const { return data_; } - - uint8_t *Data() { return data_; } - - // Similarly, but typed, much like std::vector::data - const T *data() const { return reinterpret_cast(Data()); } - T *data() { return reinterpret_cast(Data()); } - - protected: - void MutateImpl(flatbuffers::integral_constant, uoffset_t i, - const T &val) { - FLATBUFFERS_ASSERT(i < size()); - WriteScalar(data() + i, val); - } - - void MutateImpl(flatbuffers::integral_constant, uoffset_t i, - const T &val) { - *(GetMutablePointer(i)) = val; - } - - // This class is only used to access pre-existing data. Don't ever - // try to construct these manually. - // 'constexpr' allows us to use 'size()' at compile time. - // @note Must not use 'FLATBUFFERS_CONSTEXPR' here, as const is not allowed on - // a constructor. -#if defined(__cpp_constexpr) - constexpr Array(); -#else - Array(); -#endif - - uint8_t data_[length * sizeof(T)]; - - private: - // This class is a pointer. Copying will therefore create an invalid object. - // Private and unimplemented copy constructor. - Array(const Array &); - Array &operator=(const Array &); -}; - -// Specialization for Array[struct] with access using Offset pointer. -// This specialization used by idl_gen_text.cpp. -template class Array, length> { - static_assert(flatbuffers::is_same::value, "unexpected type T"); - - public: - typedef const void *return_type; - - const uint8_t *Data() const { return data_; } - - // Make idl_gen_text.cpp::PrintContainer happy. - return_type operator[](uoffset_t) const { - FLATBUFFERS_ASSERT(false); - return nullptr; - } - - private: - // This class is only used to access pre-existing data. - Array(); - Array(const Array &); - Array &operator=(const Array &); - - uint8_t data_[1]; -}; - -// Lexicographically compare two strings (possibly containing nulls), and -// return true if the first is less than the second. -static inline bool StringLessThan(const char *a_data, uoffset_t a_size, - const char *b_data, uoffset_t b_size) { - const auto cmp = memcmp(a_data, b_data, (std::min)(a_size, b_size)); - return cmp == 0 ? a_size < b_size : cmp < 0; -} - -struct String : public Vector { - const char *c_str() const { return reinterpret_cast(Data()); } - std::string str() const { return std::string(c_str(), size()); } - - // clang-format off - #ifdef FLATBUFFERS_HAS_STRING_VIEW - flatbuffers::string_view string_view() const { - return flatbuffers::string_view(c_str(), size()); - } - #endif // FLATBUFFERS_HAS_STRING_VIEW - // clang-format on - - bool operator<(const String &o) const { - return StringLessThan(this->data(), this->size(), o.data(), o.size()); - } -}; - -// Convenience function to get std::string from a String returning an empty -// string on null pointer. -static inline std::string GetString(const String *str) { - return str ? str->str() : ""; -} - -// Convenience function to get char* from a String returning an empty string on -// null pointer. -static inline const char *GetCstring(const String *str) { - return str ? str->c_str() : ""; -} - -#ifdef FLATBUFFERS_HAS_STRING_VIEW -// Convenience function to get string_view from a String returning an empty -// string_view on null pointer. -static inline flatbuffers::string_view GetStringView(const String *str) { - return str ? str->string_view() : flatbuffers::string_view(); -} -#endif // FLATBUFFERS_HAS_STRING_VIEW - -// Allocator interface. This is flatbuffers-specific and meant only for -// `vector_downward` usage. -class Allocator { - public: - virtual ~Allocator() {} - - // Allocate `size` bytes of memory. - virtual uint8_t *allocate(size_t size) = 0; - - // Deallocate `size` bytes of memory at `p` allocated by this allocator. - virtual void deallocate(uint8_t *p, size_t size) = 0; - - // Reallocate `new_size` bytes of memory, replacing the old region of size - // `old_size` at `p`. In contrast to a normal realloc, this grows downwards, - // and is intended specifcally for `vector_downward` use. - // `in_use_back` and `in_use_front` indicate how much of `old_size` is - // actually in use at each end, and needs to be copied. - virtual uint8_t *reallocate_downward(uint8_t *old_p, size_t old_size, - size_t new_size, size_t in_use_back, - size_t in_use_front) { - FLATBUFFERS_ASSERT(new_size > old_size); // vector_downward only grows - uint8_t *new_p = allocate(new_size); - memcpy_downward(old_p, old_size, new_p, new_size, in_use_back, - in_use_front); - deallocate(old_p, old_size); - return new_p; - } - - protected: - // Called by `reallocate_downward` to copy memory from `old_p` of `old_size` - // to `new_p` of `new_size`. Only memory of size `in_use_front` and - // `in_use_back` will be copied from the front and back of the old memory - // allocation. - void memcpy_downward(uint8_t *old_p, size_t old_size, uint8_t *new_p, - size_t new_size, size_t in_use_back, - size_t in_use_front) { - memcpy(new_p + new_size - in_use_back, old_p + old_size - in_use_back, - in_use_back); - memcpy(new_p, old_p, in_use_front); - } -}; - -// DefaultAllocator uses new/delete to allocate memory regions -class DefaultAllocator : public Allocator { - public: - uint8_t *allocate(size_t size) FLATBUFFERS_OVERRIDE { - return new uint8_t[size]; - } - - void deallocate(uint8_t *p, size_t) FLATBUFFERS_OVERRIDE { delete[] p; } - - static void dealloc(void *p, size_t) { delete[] static_cast(p); } -}; - -// These functions allow for a null allocator to mean use the default allocator, -// as used by DetachedBuffer and vector_downward below. -// This is to avoid having a statically or dynamically allocated default -// allocator, or having to move it between the classes that may own it. -inline uint8_t *Allocate(Allocator *allocator, size_t size) { - return allocator ? allocator->allocate(size) - : DefaultAllocator().allocate(size); -} - -inline void Deallocate(Allocator *allocator, uint8_t *p, size_t size) { - if (allocator) - allocator->deallocate(p, size); - else - DefaultAllocator().deallocate(p, size); -} - -inline uint8_t *ReallocateDownward(Allocator *allocator, uint8_t *old_p, - size_t old_size, size_t new_size, - size_t in_use_back, size_t in_use_front) { - return allocator ? allocator->reallocate_downward(old_p, old_size, new_size, - in_use_back, in_use_front) - : DefaultAllocator().reallocate_downward( - old_p, old_size, new_size, in_use_back, in_use_front); -} - -// DetachedBuffer is a finished flatbuffer memory region, detached from its -// builder. The original memory region and allocator are also stored so that -// the DetachedBuffer can manage the memory lifetime. -class DetachedBuffer { - public: - DetachedBuffer() - : allocator_(nullptr), - own_allocator_(false), - buf_(nullptr), - reserved_(0), - cur_(nullptr), - size_(0) {} - - DetachedBuffer(Allocator *allocator, bool own_allocator, uint8_t *buf, - size_t reserved, uint8_t *cur, size_t sz) - : allocator_(allocator), - own_allocator_(own_allocator), - buf_(buf), - reserved_(reserved), - cur_(cur), - size_(sz) {} - - // clang-format off - #if !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - DetachedBuffer(DetachedBuffer &&other) - : allocator_(other.allocator_), - own_allocator_(other.own_allocator_), - buf_(other.buf_), - reserved_(other.reserved_), - cur_(other.cur_), - size_(other.size_) { - other.reset(); - } - // clang-format off - #endif // !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - - // clang-format off - #if !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - DetachedBuffer &operator=(DetachedBuffer &&other) { - if (this == &other) return *this; - - destroy(); - - allocator_ = other.allocator_; - own_allocator_ = other.own_allocator_; - buf_ = other.buf_; - reserved_ = other.reserved_; - cur_ = other.cur_; - size_ = other.size_; - - other.reset(); - - return *this; - } - // clang-format off - #endif // !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - - ~DetachedBuffer() { destroy(); } - - const uint8_t *data() const { return cur_; } - - uint8_t *data() { return cur_; } - - size_t size() const { return size_; } - - // clang-format off - #if 0 // disabled for now due to the ordering of classes in this header - template - bool Verify() const { - Verifier verifier(data(), size()); - return verifier.Verify(nullptr); - } - - template - const T* GetRoot() const { - return flatbuffers::GetRoot(data()); - } - - template - T* GetRoot() { - return flatbuffers::GetRoot(data()); - } - #endif - // clang-format on - - // clang-format off - #if !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - // These may change access mode, leave these at end of public section - FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer &other)) - FLATBUFFERS_DELETE_FUNC( - DetachedBuffer &operator=(const DetachedBuffer &other)) - // clang-format off - #endif // !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - - protected: - Allocator *allocator_; - bool own_allocator_; - uint8_t *buf_; - size_t reserved_; - uint8_t *cur_; - size_t size_; - - inline void destroy() { - if (buf_) Deallocate(allocator_, buf_, reserved_); - if (own_allocator_ && allocator_) { delete allocator_; } - reset(); - } - - inline void reset() { - allocator_ = nullptr; - own_allocator_ = false; - buf_ = nullptr; - reserved_ = 0; - cur_ = nullptr; - size_ = 0; - } -}; - -// This is a minimal replication of std::vector functionality, -// except growing from higher to lower addresses. i.e push_back() inserts data -// in the lowest address in the vector. -// Since this vector leaves the lower part unused, we support a "scratch-pad" -// that can be stored there for temporary data, to share the allocated space. -// Essentially, this supports 2 std::vectors in a single buffer. -class vector_downward { - public: - explicit vector_downward(size_t initial_size, Allocator *allocator, - bool own_allocator, size_t buffer_minalign) - : allocator_(allocator), - own_allocator_(own_allocator), - initial_size_(initial_size), - buffer_minalign_(buffer_minalign), - reserved_(0), - buf_(nullptr), - cur_(nullptr), - scratch_(nullptr) {} - - // clang-format off - #if !defined(FLATBUFFERS_CPP98_STL) - vector_downward(vector_downward &&other) - #else - vector_downward(vector_downward &other) - #endif // defined(FLATBUFFERS_CPP98_STL) - // clang-format on - : allocator_(other.allocator_), - own_allocator_(other.own_allocator_), - initial_size_(other.initial_size_), - buffer_minalign_(other.buffer_minalign_), - reserved_(other.reserved_), - buf_(other.buf_), - cur_(other.cur_), - scratch_(other.scratch_) { - // No change in other.allocator_ - // No change in other.initial_size_ - // No change in other.buffer_minalign_ - other.own_allocator_ = false; - other.reserved_ = 0; - other.buf_ = nullptr; - other.cur_ = nullptr; - other.scratch_ = nullptr; - } - - // clang-format off - #if !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - vector_downward &operator=(vector_downward &&other) { - // Move construct a temporary and swap idiom - vector_downward temp(std::move(other)); - swap(temp); - return *this; - } - // clang-format off - #endif // defined(FLATBUFFERS_CPP98_STL) - // clang-format on - - ~vector_downward() { - clear_buffer(); - clear_allocator(); - } - - void reset() { - clear_buffer(); - clear(); - } - - void clear() { - if (buf_) { - cur_ = buf_ + reserved_; - } else { - reserved_ = 0; - cur_ = nullptr; - } - clear_scratch(); - } - - void clear_scratch() { scratch_ = buf_; } - - void clear_allocator() { - if (own_allocator_ && allocator_) { delete allocator_; } - allocator_ = nullptr; - own_allocator_ = false; - } - - void clear_buffer() { - if (buf_) Deallocate(allocator_, buf_, reserved_); - buf_ = nullptr; - } - - // Relinquish the pointer to the caller. - uint8_t *release_raw(size_t &allocated_bytes, size_t &offset) { - auto *buf = buf_; - allocated_bytes = reserved_; - offset = static_cast(cur_ - buf_); - - // release_raw only relinquishes the buffer ownership. - // Does not deallocate or reset the allocator. Destructor will do that. - buf_ = nullptr; - clear(); - return buf; - } - - // Relinquish the pointer to the caller. - DetachedBuffer release() { - // allocator ownership (if any) is transferred to DetachedBuffer. - DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_, - size()); - if (own_allocator_) { - allocator_ = nullptr; - own_allocator_ = false; - } - buf_ = nullptr; - clear(); - return fb; - } - - size_t ensure_space(size_t len) { - FLATBUFFERS_ASSERT(cur_ >= scratch_ && scratch_ >= buf_); - if (len > static_cast(cur_ - scratch_)) { reallocate(len); } - // Beyond this, signed offsets may not have enough range: - // (FlatBuffers > 2GB not supported). - FLATBUFFERS_ASSERT(size() < FLATBUFFERS_MAX_BUFFER_SIZE); - return len; - } - - inline uint8_t *make_space(size_t len) { - size_t space = ensure_space(len); - cur_ -= space; - return cur_; - } - - // Returns nullptr if using the DefaultAllocator. - Allocator *get_custom_allocator() { return allocator_; } - - uoffset_t size() const { - return static_cast(reserved_ - (cur_ - buf_)); - } - - uoffset_t scratch_size() const { - return static_cast(scratch_ - buf_); - } - - size_t capacity() const { return reserved_; } - - uint8_t *data() const { - FLATBUFFERS_ASSERT(cur_); - return cur_; - } - - uint8_t *scratch_data() const { - FLATBUFFERS_ASSERT(buf_); - return buf_; - } - - uint8_t *scratch_end() const { - FLATBUFFERS_ASSERT(scratch_); - return scratch_; - } - - uint8_t *data_at(size_t offset) const { return buf_ + reserved_ - offset; } - - void push(const uint8_t *bytes, size_t num) { - if (num > 0) { memcpy(make_space(num), bytes, num); } - } - - // Specialized version of push() that avoids memcpy call for small data. - template void push_small(const T &little_endian_t) { - make_space(sizeof(T)); - *reinterpret_cast(cur_) = little_endian_t; - } - - template void scratch_push_small(const T &t) { - ensure_space(sizeof(T)); - *reinterpret_cast(scratch_) = t; - scratch_ += sizeof(T); - } - - // fill() is most frequently called with small byte counts (<= 4), - // which is why we're using loops rather than calling memset. - void fill(size_t zero_pad_bytes) { - make_space(zero_pad_bytes); - for (size_t i = 0; i < zero_pad_bytes; i++) cur_[i] = 0; - } - - // Version for when we know the size is larger. - // Precondition: zero_pad_bytes > 0 - void fill_big(size_t zero_pad_bytes) { - memset(make_space(zero_pad_bytes), 0, zero_pad_bytes); - } - - void pop(size_t bytes_to_remove) { cur_ += bytes_to_remove; } - void scratch_pop(size_t bytes_to_remove) { scratch_ -= bytes_to_remove; } - - void swap(vector_downward &other) { - using std::swap; - swap(allocator_, other.allocator_); - swap(own_allocator_, other.own_allocator_); - swap(initial_size_, other.initial_size_); - swap(buffer_minalign_, other.buffer_minalign_); - swap(reserved_, other.reserved_); - swap(buf_, other.buf_); - swap(cur_, other.cur_); - swap(scratch_, other.scratch_); - } - - void swap_allocator(vector_downward &other) { - using std::swap; - swap(allocator_, other.allocator_); - swap(own_allocator_, other.own_allocator_); - } - - private: - // You shouldn't really be copying instances of this class. - FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &)) - FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &)) - - Allocator *allocator_; - bool own_allocator_; - size_t initial_size_; - size_t buffer_minalign_; - size_t reserved_; - uint8_t *buf_; - uint8_t *cur_; // Points at location between empty (below) and used (above). - uint8_t *scratch_; // Points to the end of the scratchpad in use. - - void reallocate(size_t len) { - auto old_reserved = reserved_; - auto old_size = size(); - auto old_scratch_size = scratch_size(); - reserved_ += - (std::max)(len, old_reserved ? old_reserved / 2 : initial_size_); - reserved_ = (reserved_ + buffer_minalign_ - 1) & ~(buffer_minalign_ - 1); - if (buf_) { - buf_ = ReallocateDownward(allocator_, buf_, old_reserved, reserved_, - old_size, old_scratch_size); - } else { - buf_ = Allocate(allocator_, reserved_); - } - cur_ = buf_ + reserved_ - old_size; - scratch_ = buf_ + old_scratch_size; - } -}; - -// Converts a Field ID to a virtual table offset. -inline voffset_t FieldIndexToOffset(voffset_t field_id) { - // Should correspond to what EndTable() below builds up. - const int fixed_fields = 2; // Vtable size and Object Size. - return static_cast((field_id + fixed_fields) * sizeof(voffset_t)); -} - -template -const T *data(const std::vector &v) { - // Eventually the returned pointer gets passed down to memcpy, so - // we need it to be non-null to avoid undefined behavior. - static uint8_t t; - return v.empty() ? reinterpret_cast(&t) : &v.front(); -} -template T *data(std::vector &v) { - // Eventually the returned pointer gets passed down to memcpy, so - // we need it to be non-null to avoid undefined behavior. - static uint8_t t; - return v.empty() ? reinterpret_cast(&t) : &v.front(); -} - -/// @endcond - -/// @addtogroup flatbuffers_cpp_api -/// @{ -/// @class FlatBufferBuilder -/// @brief Helper class to hold data needed in creation of a FlatBuffer. -/// To serialize data, you typically call one of the `Create*()` functions in -/// the generated code, which in turn call a sequence of `StartTable`/ -/// `PushElement`/`AddElement`/`EndTable`, or the builtin `CreateString`/ -/// `CreateVector` functions. Do this is depth-first order to build up a tree to -/// the root. `Finish()` wraps up the buffer ready for transport. -class FlatBufferBuilder { - public: - /// @brief Default constructor for FlatBufferBuilder. - /// @param[in] initial_size The initial size of the buffer, in bytes. Defaults - /// to `1024`. - /// @param[in] allocator An `Allocator` to use. If null will use - /// `DefaultAllocator`. - /// @param[in] own_allocator Whether the builder/vector should own the - /// allocator. Defaults to / `false`. - /// @param[in] buffer_minalign Force the buffer to be aligned to the given - /// minimum alignment upon reallocation. Only needed if you intend to store - /// types with custom alignment AND you wish to read the buffer in-place - /// directly after creation. - explicit FlatBufferBuilder( - size_t initial_size = 1024, Allocator *allocator = nullptr, - bool own_allocator = false, - size_t buffer_minalign = AlignOf()) - : buf_(initial_size, allocator, own_allocator, buffer_minalign), - num_field_loc(0), - max_voffset_(0), - nested(false), - finished(false), - minalign_(1), - force_defaults_(false), - dedup_vtables_(true), - string_pool(nullptr) { - EndianCheck(); - } - - // clang-format off - /// @brief Move constructor for FlatBufferBuilder. - #if !defined(FLATBUFFERS_CPP98_STL) - FlatBufferBuilder(FlatBufferBuilder &&other) - #else - FlatBufferBuilder(FlatBufferBuilder &other) - #endif // #if !defined(FLATBUFFERS_CPP98_STL) - : buf_(1024, nullptr, false, AlignOf()), - num_field_loc(0), - max_voffset_(0), - nested(false), - finished(false), - minalign_(1), - force_defaults_(false), - dedup_vtables_(true), - string_pool(nullptr) { - EndianCheck(); - // Default construct and swap idiom. - // Lack of delegating constructors in vs2010 makes it more verbose than needed. - Swap(other); - } - // clang-format on - - // clang-format off - #if !defined(FLATBUFFERS_CPP98_STL) - // clang-format on - /// @brief Move assignment operator for FlatBufferBuilder. - FlatBufferBuilder &operator=(FlatBufferBuilder &&other) { - // Move construct a temporary and swap idiom - FlatBufferBuilder temp(std::move(other)); - Swap(temp); - return *this; - } - // clang-format off - #endif // defined(FLATBUFFERS_CPP98_STL) - // clang-format on - - void Swap(FlatBufferBuilder &other) { - using std::swap; - buf_.swap(other.buf_); - swap(num_field_loc, other.num_field_loc); - swap(max_voffset_, other.max_voffset_); - swap(nested, other.nested); - swap(finished, other.finished); - swap(minalign_, other.minalign_); - swap(force_defaults_, other.force_defaults_); - swap(dedup_vtables_, other.dedup_vtables_); - swap(string_pool, other.string_pool); - } - - ~FlatBufferBuilder() { - if (string_pool) delete string_pool; - } - - void Reset() { - Clear(); // clear builder state - buf_.reset(); // deallocate buffer - } - - /// @brief Reset all the state in this FlatBufferBuilder so it can be reused - /// to construct another buffer. - void Clear() { - ClearOffsets(); - buf_.clear(); - nested = false; - finished = false; - minalign_ = 1; - if (string_pool) string_pool->clear(); - } - - /// @brief The current size of the serialized buffer, counting from the end. - /// @return Returns an `uoffset_t` with the current size of the buffer. - uoffset_t GetSize() const { return buf_.size(); } - - /// @brief Get the serialized buffer (after you call `Finish()`). - /// @return Returns an `uint8_t` pointer to the FlatBuffer data inside the - /// buffer. - uint8_t *GetBufferPointer() const { - Finished(); - return buf_.data(); - } - - /// @brief Get a pointer to an unfinished buffer. - /// @return Returns a `uint8_t` pointer to the unfinished buffer. - uint8_t *GetCurrentBufferPointer() const { return buf_.data(); } - - /// @brief Get the released pointer to the serialized buffer. - /// @warning Do NOT attempt to use this FlatBufferBuilder afterwards! - /// @return A `FlatBuffer` that owns the buffer and its allocator and - /// behaves similar to a `unique_ptr` with a deleter. - FLATBUFFERS_ATTRIBUTE(deprecated("use Release() instead")) - DetachedBuffer ReleaseBufferPointer() { - Finished(); - return buf_.release(); - } - - /// @brief Get the released DetachedBuffer. - /// @return A `DetachedBuffer` that owns the buffer and its allocator. - DetachedBuffer Release() { - Finished(); - return buf_.release(); - } - - /// @brief Get the released pointer to the serialized buffer. - /// @param size The size of the memory block containing - /// the serialized `FlatBuffer`. - /// @param offset The offset from the released pointer where the finished - /// `FlatBuffer` starts. - /// @return A raw pointer to the start of the memory block containing - /// the serialized `FlatBuffer`. - /// @remark If the allocator is owned, it gets deleted when the destructor is - /// called.. - uint8_t *ReleaseRaw(size_t &size, size_t &offset) { - Finished(); - return buf_.release_raw(size, offset); - } - - /// @brief get the minimum alignment this buffer needs to be accessed - /// properly. This is only known once all elements have been written (after - /// you call Finish()). You can use this information if you need to embed - /// a FlatBuffer in some other buffer, such that you can later read it - /// without first having to copy it into its own buffer. - size_t GetBufferMinAlignment() const { - Finished(); - return minalign_; - } - - /// @cond FLATBUFFERS_INTERNAL - void Finished() const { - // If you get this assert, you're attempting to get access a buffer - // which hasn't been finished yet. Be sure to call - // FlatBufferBuilder::Finish with your root table. - // If you really need to access an unfinished buffer, call - // GetCurrentBufferPointer instead. - FLATBUFFERS_ASSERT(finished); - } - /// @endcond - - /// @brief In order to save space, fields that are set to their default value - /// don't get serialized into the buffer. - /// @param[in] fd When set to `true`, always serializes default values that - /// are set. Optional fields which are not set explicitly, will still not be - /// serialized. - void ForceDefaults(bool fd) { force_defaults_ = fd; } - - /// @brief By default vtables are deduped in order to save space. - /// @param[in] dedup When set to `true`, dedup vtables. - void DedupVtables(bool dedup) { dedup_vtables_ = dedup; } - - /// @cond FLATBUFFERS_INTERNAL - void Pad(size_t num_bytes) { buf_.fill(num_bytes); } - - void TrackMinAlign(size_t elem_size) { - if (elem_size > minalign_) minalign_ = elem_size; - } - - void Align(size_t elem_size) { - TrackMinAlign(elem_size); - buf_.fill(PaddingBytes(buf_.size(), elem_size)); - } - - void PushFlatBuffer(const uint8_t *bytes, size_t size) { - PushBytes(bytes, size); - finished = true; - } - - void PushBytes(const uint8_t *bytes, size_t size) { buf_.push(bytes, size); } - - void PopBytes(size_t amount) { buf_.pop(amount); } - - template void AssertScalarT() { - // The code assumes power of 2 sizes and endian-swap-ability. - static_assert(flatbuffers::is_scalar::value, "T must be a scalar type"); - } - - // Write a single aligned scalar to the buffer - template uoffset_t PushElement(T element) { - AssertScalarT(); - T litle_endian_element = EndianScalar(element); - Align(sizeof(T)); - buf_.push_small(litle_endian_element); - return GetSize(); - } - - template uoffset_t PushElement(Offset off) { - // Special case for offsets: see ReferTo below. - return PushElement(ReferTo(off.o)); - } - - // When writing fields, we track where they are, so we can create correct - // vtables later. - void TrackField(voffset_t field, uoffset_t off) { - FieldLoc fl = { off, field }; - buf_.scratch_push_small(fl); - num_field_loc++; - max_voffset_ = (std::max)(max_voffset_, field); - } - - // Like PushElement, but additionally tracks the field this represents. - template void AddElement(voffset_t field, T e, T def) { - // We don't serialize values equal to the default. - if (IsTheSameAs(e, def) && !force_defaults_) return; - auto off = PushElement(e); - TrackField(field, off); - } - - template void AddElement(voffset_t field, T e) { - auto off = PushElement(e); - TrackField(field, off); - } - - template void AddOffset(voffset_t field, Offset off) { - if (off.IsNull()) return; // Don't store. - AddElement(field, ReferTo(off.o), static_cast(0)); - } - - template void AddStruct(voffset_t field, const T *structptr) { - if (!structptr) return; // Default, don't store. - Align(AlignOf()); - buf_.push_small(*structptr); - TrackField(field, GetSize()); - } - - void AddStructOffset(voffset_t field, uoffset_t off) { - TrackField(field, off); - } - - // Offsets initially are relative to the end of the buffer (downwards). - // This function converts them to be relative to the current location - // in the buffer (when stored here), pointing upwards. - uoffset_t ReferTo(uoffset_t off) { - // Align to ensure GetSize() below is correct. - Align(sizeof(uoffset_t)); - // Offset must refer to something already in buffer. - FLATBUFFERS_ASSERT(off && off <= GetSize()); - return GetSize() - off + static_cast(sizeof(uoffset_t)); - } - - void NotNested() { - // If you hit this, you're trying to construct a Table/Vector/String - // during the construction of its parent table (between the MyTableBuilder - // and table.Finish(). - // Move the creation of these sub-objects to above the MyTableBuilder to - // not get this assert. - // Ignoring this assert may appear to work in simple cases, but the reason - // it is here is that storing objects in-line may cause vtable offsets - // to not fit anymore. It also leads to vtable duplication. - FLATBUFFERS_ASSERT(!nested); - // If you hit this, fields were added outside the scope of a table. - FLATBUFFERS_ASSERT(!num_field_loc); - } - - // From generated code (or from the parser), we call StartTable/EndTable - // with a sequence of AddElement calls in between. - uoffset_t StartTable() { - NotNested(); - nested = true; - return GetSize(); - } - - // This finishes one serialized object by generating the vtable if it's a - // table, comparing it against existing vtables, and writing the - // resulting vtable offset. - uoffset_t EndTable(uoffset_t start) { - // If you get this assert, a corresponding StartTable wasn't called. - FLATBUFFERS_ASSERT(nested); - // Write the vtable offset, which is the start of any Table. - // We fill it's value later. - auto vtableoffsetloc = PushElement(0); - // Write a vtable, which consists entirely of voffset_t elements. - // It starts with the number of offsets, followed by a type id, followed - // by the offsets themselves. In reverse: - // Include space for the last offset and ensure empty tables have a - // minimum size. - max_voffset_ = - (std::max)(static_cast(max_voffset_ + sizeof(voffset_t)), - FieldIndexToOffset(0)); - buf_.fill_big(max_voffset_); - auto table_object_size = vtableoffsetloc - start; - // Vtable use 16bit offsets. - FLATBUFFERS_ASSERT(table_object_size < 0x10000); - WriteScalar(buf_.data() + sizeof(voffset_t), - static_cast(table_object_size)); - WriteScalar(buf_.data(), max_voffset_); - // Write the offsets into the table - for (auto it = buf_.scratch_end() - num_field_loc * sizeof(FieldLoc); - it < buf_.scratch_end(); it += sizeof(FieldLoc)) { - auto field_location = reinterpret_cast(it); - auto pos = static_cast(vtableoffsetloc - field_location->off); - // If this asserts, it means you've set a field twice. - FLATBUFFERS_ASSERT( - !ReadScalar(buf_.data() + field_location->id)); - WriteScalar(buf_.data() + field_location->id, pos); - } - ClearOffsets(); - auto vt1 = reinterpret_cast(buf_.data()); - auto vt1_size = ReadScalar(vt1); - auto vt_use = GetSize(); - // See if we already have generated a vtable with this exact same - // layout before. If so, make it point to the old one, remove this one. - if (dedup_vtables_) { - for (auto it = buf_.scratch_data(); it < buf_.scratch_end(); - it += sizeof(uoffset_t)) { - auto vt_offset_ptr = reinterpret_cast(it); - auto vt2 = reinterpret_cast(buf_.data_at(*vt_offset_ptr)); - auto vt2_size = ReadScalar(vt2); - if (vt1_size != vt2_size || 0 != memcmp(vt2, vt1, vt1_size)) continue; - vt_use = *vt_offset_ptr; - buf_.pop(GetSize() - vtableoffsetloc); - break; - } - } - // If this is a new vtable, remember it. - if (vt_use == GetSize()) { buf_.scratch_push_small(vt_use); } - // Fill the vtable offset we created above. - // The offset points from the beginning of the object to where the - // vtable is stored. - // Offsets default direction is downward in memory for future format - // flexibility (storing all vtables at the start of the file). - WriteScalar(buf_.data_at(vtableoffsetloc), - static_cast(vt_use) - - static_cast(vtableoffsetloc)); - - nested = false; - return vtableoffsetloc; - } - - FLATBUFFERS_ATTRIBUTE(deprecated("call the version above instead")) - uoffset_t EndTable(uoffset_t start, voffset_t /*numfields*/) { - return EndTable(start); - } - - // This checks a required field has been set in a given table that has - // just been constructed. - template void Required(Offset table, voffset_t field); - - uoffset_t StartStruct(size_t alignment) { - Align(alignment); - return GetSize(); - } - - uoffset_t EndStruct() { return GetSize(); } - - void ClearOffsets() { - buf_.scratch_pop(num_field_loc * sizeof(FieldLoc)); - num_field_loc = 0; - max_voffset_ = 0; - } - - // Aligns such that when "len" bytes are written, an object can be written - // after it with "alignment" without padding. - void PreAlign(size_t len, size_t alignment) { - TrackMinAlign(alignment); - buf_.fill(PaddingBytes(GetSize() + len, alignment)); - } - template void PreAlign(size_t len) { - AssertScalarT(); - PreAlign(len, sizeof(T)); - } - /// @endcond - - /// @brief Store a string in the buffer, which can contain any binary data. - /// @param[in] str A const char pointer to the data to be stored as a string. - /// @param[in] len The number of bytes that should be stored from `str`. - /// @return Returns the offset in the buffer where the string starts. - Offset CreateString(const char *str, size_t len) { - NotNested(); - PreAlign(len + 1); // Always 0-terminated. - buf_.fill(1); - PushBytes(reinterpret_cast(str), len); - PushElement(static_cast(len)); - return Offset(GetSize()); - } - - /// @brief Store a string in the buffer, which is null-terminated. - /// @param[in] str A const char pointer to a C-string to add to the buffer. - /// @return Returns the offset in the buffer where the string starts. - Offset CreateString(const char *str) { - return CreateString(str, strlen(str)); - } - - /// @brief Store a string in the buffer, which is null-terminated. - /// @param[in] str A char pointer to a C-string to add to the buffer. - /// @return Returns the offset in the buffer where the string starts. - Offset CreateString(char *str) { - return CreateString(str, strlen(str)); - } - - /// @brief Store a string in the buffer, which can contain any binary data. - /// @param[in] str A const reference to a std::string to store in the buffer. - /// @return Returns the offset in the buffer where the string starts. - Offset CreateString(const std::string &str) { - return CreateString(str.c_str(), str.length()); - } - - // clang-format off - #ifdef FLATBUFFERS_HAS_STRING_VIEW - /// @brief Store a string in the buffer, which can contain any binary data. - /// @param[in] str A const string_view to copy in to the buffer. - /// @return Returns the offset in the buffer where the string starts. - Offset CreateString(flatbuffers::string_view str) { - return CreateString(str.data(), str.size()); - } - #endif // FLATBUFFERS_HAS_STRING_VIEW - // clang-format on - - /// @brief Store a string in the buffer, which can contain any binary data. - /// @param[in] str A const pointer to a `String` struct to add to the buffer. - /// @return Returns the offset in the buffer where the string starts - Offset CreateString(const String *str) { - return str ? CreateString(str->c_str(), str->size()) : 0; - } - - /// @brief Store a string in the buffer, which can contain any binary data. - /// @param[in] str A const reference to a std::string like type with support - /// of T::c_str() and T::length() to store in the buffer. - /// @return Returns the offset in the buffer where the string starts. - template Offset CreateString(const T &str) { - return CreateString(str.c_str(), str.length()); - } - - /// @brief Store a string in the buffer, which can contain any binary data. - /// If a string with this exact contents has already been serialized before, - /// instead simply returns the offset of the existing string. - /// @param[in] str A const char pointer to the data to be stored as a string. - /// @param[in] len The number of bytes that should be stored from `str`. - /// @return Returns the offset in the buffer where the string starts. - Offset CreateSharedString(const char *str, size_t len) { - if (!string_pool) - string_pool = new StringOffsetMap(StringOffsetCompare(buf_)); - auto size_before_string = buf_.size(); - // Must first serialize the string, since the set is all offsets into - // buffer. - auto off = CreateString(str, len); - auto it = string_pool->find(off); - // If it exists we reuse existing serialized data! - if (it != string_pool->end()) { - // We can remove the string we serialized. - buf_.pop(buf_.size() - size_before_string); - return *it; - } - // Record this string for future use. - string_pool->insert(off); - return off; - } - - /// @brief Store a string in the buffer, which null-terminated. - /// If a string with this exact contents has already been serialized before, - /// instead simply returns the offset of the existing string. - /// @param[in] str A const char pointer to a C-string to add to the buffer. - /// @return Returns the offset in the buffer where the string starts. - Offset CreateSharedString(const char *str) { - return CreateSharedString(str, strlen(str)); - } - - /// @brief Store a string in the buffer, which can contain any binary data. - /// If a string with this exact contents has already been serialized before, - /// instead simply returns the offset of the existing string. - /// @param[in] str A const reference to a std::string to store in the buffer. - /// @return Returns the offset in the buffer where the string starts. - Offset CreateSharedString(const std::string &str) { - return CreateSharedString(str.c_str(), str.length()); - } - - /// @brief Store a string in the buffer, which can contain any binary data. - /// If a string with this exact contents has already been serialized before, - /// instead simply returns the offset of the existing string. - /// @param[in] str A const pointer to a `String` struct to add to the buffer. - /// @return Returns the offset in the buffer where the string starts - Offset CreateSharedString(const String *str) { - return CreateSharedString(str->c_str(), str->size()); - } - - /// @cond FLATBUFFERS_INTERNAL - uoffset_t EndVector(size_t len) { - FLATBUFFERS_ASSERT(nested); // Hit if no corresponding StartVector. - nested = false; - return PushElement(static_cast(len)); - } - - void StartVector(size_t len, size_t elemsize) { - NotNested(); - nested = true; - PreAlign(len * elemsize); - PreAlign(len * elemsize, elemsize); // Just in case elemsize > uoffset_t. - } - - // Call this right before StartVector/CreateVector if you want to force the - // alignment to be something different than what the element size would - // normally dictate. - // This is useful when storing a nested_flatbuffer in a vector of bytes, - // or when storing SIMD floats, etc. - void ForceVectorAlignment(size_t len, size_t elemsize, size_t alignment) { - PreAlign(len * elemsize, alignment); - } - - // Similar to ForceVectorAlignment but for String fields. - void ForceStringAlignment(size_t len, size_t alignment) { - PreAlign((len + 1) * sizeof(char), alignment); - } - - /// @endcond - - /// @brief Serialize an array into a FlatBuffer `vector`. - /// @tparam T The data type of the array elements. - /// @param[in] v A pointer to the array of type `T` to serialize into the - /// buffer as a `vector`. - /// @param[in] len The number of elements to serialize. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template Offset> CreateVector(const T *v, size_t len) { - // If this assert hits, you're specifying a template argument that is - // causing the wrong overload to be selected, remove it. - AssertScalarT(); - StartVector(len, sizeof(T)); - if (len == 0) { - return Offset>(EndVector(len)); - } - // clang-format off - #if FLATBUFFERS_LITTLEENDIAN - PushBytes(reinterpret_cast(v), len * sizeof(T)); - #else - if (sizeof(T) == 1) { - PushBytes(reinterpret_cast(v), len); - } else { - for (auto i = len; i > 0; ) { - PushElement(v[--i]); - } - } - #endif - // clang-format on - return Offset>(EndVector(len)); - } - - template - Offset>> CreateVector(const Offset *v, size_t len) { - StartVector(len, sizeof(Offset)); - for (auto i = len; i > 0;) { PushElement(v[--i]); } - return Offset>>(EndVector(len)); - } - - /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. - /// @tparam T The data type of the `std::vector` elements. - /// @param v A const reference to the `std::vector` to serialize into the - /// buffer as a `vector`. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template Offset> CreateVector(const std::vector &v) { - return CreateVector(data(v), v.size()); - } - - // vector may be implemented using a bit-set, so we can't access it as - // an array. Instead, read elements manually. - // Background: https://isocpp.org/blog/2012/11/on-vectorbool - Offset> CreateVector(const std::vector &v) { - StartVector(v.size(), sizeof(uint8_t)); - for (auto i = v.size(); i > 0;) { - PushElement(static_cast(v[--i])); - } - return Offset>(EndVector(v.size())); - } - - // clang-format off - #ifndef FLATBUFFERS_CPP98_STL - /// @brief Serialize values returned by a function into a FlatBuffer `vector`. - /// This is a convenience function that takes care of iteration for you. - /// @tparam T The data type of the `std::vector` elements. - /// @param f A function that takes the current iteration 0..vector_size-1 and - /// returns any type that you can construct a FlatBuffers vector out of. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template Offset> CreateVector(size_t vector_size, - const std::function &f) { - std::vector elems(vector_size); - for (size_t i = 0; i < vector_size; i++) elems[i] = f(i); - return CreateVector(elems); - } - #endif - // clang-format on - - /// @brief Serialize values returned by a function into a FlatBuffer `vector`. - /// This is a convenience function that takes care of iteration for you. - /// @tparam T The data type of the `std::vector` elements. - /// @param f A function that takes the current iteration 0..vector_size-1, - /// and the state parameter returning any type that you can construct a - /// FlatBuffers vector out of. - /// @param state State passed to f. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVector(size_t vector_size, F f, S *state) { - std::vector elems(vector_size); - for (size_t i = 0; i < vector_size; i++) elems[i] = f(i, state); - return CreateVector(elems); - } - - /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. - /// This is a convenience function for a common case. - /// @param v A const reference to the `std::vector` to serialize into the - /// buffer as a `vector`. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - Offset>> CreateVectorOfStrings( - const std::vector &v) { - std::vector> offsets(v.size()); - for (size_t i = 0; i < v.size(); i++) offsets[i] = CreateString(v[i]); - return CreateVector(offsets); - } - - /// @brief Serialize an array of structs into a FlatBuffer `vector`. - /// @tparam T The data type of the struct array elements. - /// @param[in] v A pointer to the array of type `T` to serialize into the - /// buffer as a `vector`. - /// @param[in] len The number of elements to serialize. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVectorOfStructs(const T *v, size_t len) { - StartVector(len * sizeof(T) / AlignOf(), AlignOf()); - PushBytes(reinterpret_cast(v), sizeof(T) * len); - return Offset>(EndVector(len)); - } - - /// @brief Serialize an array of native structs into a FlatBuffer `vector`. - /// @tparam T The data type of the struct array elements. - /// @tparam S The data type of the native struct array elements. - /// @param[in] v A pointer to the array of type `S` to serialize into the - /// buffer as a `vector`. - /// @param[in] len The number of elements to serialize. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVectorOfNativeStructs(const S *v, - size_t len) { - extern T Pack(const S &); - std::vector vv(len); - std::transform(v, v + len, vv.begin(), Pack); - return CreateVectorOfStructs(data(vv), vv.size()); - } - - // clang-format off - #ifndef FLATBUFFERS_CPP98_STL - /// @brief Serialize an array of structs into a FlatBuffer `vector`. - /// @tparam T The data type of the struct array elements. - /// @param[in] filler A function that takes the current iteration 0..vector_size-1 - /// and a pointer to the struct that must be filled. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - /// This is mostly useful when flatbuffers are generated with mutation - /// accessors. - template Offset> CreateVectorOfStructs( - size_t vector_size, const std::function &filler) { - T* structs = StartVectorOfStructs(vector_size); - for (size_t i = 0; i < vector_size; i++) { - filler(i, structs); - structs++; - } - return EndVectorOfStructs(vector_size); - } - #endif - // clang-format on - - /// @brief Serialize an array of structs into a FlatBuffer `vector`. - /// @tparam T The data type of the struct array elements. - /// @param[in] f A function that takes the current iteration 0..vector_size-1, - /// a pointer to the struct that must be filled and the state argument. - /// @param[in] state Arbitrary state to pass to f. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - /// This is mostly useful when flatbuffers are generated with mutation - /// accessors. - template - Offset> CreateVectorOfStructs(size_t vector_size, F f, - S *state) { - T *structs = StartVectorOfStructs(vector_size); - for (size_t i = 0; i < vector_size; i++) { - f(i, structs, state); - structs++; - } - return EndVectorOfStructs(vector_size); - } - - /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector`. - /// @tparam T The data type of the `std::vector` struct elements. - /// @param[in] v A const reference to the `std::vector` of structs to - /// serialize into the buffer as a `vector`. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVectorOfStructs( - const std::vector &v) { - return CreateVectorOfStructs(data(v), v.size()); - } - - /// @brief Serialize a `std::vector` of native structs into a FlatBuffer - /// `vector`. - /// @tparam T The data type of the `std::vector` struct elements. - /// @tparam S The data type of the `std::vector` native struct elements. - /// @param[in] v A const reference to the `std::vector` of structs to - /// serialize into the buffer as a `vector`. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVectorOfNativeStructs( - const std::vector &v) { - return CreateVectorOfNativeStructs(data(v), v.size()); - } - - /// @cond FLATBUFFERS_INTERNAL - template struct StructKeyComparator { - bool operator()(const T &a, const T &b) const { - return a.KeyCompareLessThan(&b); - } - - FLATBUFFERS_DELETE_FUNC( - StructKeyComparator &operator=(const StructKeyComparator &)) - }; - /// @endcond - - /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector` - /// in sorted order. - /// @tparam T The data type of the `std::vector` struct elements. - /// @param[in] v A const reference to the `std::vector` of structs to - /// serialize into the buffer as a `vector`. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVectorOfSortedStructs(std::vector *v) { - return CreateVectorOfSortedStructs(data(*v), v->size()); - } - - /// @brief Serialize a `std::vector` of native structs into a FlatBuffer - /// `vector` in sorted order. - /// @tparam T The data type of the `std::vector` struct elements. - /// @tparam S The data type of the `std::vector` native struct elements. - /// @param[in] v A const reference to the `std::vector` of structs to - /// serialize into the buffer as a `vector`. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVectorOfSortedNativeStructs( - std::vector *v) { - return CreateVectorOfSortedNativeStructs(data(*v), v->size()); - } - - /// @brief Serialize an array of structs into a FlatBuffer `vector` in sorted - /// order. - /// @tparam T The data type of the struct array elements. - /// @param[in] v A pointer to the array of type `T` to serialize into the - /// buffer as a `vector`. - /// @param[in] len The number of elements to serialize. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVectorOfSortedStructs(T *v, size_t len) { - std::sort(v, v + len, StructKeyComparator()); - return CreateVectorOfStructs(v, len); - } - - /// @brief Serialize an array of native structs into a FlatBuffer `vector` in - /// sorted order. - /// @tparam T The data type of the struct array elements. - /// @tparam S The data type of the native struct array elements. - /// @param[in] v A pointer to the array of type `S` to serialize into the - /// buffer as a `vector`. - /// @param[in] len The number of elements to serialize. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset> CreateVectorOfSortedNativeStructs(S *v, - size_t len) { - extern T Pack(const S &); - typedef T (*Pack_t)(const S &); - std::vector vv(len); - std::transform(v, v + len, vv.begin(), static_cast(Pack)); - return CreateVectorOfSortedStructs(vv, len); - } - - /// @cond FLATBUFFERS_INTERNAL - template struct TableKeyComparator { - TableKeyComparator(vector_downward &buf) : buf_(buf) {} - TableKeyComparator(const TableKeyComparator &other) : buf_(other.buf_) {} - bool operator()(const Offset &a, const Offset &b) const { - auto table_a = reinterpret_cast(buf_.data_at(a.o)); - auto table_b = reinterpret_cast(buf_.data_at(b.o)); - return table_a->KeyCompareLessThan(table_b); - } - vector_downward &buf_; - - private: - FLATBUFFERS_DELETE_FUNC(TableKeyComparator &operator=(const TableKeyComparator &other)) - }; - /// @endcond - - /// @brief Serialize an array of `table` offsets as a `vector` in the buffer - /// in sorted order. - /// @tparam T The data type that the offset refers to. - /// @param[in] v An array of type `Offset` that contains the `table` - /// offsets to store in the buffer in sorted order. - /// @param[in] len The number of elements to store in the `vector`. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset>> CreateVectorOfSortedTables(Offset *v, - size_t len) { - std::sort(v, v + len, TableKeyComparator(buf_)); - return CreateVector(v, len); - } - - /// @brief Serialize an array of `table` offsets as a `vector` in the buffer - /// in sorted order. - /// @tparam T The data type that the offset refers to. - /// @param[in] v An array of type `Offset` that contains the `table` - /// offsets to store in the buffer in sorted order. - /// @return Returns a typed `Offset` into the serialized data indicating - /// where the vector is stored. - template - Offset>> CreateVectorOfSortedTables( - std::vector> *v) { - return CreateVectorOfSortedTables(data(*v), v->size()); - } - - /// @brief Specialized version of `CreateVector` for non-copying use cases. - /// Write the data any time later to the returned buffer pointer `buf`. - /// @param[in] len The number of elements to store in the `vector`. - /// @param[in] elemsize The size of each element in the `vector`. - /// @param[out] buf A pointer to a `uint8_t` pointer that can be - /// written to at a later time to serialize the data into a `vector` - /// in the buffer. - uoffset_t CreateUninitializedVector(size_t len, size_t elemsize, - uint8_t **buf) { - NotNested(); - StartVector(len, elemsize); - buf_.make_space(len * elemsize); - auto vec_start = GetSize(); - auto vec_end = EndVector(len); - *buf = buf_.data_at(vec_start); - return vec_end; - } - - /// @brief Specialized version of `CreateVector` for non-copying use cases. - /// Write the data any time later to the returned buffer pointer `buf`. - /// @tparam T The data type of the data that will be stored in the buffer - /// as a `vector`. - /// @param[in] len The number of elements to store in the `vector`. - /// @param[out] buf A pointer to a pointer of type `T` that can be - /// written to at a later time to serialize the data into a `vector` - /// in the buffer. - template - Offset> CreateUninitializedVector(size_t len, T **buf) { - AssertScalarT(); - return CreateUninitializedVector(len, sizeof(T), - reinterpret_cast(buf)); - } - - template - Offset> CreateUninitializedVectorOfStructs(size_t len, - T **buf) { - return CreateUninitializedVector(len, sizeof(T), - reinterpret_cast(buf)); - } - - // @brief Create a vector of scalar type T given as input a vector of scalar - // type U, useful with e.g. pre "enum class" enums, or any existing scalar - // data of the wrong type. - template - Offset> CreateVectorScalarCast(const U *v, size_t len) { - AssertScalarT(); - AssertScalarT(); - StartVector(len, sizeof(T)); - for (auto i = len; i > 0;) { PushElement(static_cast(v[--i])); } - return Offset>(EndVector(len)); - } - - /// @brief Write a struct by itself, typically to be part of a union. - template Offset CreateStruct(const T &structobj) { - NotNested(); - Align(AlignOf()); - buf_.push_small(structobj); - return Offset(GetSize()); - } - - /// @brief The length of a FlatBuffer file header. - static const size_t kFileIdentifierLength = 4; - - /// @brief Finish serializing a buffer by writing the root offset. - /// @param[in] file_identifier If a `file_identifier` is given, the buffer - /// will be prefixed with a standard FlatBuffers file header. - template - void Finish(Offset root, const char *file_identifier = nullptr) { - Finish(root.o, file_identifier, false); - } - - /// @brief Finish a buffer with a 32 bit size field pre-fixed (size of the - /// buffer following the size field). These buffers are NOT compatible - /// with standard buffers created by Finish, i.e. you can't call GetRoot - /// on them, you have to use GetSizePrefixedRoot instead. - /// All >32 bit quantities in this buffer will be aligned when the whole - /// size pre-fixed buffer is aligned. - /// These kinds of buffers are useful for creating a stream of FlatBuffers. - template - void FinishSizePrefixed(Offset root, - const char *file_identifier = nullptr) { - Finish(root.o, file_identifier, true); - } - - void SwapBufAllocator(FlatBufferBuilder &other) { - buf_.swap_allocator(other.buf_); - } - - protected: - // You shouldn't really be copying instances of this class. - FlatBufferBuilder(const FlatBufferBuilder &); - FlatBufferBuilder &operator=(const FlatBufferBuilder &); - - void Finish(uoffset_t root, const char *file_identifier, bool size_prefix) { - NotNested(); - buf_.clear_scratch(); - // This will cause the whole buffer to be aligned. - PreAlign((size_prefix ? sizeof(uoffset_t) : 0) + sizeof(uoffset_t) + - (file_identifier ? kFileIdentifierLength : 0), - minalign_); - if (file_identifier) { - FLATBUFFERS_ASSERT(strlen(file_identifier) == kFileIdentifierLength); - PushBytes(reinterpret_cast(file_identifier), - kFileIdentifierLength); - } - PushElement(ReferTo(root)); // Location of root. - if (size_prefix) { PushElement(GetSize()); } - finished = true; - } - - struct FieldLoc { - uoffset_t off; - voffset_t id; - }; - - vector_downward buf_; - - // Accumulating offsets of table members while it is being built. - // We store these in the scratch pad of buf_, after the vtable offsets. - uoffset_t num_field_loc; - // Track how much of the vtable is in use, so we can output the most compact - // possible vtable. - voffset_t max_voffset_; - - // Ensure objects are not nested. - bool nested; - - // Ensure the buffer is finished before it is being accessed. - bool finished; - - size_t minalign_; - - bool force_defaults_; // Serialize values equal to their defaults anyway. - - bool dedup_vtables_; - - struct StringOffsetCompare { - StringOffsetCompare(const vector_downward &buf) : buf_(&buf) {} - bool operator()(const Offset &a, const Offset &b) const { - auto stra = reinterpret_cast(buf_->data_at(a.o)); - auto strb = reinterpret_cast(buf_->data_at(b.o)); - return StringLessThan(stra->data(), stra->size(), strb->data(), - strb->size()); - } - const vector_downward *buf_; - }; - - // For use with CreateSharedString. Instantiated on first use only. - typedef std::set, StringOffsetCompare> StringOffsetMap; - StringOffsetMap *string_pool; - - private: - // Allocates space for a vector of structures. - // Must be completed with EndVectorOfStructs(). - template T *StartVectorOfStructs(size_t vector_size) { - StartVector(vector_size * sizeof(T) / AlignOf(), AlignOf()); - return reinterpret_cast(buf_.make_space(vector_size * sizeof(T))); - } - - // End the vector of structues in the flatbuffers. - // Vector should have previously be started with StartVectorOfStructs(). - template - Offset> EndVectorOfStructs(size_t vector_size) { - return Offset>(EndVector(vector_size)); - } -}; -/// @} - -/// @cond FLATBUFFERS_INTERNAL -// Helpers to get a typed pointer to the root object contained in the buffer. -template T *GetMutableRoot(void *buf) { - EndianCheck(); - return reinterpret_cast( - reinterpret_cast(buf) + - EndianScalar(*reinterpret_cast(buf))); -} - -template const T *GetRoot(const void *buf) { - return GetMutableRoot(const_cast(buf)); -} - -template const T *GetSizePrefixedRoot(const void *buf) { - return GetRoot(reinterpret_cast(buf) + sizeof(uoffset_t)); -} - -/// Helpers to get a typed pointer to objects that are currently being built. -/// @warning Creating new objects will lead to reallocations and invalidates -/// the pointer! -template -T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb, Offset offset) { - return reinterpret_cast(fbb.GetCurrentBufferPointer() + fbb.GetSize() - - offset.o); -} - -template -const T *GetTemporaryPointer(FlatBufferBuilder &fbb, Offset offset) { - return GetMutableTemporaryPointer(fbb, offset); -} - -/// @brief Get a pointer to the the file_identifier section of the buffer. -/// @return Returns a const char pointer to the start of the file_identifier -/// characters in the buffer. The returned char * has length -/// 'flatbuffers::FlatBufferBuilder::kFileIdentifierLength'. -/// This function is UNDEFINED for FlatBuffers whose schema does not include -/// a file_identifier (likely points at padding or the start of a the root -/// vtable). -inline const char *GetBufferIdentifier(const void *buf, - bool size_prefixed = false) { - return reinterpret_cast(buf) + - ((size_prefixed) ? 2 * sizeof(uoffset_t) : sizeof(uoffset_t)); -} - -// Helper to see if the identifier in a buffer has the expected value. -inline bool BufferHasIdentifier(const void *buf, const char *identifier, - bool size_prefixed = false) { - return strncmp(GetBufferIdentifier(buf, size_prefixed), identifier, - FlatBufferBuilder::kFileIdentifierLength) == 0; -} - -// Helper class to verify the integrity of a FlatBuffer -class Verifier FLATBUFFERS_FINAL_CLASS { - public: - Verifier(const uint8_t *buf, size_t buf_len, uoffset_t _max_depth = 64, - uoffset_t _max_tables = 1000000, bool _check_alignment = true) - : buf_(buf), - size_(buf_len), - depth_(0), - max_depth_(_max_depth), - num_tables_(0), - max_tables_(_max_tables), - upper_bound_(0), - check_alignment_(_check_alignment) { - FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE); - } - - // Central location where any verification failures register. - bool Check(bool ok) const { - // clang-format off - #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE - FLATBUFFERS_ASSERT(ok); - #endif - #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE - if (!ok) - upper_bound_ = 0; - #endif - // clang-format on - return ok; - } - - // Verify any range within the buffer. - bool Verify(size_t elem, size_t elem_len) const { - // clang-format off - #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE - auto upper_bound = elem + elem_len; - if (upper_bound_ < upper_bound) - upper_bound_ = upper_bound; - #endif - // clang-format on - return Check(elem_len < size_ && elem <= size_ - elem_len); - } - - template bool VerifyAlignment(size_t elem) const { - return Check((elem & (sizeof(T) - 1)) == 0 || !check_alignment_); - } - - // Verify a range indicated by sizeof(T). - template bool Verify(size_t elem) const { - return VerifyAlignment(elem) && Verify(elem, sizeof(T)); - } - - bool VerifyFromPointer(const uint8_t *p, size_t len) { - auto o = static_cast(p - buf_); - return Verify(o, len); - } - - // Verify relative to a known-good base pointer. - bool Verify(const uint8_t *base, voffset_t elem_off, size_t elem_len) const { - return Verify(static_cast(base - buf_) + elem_off, elem_len); - } - - template - bool Verify(const uint8_t *base, voffset_t elem_off) const { - return Verify(static_cast(base - buf_) + elem_off, sizeof(T)); - } - - // Verify a pointer (may be NULL) of a table type. - template bool VerifyTable(const T *table) { - return !table || table->Verify(*this); - } - - // Verify a pointer (may be NULL) of any vector type. - template bool VerifyVector(const Vector *vec) const { - return !vec || VerifyVectorOrString(reinterpret_cast(vec), - sizeof(T)); - } - - // Verify a pointer (may be NULL) of a vector to struct. - template bool VerifyVector(const Vector *vec) const { - return VerifyVector(reinterpret_cast *>(vec)); - } - - // Verify a pointer (may be NULL) to string. - bool VerifyString(const String *str) const { - size_t end; - return !str || (VerifyVectorOrString(reinterpret_cast(str), - 1, &end) && - Verify(end, 1) && // Must have terminator - Check(buf_[end] == '\0')); // Terminating byte must be 0. - } - - // Common code between vectors and strings. - bool VerifyVectorOrString(const uint8_t *vec, size_t elem_size, - size_t *end = nullptr) const { - auto veco = static_cast(vec - buf_); - // Check we can read the size field. - if (!Verify(veco)) return false; - // Check the whole array. If this is a string, the byte past the array - // must be 0. - auto size = ReadScalar(vec); - auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size; - if (!Check(size < max_elems)) - return false; // Protect against byte_size overflowing. - auto byte_size = sizeof(size) + elem_size * size; - if (end) *end = veco + byte_size; - return Verify(veco, byte_size); - } - - // Special case for string contents, after the above has been called. - bool VerifyVectorOfStrings(const Vector> *vec) const { - if (vec) { - for (uoffset_t i = 0; i < vec->size(); i++) { - if (!VerifyString(vec->Get(i))) return false; - } - } - return true; - } - - // Special case for table contents, after the above has been called. - template bool VerifyVectorOfTables(const Vector> *vec) { - if (vec) { - for (uoffset_t i = 0; i < vec->size(); i++) { - if (!vec->Get(i)->Verify(*this)) return false; - } - } - return true; - } - - __supress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart( - const uint8_t *table) { - // Check the vtable offset. - auto tableo = static_cast(table - buf_); - if (!Verify(tableo)) return false; - // This offset may be signed, but doing the subtraction unsigned always - // gives the result we want. - auto vtableo = tableo - static_cast(ReadScalar(table)); - // Check the vtable size field, then check vtable fits in its entirety. - return VerifyComplexity() && Verify(vtableo) && - VerifyAlignment(ReadScalar(buf_ + vtableo)) && - Verify(vtableo, ReadScalar(buf_ + vtableo)); - } - - template - bool VerifyBufferFromStart(const char *identifier, size_t start) { - if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) && - BufferHasIdentifier(buf_ + start, identifier)))) { - return false; - } - - // Call T::Verify, which must be in the generated code for this type. - auto o = VerifyOffset(start); - return o && reinterpret_cast(buf_ + start + o)->Verify(*this) - // clang-format off - #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE - && GetComputedSize() - #endif - ; - // clang-format on - } - - // Verify this whole buffer, starting with root type T. - template bool VerifyBuffer() { return VerifyBuffer(nullptr); } - - template bool VerifyBuffer(const char *identifier) { - return VerifyBufferFromStart(identifier, 0); - } - - template bool VerifySizePrefixedBuffer(const char *identifier) { - return Verify(0U) && - ReadScalar(buf_) == size_ - sizeof(uoffset_t) && - VerifyBufferFromStart(identifier, sizeof(uoffset_t)); - } - - uoffset_t VerifyOffset(size_t start) const { - if (!Verify(start)) return 0; - auto o = ReadScalar(buf_ + start); - // May not point to itself. - if (!Check(o != 0)) return 0; - // Can't wrap around / buffers are max 2GB. - if (!Check(static_cast(o) >= 0)) return 0; - // Must be inside the buffer to create a pointer from it (pointer outside - // buffer is UB). - if (!Verify(start + o, 1)) return 0; - return o; - } - - uoffset_t VerifyOffset(const uint8_t *base, voffset_t start) const { - return VerifyOffset(static_cast(base - buf_) + start); - } - - // Called at the start of a table to increase counters measuring data - // structure depth and amount, and possibly bails out with false if - // limits set by the constructor have been hit. Needs to be balanced - // with EndTable(). - bool VerifyComplexity() { - depth_++; - num_tables_++; - return Check(depth_ <= max_depth_ && num_tables_ <= max_tables_); - } - - // Called at the end of a table to pop the depth count. - bool EndTable() { - depth_--; - return true; - } - - // Returns the message size in bytes - size_t GetComputedSize() const { - // clang-format off - #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE - uintptr_t size = upper_bound_; - // Align the size to uoffset_t - size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1); - return (size > size_) ? 0 : size; - #else - // Must turn on FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE for this to work. - (void)upper_bound_; - FLATBUFFERS_ASSERT(false); - return 0; - #endif - // clang-format on - } - - private: - const uint8_t *buf_; - size_t size_; - uoffset_t depth_; - uoffset_t max_depth_; - uoffset_t num_tables_; - uoffset_t max_tables_; - mutable size_t upper_bound_; - bool check_alignment_; -}; - -// Convenient way to bundle a buffer and its length, to pass it around -// typed by its root. -// A BufferRef does not own its buffer. -struct BufferRefBase {}; // for std::is_base_of -template struct BufferRef : BufferRefBase { - BufferRef() : buf(nullptr), len(0), must_free(false) {} - BufferRef(uint8_t *_buf, uoffset_t _len) - : buf(_buf), len(_len), must_free(false) {} - - ~BufferRef() { - if (must_free) free(buf); - } - - const T *GetRoot() const { return flatbuffers::GetRoot(buf); } - - bool Verify() { - Verifier verifier(buf, len); - return verifier.VerifyBuffer(nullptr); - } - - uint8_t *buf; - uoffset_t len; - bool must_free; -}; - -// "structs" are flat structures that do not have an offset table, thus -// always have all members present and do not support forwards/backwards -// compatible extensions. - -class Struct FLATBUFFERS_FINAL_CLASS { - public: - template T GetField(uoffset_t o) const { - return ReadScalar(&data_[o]); - } - - template T GetStruct(uoffset_t o) const { - return reinterpret_cast(&data_[o]); - } - - const uint8_t *GetAddressOf(uoffset_t o) const { return &data_[o]; } - uint8_t *GetAddressOf(uoffset_t o) { return &data_[o]; } - - private: - // private constructor & copy constructor: you obtain instances of this - // class by pointing to existing data only - Struct(); - Struct(const Struct &); - Struct &operator=(const Struct &); - - uint8_t data_[1]; -}; - -// "tables" use an offset table (possibly shared) that allows fields to be -// omitted and added at will, but uses an extra indirection to read. -class Table { - public: - const uint8_t *GetVTable() const { - return data_ - ReadScalar(data_); - } - - // This gets the field offset for any of the functions below it, or 0 - // if the field was not present. - voffset_t GetOptionalFieldOffset(voffset_t field) const { - // The vtable offset is always at the start. - auto vtable = GetVTable(); - // The first element is the size of the vtable (fields + type id + itself). - auto vtsize = ReadScalar(vtable); - // If the field we're accessing is outside the vtable, we're reading older - // data, so it's the same as if the offset was 0 (not present). - return field < vtsize ? ReadScalar(vtable + field) : 0; - } - - template T GetField(voffset_t field, T defaultval) const { - auto field_offset = GetOptionalFieldOffset(field); - return field_offset ? ReadScalar(data_ + field_offset) : defaultval; - } - - template P GetPointer(voffset_t field) { - auto field_offset = GetOptionalFieldOffset(field); - auto p = data_ + field_offset; - return field_offset ? reinterpret_cast

(p + ReadScalar(p)) - : nullptr; - } - template P GetPointer(voffset_t field) const { - return const_cast

(this)->GetPointer

(field); - } - - template P GetStruct(voffset_t field) const { - auto field_offset = GetOptionalFieldOffset(field); - auto p = const_cast(data_ + field_offset); - return field_offset ? reinterpret_cast

(p) : nullptr; - } - - template - flatbuffers::Optional GetOptional(voffset_t field) const { - auto field_offset = GetOptionalFieldOffset(field); - auto p = data_ + field_offset; - return field_offset ? Optional(static_cast(ReadScalar(p))) - : Optional(); - } - - template bool SetField(voffset_t field, T val, T def) { - auto field_offset = GetOptionalFieldOffset(field); - if (!field_offset) return IsTheSameAs(val, def); - WriteScalar(data_ + field_offset, val); - return true; - } - template bool SetField(voffset_t field, T val) { - auto field_offset = GetOptionalFieldOffset(field); - if (!field_offset) return false; - WriteScalar(data_ + field_offset, val); - return true; - } - - bool SetPointer(voffset_t field, const uint8_t *val) { - auto field_offset = GetOptionalFieldOffset(field); - if (!field_offset) return false; - WriteScalar(data_ + field_offset, - static_cast(val - (data_ + field_offset))); - return true; - } - - uint8_t *GetAddressOf(voffset_t field) { - auto field_offset = GetOptionalFieldOffset(field); - return field_offset ? data_ + field_offset : nullptr; - } - const uint8_t *GetAddressOf(voffset_t field) const { - return const_cast

(this)->GetAddressOf(field); - } - - bool CheckField(voffset_t field) const { - return GetOptionalFieldOffset(field) != 0; - } - - // Verify the vtable of this table. - // Call this once per table, followed by VerifyField once per field. - bool VerifyTableStart(Verifier &verifier) const { - return verifier.VerifyTableStart(data_); - } - - // Verify a particular field. - template - bool VerifyField(const Verifier &verifier, voffset_t field) const { - // Calling GetOptionalFieldOffset should be safe now thanks to - // VerifyTable(). - auto field_offset = GetOptionalFieldOffset(field); - // Check the actual field. - return !field_offset || verifier.Verify(data_, field_offset); - } - - // VerifyField for required fields. - template - bool VerifyFieldRequired(const Verifier &verifier, voffset_t field) const { - auto field_offset = GetOptionalFieldOffset(field); - return verifier.Check(field_offset != 0) && - verifier.Verify(data_, field_offset); - } - - // Versions for offsets. - bool VerifyOffset(const Verifier &verifier, voffset_t field) const { - auto field_offset = GetOptionalFieldOffset(field); - return !field_offset || verifier.VerifyOffset(data_, field_offset); - } - - bool VerifyOffsetRequired(const Verifier &verifier, voffset_t field) const { - auto field_offset = GetOptionalFieldOffset(field); - return verifier.Check(field_offset != 0) && - verifier.VerifyOffset(data_, field_offset); - } - - private: - // private constructor & copy constructor: you obtain instances of this - // class by pointing to existing data only - Table(); - Table(const Table &other); - Table &operator=(const Table &); - - uint8_t data_[1]; -}; - -// This specialization allows avoiding warnings like: -// MSVC C4800: type: forcing value to bool 'true' or 'false'. -template<> -inline flatbuffers::Optional Table::GetOptional( - voffset_t field) const { - auto field_offset = GetOptionalFieldOffset(field); - auto p = data_ + field_offset; - return field_offset ? Optional(ReadScalar(p) != 0) - : Optional(); -} - -template -void FlatBufferBuilder::Required(Offset table, voffset_t field) { - auto table_ptr = reinterpret_cast(buf_.data_at(table.o)); - bool ok = table_ptr->GetOptionalFieldOffset(field) != 0; - // If this fails, the caller will show what field needs to be set. - FLATBUFFERS_ASSERT(ok); - (void)ok; -} - -/// @brief This can compute the start of a FlatBuffer from a root pointer, i.e. -/// it is the opposite transformation of GetRoot(). -/// This may be useful if you want to pass on a root and have the recipient -/// delete the buffer afterwards. -inline const uint8_t *GetBufferStartFromRootPointer(const void *root) { - auto table = reinterpret_cast(root); - auto vtable = table->GetVTable(); - // Either the vtable is before the root or after the root. - auto start = (std::min)(vtable, reinterpret_cast(root)); - // Align to at least sizeof(uoffset_t). - start = reinterpret_cast(reinterpret_cast(start) & - ~(sizeof(uoffset_t) - 1)); - // Additionally, there may be a file_identifier in the buffer, and the root - // offset. The buffer may have been aligned to any size between - // sizeof(uoffset_t) and FLATBUFFERS_MAX_ALIGNMENT (see "force_align"). - // Sadly, the exact alignment is only known when constructing the buffer, - // since it depends on the presence of values with said alignment properties. - // So instead, we simply look at the next uoffset_t values (root, - // file_identifier, and alignment padding) to see which points to the root. - // None of the other values can "impersonate" the root since they will either - // be 0 or four ASCII characters. - static_assert(FlatBufferBuilder::kFileIdentifierLength == sizeof(uoffset_t), - "file_identifier is assumed to be the same size as uoffset_t"); - for (auto possible_roots = FLATBUFFERS_MAX_ALIGNMENT / sizeof(uoffset_t) + 1; - possible_roots; possible_roots--) { - start -= sizeof(uoffset_t); - if (ReadScalar(start) + start == - reinterpret_cast(root)) - return start; - } - // We didn't find the root, either the "root" passed isn't really a root, - // or the buffer is corrupt. - // Assert, because calling this function with bad data may cause reads - // outside of buffer boundaries. - FLATBUFFERS_ASSERT(false); - return nullptr; -} - -/// @brief This return the prefixed size of a FlatBuffer. -inline uoffset_t GetPrefixedSize(const uint8_t *buf) { - return ReadScalar(buf); -} - -// Base class for native objects (FlatBuffer data de-serialized into native -// C++ data structures). -// Contains no functionality, purely documentative. -struct NativeTable {}; - -/// @brief Function types to be used with resolving hashes into objects and -/// back again. The resolver gets a pointer to a field inside an object API -/// object that is of the type specified in the schema using the attribute -/// `cpp_type` (it is thus important whatever you write to this address -/// matches that type). The value of this field is initially null, so you -/// may choose to implement a delayed binding lookup using this function -/// if you wish. The resolver does the opposite lookup, for when the object -/// is being serialized again. -typedef uint64_t hash_value_t; -// clang-format off -#ifdef FLATBUFFERS_CPP98_STL - typedef void (*resolver_function_t)(void **pointer_adr, hash_value_t hash); - typedef hash_value_t (*rehasher_function_t)(void *pointer); -#else - typedef std::function - resolver_function_t; - typedef std::function rehasher_function_t; -#endif -// clang-format on - -// Helper function to test if a field is present, using any of the field -// enums in the generated code. -// `table` must be a generated table type. Since this is a template parameter, -// this is not typechecked to be a subclass of Table, so beware! -// Note: this function will return false for fields equal to the default -// value, since they're not stored in the buffer (unless force_defaults was -// used). -template -bool IsFieldPresent(const T *table, typename T::FlatBuffersVTableOffset field) { - // Cast, since Table is a private baseclass of any table types. - return reinterpret_cast(table)->CheckField( - static_cast(field)); -} - -// Utility function for reverse lookups on the EnumNames*() functions -// (in the generated C++ code) -// names must be NULL terminated. -inline int LookupEnum(const char **names, const char *name) { - for (const char **p = names; *p; p++) - if (!strcmp(*p, name)) return static_cast(p - names); - return -1; -} - -// These macros allow us to layout a struct with a guarantee that they'll end -// up looking the same on different compilers and platforms. -// It does this by disallowing the compiler to do any padding, and then -// does padding itself by inserting extra padding fields that make every -// element aligned to its own size. -// Additionally, it manually sets the alignment of the struct as a whole, -// which is typically its largest element, or a custom size set in the schema -// by the force_align attribute. -// These are used in the generated code only. - -// clang-format off -#if defined(_MSC_VER) - #define FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(alignment) \ - __pragma(pack(1)) \ - struct __declspec(align(alignment)) - #define FLATBUFFERS_STRUCT_END(name, size) \ - __pragma(pack()) \ - static_assert(sizeof(name) == size, "compiler breaks packing rules") -#elif defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__) - #define FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(alignment) \ - _Pragma("pack(1)") \ - struct __attribute__((aligned(alignment))) - #define FLATBUFFERS_STRUCT_END(name, size) \ - _Pragma("pack()") \ - static_assert(sizeof(name) == size, "compiler breaks packing rules") -#else - #error Unknown compiler, please define structure alignment macros -#endif -// clang-format on - -// Minimal reflection via code generation. -// Besides full-fat reflection (see reflection.h) and parsing/printing by -// loading schemas (see idl.h), we can also have code generation for mimimal -// reflection data which allows pretty-printing and other uses without needing -// a schema or a parser. -// Generate code with --reflect-types (types only) or --reflect-names (names -// also) to enable. -// See minireflect.h for utilities using this functionality. - -// These types are organized slightly differently as the ones in idl.h. -enum SequenceType { ST_TABLE, ST_STRUCT, ST_UNION, ST_ENUM }; - -// Scalars have the same order as in idl.h -// clang-format off -#define FLATBUFFERS_GEN_ELEMENTARY_TYPES(ET) \ - ET(ET_UTYPE) \ - ET(ET_BOOL) \ - ET(ET_CHAR) \ - ET(ET_UCHAR) \ - ET(ET_SHORT) \ - ET(ET_USHORT) \ - ET(ET_INT) \ - ET(ET_UINT) \ - ET(ET_LONG) \ - ET(ET_ULONG) \ - ET(ET_FLOAT) \ - ET(ET_DOUBLE) \ - ET(ET_STRING) \ - ET(ET_SEQUENCE) // See SequenceType. - -enum ElementaryType { - #define FLATBUFFERS_ET(E) E, - FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET) - #undef FLATBUFFERS_ET -}; - -inline const char * const *ElementaryTypeNames() { - static const char * const names[] = { - #define FLATBUFFERS_ET(E) #E, - FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET) - #undef FLATBUFFERS_ET - }; - return names; -} -// clang-format on - -// Basic type info cost just 16bits per field! -struct TypeCode { - uint16_t base_type : 4; // ElementaryType - uint16_t is_repeating : 1; // Either vector (in table) or array (in struct) - int16_t sequence_ref : 11; // Index into type_refs below, or -1 for none. -}; - -static_assert(sizeof(TypeCode) == 2, "TypeCode"); - -struct TypeTable; - -// Signature of the static method present in each type. -typedef const TypeTable *(*TypeFunction)(); - -struct TypeTable { - SequenceType st; - size_t num_elems; // of type_codes, values, names (but not type_refs). - const TypeCode *type_codes; // num_elems count - const TypeFunction *type_refs; // less than num_elems entries (see TypeCode). - const int16_t *array_sizes; // less than num_elems entries (see TypeCode). - const int64_t *values; // Only set for non-consecutive enum/union or structs. - const char *const *names; // Only set if compiled with --reflect-names. -}; - -// String which identifies the current version of FlatBuffers. -// flatbuffer_version_string is used by Google developers to identify which -// applications uploaded to Google Play are using this library. This allows -// the development team at Google to determine the popularity of the library. -// How it works: Applications that are uploaded to the Google Play Store are -// scanned for this version string. We track which applications are using it -// to measure popularity. You are free to remove it (of course) but we would -// appreciate if you left it in. - -// Weak linkage is culled by VS & doesn't work on cygwin. -// clang-format off -#if !defined(_WIN32) && !defined(__CYGWIN__) - -extern volatile __attribute__((weak)) const char *flatbuffer_version_string; -volatile __attribute__((weak)) const char *flatbuffer_version_string = - "FlatBuffers " - FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MAJOR) "." - FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MINOR) "." - FLATBUFFERS_STRING(FLATBUFFERS_VERSION_REVISION); - -#endif // !defined(_WIN32) && !defined(__CYGWIN__) - -#define FLATBUFFERS_DEFINE_BITMASK_OPERATORS(E, T)\ - inline E operator | (E lhs, E rhs){\ - return E(T(lhs) | T(rhs));\ - }\ - inline E operator & (E lhs, E rhs){\ - return E(T(lhs) & T(rhs));\ - }\ - inline E operator ^ (E lhs, E rhs){\ - return E(T(lhs) ^ T(rhs));\ - }\ - inline E operator ~ (E lhs){\ - return E(~T(lhs));\ - }\ - inline E operator |= (E &lhs, E rhs){\ - lhs = lhs | rhs;\ - return lhs;\ - }\ - inline E operator &= (E &lhs, E rhs){\ - lhs = lhs & rhs;\ - return lhs;\ - }\ - inline E operator ^= (E &lhs, E rhs){\ - lhs = lhs ^ rhs;\ - return lhs;\ - }\ - inline bool operator !(E rhs) \ - {\ - return !bool(T(rhs)); \ - } -/// @endcond -} // namespace flatbuffers - -// clang-format on - -#endif // FLATBUFFERS_H_ diff --git a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/stl_emulation.h b/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/stl_emulation.h deleted file mode 100644 index c9a1a8bf..00000000 --- a/code/components/tfmicro/third_party/flatbuffers/include/flatbuffers/stl_emulation.h +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FLATBUFFERS_STL_EMULATION_H_ -#define FLATBUFFERS_STL_EMULATION_H_ - -// clang-format off -#include "flatbuffers/base.h" - -#include -#include -#include -#include -#include - -// Detect C++17 compatible compiler. -// __cplusplus >= 201703L - a compiler has support of 'static inline' variables. -#if defined(FLATBUFFERS_USE_STD_OPTIONAL) \ - || (defined(__cplusplus) && __cplusplus >= 201703L) \ - || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)) - #include - #ifndef FLATBUFFERS_USE_STD_OPTIONAL - #define FLATBUFFERS_USE_STD_OPTIONAL - #endif -#endif - -#if defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL) - #define FLATBUFFERS_CPP98_STL -#endif // defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL) - -#if defined(FLATBUFFERS_CPP98_STL) - #include -#endif // defined(FLATBUFFERS_CPP98_STL) - -// This header provides backwards compatibility for C++98 STLs like stlport. -namespace flatbuffers { - -// Retrieve ::back() from a string in a way that is compatible with pre C++11 -// STLs (e.g stlport). -inline char& string_back(std::string &value) { - return value[value.length() - 1]; -} - -inline char string_back(const std::string &value) { - return value[value.length() - 1]; -} - -// Helper method that retrieves ::data() from a vector in a way that is -// compatible with pre C++11 STLs (e.g stlport). -template inline T *vector_data(std::vector &vector) { - // In some debug environments, operator[] does bounds checking, so &vector[0] - // can't be used. - return vector.empty() ? nullptr : &vector[0]; -} - -template inline const T *vector_data( - const std::vector &vector) { - return vector.empty() ? nullptr : &vector[0]; -} - -template -inline void vector_emplace_back(std::vector *vector, V &&data) { - #if defined(FLATBUFFERS_CPP98_STL) - vector->push_back(data); - #else - vector->emplace_back(std::forward(data)); - #endif // defined(FLATBUFFERS_CPP98_STL) -} - -#ifndef FLATBUFFERS_CPP98_STL - #if defined(FLATBUFFERS_TEMPLATES_ALIASES) - template - using numeric_limits = std::numeric_limits; - #else - template class numeric_limits : - public std::numeric_limits {}; - #endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) -#else - template class numeric_limits : - public std::numeric_limits { - public: - // Android NDK fix. - static T lowest() { - return std::numeric_limits::min(); - } - }; - - template <> class numeric_limits : - public std::numeric_limits { - public: - static float lowest() { return -FLT_MAX; } - }; - - template <> class numeric_limits : - public std::numeric_limits { - public: - static double lowest() { return -DBL_MAX; } - }; - - template <> class numeric_limits { - public: - static unsigned long long min() { return 0ULL; } - static unsigned long long max() { return ~0ULL; } - static unsigned long long lowest() { - return numeric_limits::min(); - } - }; - - template <> class numeric_limits { - public: - static long long min() { - return static_cast(1ULL << ((sizeof(long long) << 3) - 1)); - } - static long long max() { - return static_cast( - (1ULL << ((sizeof(long long) << 3) - 1)) - 1); - } - static long long lowest() { - return numeric_limits::min(); - } - }; -#endif // FLATBUFFERS_CPP98_STL - -#if defined(FLATBUFFERS_TEMPLATES_ALIASES) - #ifndef FLATBUFFERS_CPP98_STL - template using is_scalar = std::is_scalar; - template using is_same = std::is_same; - template using is_floating_point = std::is_floating_point; - template using is_unsigned = std::is_unsigned; - template using is_enum = std::is_enum; - template using make_unsigned = std::make_unsigned; - template - using conditional = std::conditional; - template - using integral_constant = std::integral_constant; - #else - // Map C++ TR1 templates defined by stlport. - template using is_scalar = std::tr1::is_scalar; - template using is_same = std::tr1::is_same; - template using is_floating_point = - std::tr1::is_floating_point; - template using is_unsigned = std::tr1::is_unsigned; - template using is_enum = std::tr1::is_enum; - // Android NDK doesn't have std::make_unsigned or std::tr1::make_unsigned. - template struct make_unsigned { - static_assert(is_unsigned::value, "Specialization not implemented!"); - using type = T; - }; - template<> struct make_unsigned { using type = unsigned char; }; - template<> struct make_unsigned { using type = unsigned short; }; - template<> struct make_unsigned { using type = unsigned int; }; - template<> struct make_unsigned { using type = unsigned long; }; - template<> - struct make_unsigned { using type = unsigned long long; }; - template - using conditional = std::tr1::conditional; - template - using integral_constant = std::tr1::integral_constant; - #endif // !FLATBUFFERS_CPP98_STL -#else - // MSVC 2010 doesn't support C++11 aliases. - template struct is_scalar : public std::is_scalar {}; - template struct is_same : public std::is_same {}; - template struct is_floating_point : - public std::is_floating_point {}; - template struct is_unsigned : public std::is_unsigned {}; - template struct is_enum : public std::is_enum {}; - template struct make_unsigned : public std::make_unsigned {}; - template - struct conditional : public std::conditional {}; - template - struct integral_constant : public std::integral_constant {}; -#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) - -#ifndef FLATBUFFERS_CPP98_STL - #if defined(FLATBUFFERS_TEMPLATES_ALIASES) - template using unique_ptr = std::unique_ptr; - #else - // MSVC 2010 doesn't support C++11 aliases. - // We're manually "aliasing" the class here as we want to bring unique_ptr - // into the flatbuffers namespace. We have unique_ptr in the flatbuffers - // namespace we have a completely independent implementation (see below) - // for C++98 STL implementations. - template class unique_ptr : public std::unique_ptr { - public: - unique_ptr() {} - explicit unique_ptr(T* p) : std::unique_ptr(p) {} - unique_ptr(std::unique_ptr&& u) { *this = std::move(u); } - unique_ptr(unique_ptr&& u) { *this = std::move(u); } - unique_ptr& operator=(std::unique_ptr&& u) { - std::unique_ptr::reset(u.release()); - return *this; - } - unique_ptr& operator=(unique_ptr&& u) { - std::unique_ptr::reset(u.release()); - return *this; - } - unique_ptr& operator=(T* p) { - return std::unique_ptr::operator=(p); - } - }; - #endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) -#else - // Very limited implementation of unique_ptr. - // This is provided simply to allow the C++ code generated from the default - // settings to function in C++98 environments with no modifications. - template class unique_ptr { - public: - typedef T element_type; - - unique_ptr() : ptr_(nullptr) {} - explicit unique_ptr(T* p) : ptr_(p) {} - unique_ptr(unique_ptr&& u) : ptr_(nullptr) { reset(u.release()); } - unique_ptr(const unique_ptr& u) : ptr_(nullptr) { - reset(const_cast(&u)->release()); - } - ~unique_ptr() { reset(); } - - unique_ptr& operator=(const unique_ptr& u) { - reset(const_cast(&u)->release()); - return *this; - } - - unique_ptr& operator=(unique_ptr&& u) { - reset(u.release()); - return *this; - } - - unique_ptr& operator=(T* p) { - reset(p); - return *this; - } - - const T& operator*() const { return *ptr_; } - T* operator->() const { return ptr_; } - T* get() const noexcept { return ptr_; } - explicit operator bool() const { return ptr_ != nullptr; } - - // modifiers - T* release() { - T* value = ptr_; - ptr_ = nullptr; - return value; - } - - void reset(T* p = nullptr) { - T* value = ptr_; - ptr_ = p; - if (value) delete value; - } - - void swap(unique_ptr& u) { - T* temp_ptr = ptr_; - ptr_ = u.ptr_; - u.ptr_ = temp_ptr; - } - - private: - T* ptr_; - }; - - template bool operator==(const unique_ptr& x, - const unique_ptr& y) { - return x.get() == y.get(); - } - - template bool operator==(const unique_ptr& x, - const D* y) { - return static_cast(x.get()) == y; - } - - template bool operator==(const unique_ptr& x, intptr_t y) { - return reinterpret_cast(x.get()) == y; - } - - template bool operator!=(const unique_ptr& x, decltype(nullptr)) { - return !!x; - } - - template bool operator!=(decltype(nullptr), const unique_ptr& x) { - return !!x; - } - - template bool operator==(const unique_ptr& x, decltype(nullptr)) { - return !x; - } - - template bool operator==(decltype(nullptr), const unique_ptr& x) { - return !x; - } - -#endif // !FLATBUFFERS_CPP98_STL - -#ifdef FLATBUFFERS_USE_STD_OPTIONAL -template -using Optional = std::optional; -using nullopt_t = std::nullopt_t; -inline constexpr nullopt_t nullopt = std::nullopt; - -#else -// Limited implementation of Optional type for a scalar T. -// This implementation limited by trivial types compatible with -// std::is_arithmetic or std::is_enum type traits. - -// A tag to indicate an empty flatbuffers::optional. -struct nullopt_t { - explicit FLATBUFFERS_CONSTEXPR_CPP11 nullopt_t(int) {} -}; - -#if defined(FLATBUFFERS_CONSTEXPR_DEFINED) - namespace internal { - template struct nullopt_holder { - static constexpr nullopt_t instance_ = nullopt_t(0); - }; - template - constexpr nullopt_t nullopt_holder::instance_; - } - static constexpr const nullopt_t &nullopt = internal::nullopt_holder::instance_; - -#else - namespace internal { - template struct nullopt_holder { - static const nullopt_t instance_; - }; - template - const nullopt_t nullopt_holder::instance_ = nullopt_t(0); - } - static const nullopt_t &nullopt = internal::nullopt_holder::instance_; - -#endif - -template -class Optional FLATBUFFERS_FINAL_CLASS { - // Non-scalar 'T' would extremely complicated Optional. - // Use is_scalar checking because flatbuffers flatbuffers::is_arithmetic - // isn't implemented. - static_assert(flatbuffers::is_scalar::value, "unexpected type T"); - - public: - ~Optional() {} - - FLATBUFFERS_CONSTEXPR_CPP11 Optional() FLATBUFFERS_NOEXCEPT - : value_(), has_value_(false) {} - - FLATBUFFERS_CONSTEXPR_CPP11 Optional(nullopt_t) FLATBUFFERS_NOEXCEPT - : value_(), has_value_(false) {} - - FLATBUFFERS_CONSTEXPR_CPP11 Optional(T val) FLATBUFFERS_NOEXCEPT - : value_(val), has_value_(true) {} - - FLATBUFFERS_CONSTEXPR_CPP11 Optional(const Optional &other) FLATBUFFERS_NOEXCEPT - : value_(other.value_), has_value_(other.has_value_) {} - - FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(const Optional &other) FLATBUFFERS_NOEXCEPT { - value_ = other.value_; - has_value_ = other.has_value_; - return *this; - } - - FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(nullopt_t) FLATBUFFERS_NOEXCEPT { - value_ = T(); - has_value_ = false; - return *this; - } - - FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(T val) FLATBUFFERS_NOEXCEPT { - value_ = val; - has_value_ = true; - return *this; - } - - void reset() FLATBUFFERS_NOEXCEPT { - *this = nullopt; - } - - void swap(Optional &other) FLATBUFFERS_NOEXCEPT { - std::swap(value_, other.value_); - std::swap(has_value_, other.has_value_); - } - - FLATBUFFERS_CONSTEXPR_CPP11 FLATBUFFERS_EXPLICIT_CPP11 operator bool() const FLATBUFFERS_NOEXCEPT { - return has_value_; - } - - FLATBUFFERS_CONSTEXPR_CPP11 bool has_value() const FLATBUFFERS_NOEXCEPT { - return has_value_; - } - - FLATBUFFERS_CONSTEXPR_CPP11 const T& operator*() const FLATBUFFERS_NOEXCEPT { - return value_; - } - - const T& value() const { - FLATBUFFERS_ASSERT(has_value()); - return value_; - } - - T value_or(T default_value) const FLATBUFFERS_NOEXCEPT { - return has_value() ? value_ : default_value; - } - - private: - T value_; - bool has_value_; -}; - -template -FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& opt, nullopt_t) FLATBUFFERS_NOEXCEPT { - return !opt; -} -template -FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(nullopt_t, const Optional& opt) FLATBUFFERS_NOEXCEPT { - return !opt; -} - -template -FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& lhs, const U& rhs) FLATBUFFERS_NOEXCEPT { - return static_cast(lhs) && (*lhs == rhs); -} - -template -FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const T& lhs, const Optional& rhs) FLATBUFFERS_NOEXCEPT { - return static_cast(rhs) && (lhs == *rhs); -} - -template -FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& lhs, const Optional& rhs) FLATBUFFERS_NOEXCEPT { - return static_cast(lhs) != static_cast(rhs) - ? false - : !static_cast(lhs) ? false : (*lhs == *rhs); -} -#endif // FLATBUFFERS_USE_STD_OPTIONAL - -} // namespace flatbuffers - -#endif // FLATBUFFERS_STL_EMULATION_H_ diff --git a/code/main/version.cpp b/code/main/version.cpp index 14bf63a3..ba140d64 100644 --- a/code/main/version.cpp +++ b/code/main/version.cpp @@ -1,4 +1,4 @@ -const char* GIT_REV="eac5134"; -const char* GIT_TAG=""; -const char* GIT_BRANCH="master"; -const char* BUILD_TIME="2022-01-01 08:54"; \ No newline at end of file +const char* GIT_REV="8e8897c"; +const char* GIT_TAG="v10.0.2"; +const char* GIT_BRANCH="rolling"; +const char* BUILD_TIME="2022-01-03 17:21"; \ No newline at end of file diff --git a/code/platformio.ini b/code/platformio.ini index c0b62bc6..b082bd42 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -30,7 +30,7 @@ lib_deps = jomjol_controlcamera jomjol_flowcontroll jomjol_tfliteclass - tfmicro + tflite-lib jomjol_fileserver_ota jomjol_time_sntp jomjol_logfile diff --git a/code/sdkconfig.esp32cam b/code/sdkconfig.esp32cam index 46466b26..14b87455 100644 --- a/code/sdkconfig.esp32cam +++ b/code/sdkconfig.esp32cam @@ -490,7 +490,7 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y # # HTTP Server # -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=16 @@ -553,11 +553,11 @@ CONFIG_ESP_TIMER_IMPL_TG0_LAC=y # # Wi-Fi # -CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=5 +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=16 CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 -CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=8 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16 CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=16 # CONFIG_ESP32_WIFI_CSI_ENABLED is not set CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y diff --git a/code/sdkconfig.esp32cam.old b/code/sdkconfig.esp32cam.old index f26be869..46466b26 100644 --- a/code/sdkconfig.esp32cam.old +++ b/code/sdkconfig.esp32cam.old @@ -1225,3 +1225,162 @@ CONFIG_CAMERA_CORE0=y # # CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set # end of Compatibility options + +# Deprecated options for backward compatibility +CONFIG_TOOLPREFIX="xtensa-esp32-elf-" +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +# CONFIG_MONITOR_BAUD_9600B is not set +# CONFIG_MONITOR_BAUD_57600B is not set +CONFIG_MONITOR_BAUD_115200B=y +# CONFIG_MONITOR_BAUD_230400B is not set +# CONFIG_MONITOR_BAUD_921600B is not set +# CONFIG_MONITOR_BAUD_2MB is not set +# CONFIG_MONITOR_BAUD_OTHER is not set +CONFIG_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_MONITOR_BAUD=115200 +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set +CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +# CONFIG_DISABLE_GCC8_WARNINGS is not set +# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y +CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=0 +CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=0 +CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0 +CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 +CONFIG_ADC2_DISABLE_DAC=y +CONFIG_SPIRAM_SUPPORT=y +# CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST is not set +CONFIG_TRACEMEM_RESERVE_DRAM=0x0 +# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set +CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y +CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 +# CONFIG_ULP_COPROC_ENABLED is not set +CONFIG_ULP_COPROC_RESERVE_MEM=0 +CONFIG_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_BROWNOUT_DET_LVL=0 +CONFIG_REDUCE_PHY_TX_POWER=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set +# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_EVENT_LOOP_PROFILING is not set +CONFIG_POST_EVENTS_FROM_ISR=y +CONFIG_POST_EVENTS_FROM_IRAM_ISR=y +# CONFIG_ESP32S2_PANIC_PRINT_HALT is not set +CONFIG_ESP32S2_PANIC_PRINT_REBOOT=y +# CONFIG_ESP32S2_PANIC_SILENT_REBOOT is not set +# CONFIG_ESP32S2_PANIC_GDBSTUB is not set +CONFIG_TIMER_TASK_STACK_SIZE=3584 +# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set +# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y +CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150 +CONFIG_MB_MASTER_DELAY_MS_CONVERT=200 +CONFIG_MB_QUEUE_LENGTH=20 +CONFIG_MB_SERIAL_TASK_STACK_SIZE=4096 +CONFIG_MB_SERIAL_BUF_SIZE=256 +CONFIG_MB_SERIAL_TASK_PRIO=10 +CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT=y +CONFIG_MB_CONTROLLER_SLAVE_ID=0x00112233 +CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20 +CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 +CONFIG_MB_CONTROLLER_STACK_SIZE=4096 +CONFIG_MB_EVENT_QUEUE_TIMEOUT=20 +CONFIG_MB_TIMER_PORT_ENABLED=y +CONFIG_MB_TIMER_GROUP=0 +CONFIG_MB_TIMER_INDEX=0 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_L2_TO_L3_COPY is not set +# CONFIG_USE_ONLY_LWIP_SELECT is not set +CONFIG_ESP_GRATUITOUS_ARP=y +CONFIG_GARP_TMR_INTERVAL=60 +CONFIG_TCPIP_RECVMBOX_SIZE=32 +CONFIG_TCP_MAXRTX=12 +CONFIG_TCP_SYNMAXRTX=12 +CONFIG_TCP_MSS=1440 +CONFIG_TCP_MSL=60000 +CONFIG_TCP_SND_BUF_DEFAULT=5744 +CONFIG_TCP_WND_DEFAULT=5744 +CONFIG_TCP_RECVMBOX_SIZE=6 +CONFIG_TCP_QUEUE_OOSEQ=y +# CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set +CONFIG_TCP_OVERSIZE_MSS=y +# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_TCP_OVERSIZE_DISABLE is not set +CONFIG_UDP_RECVMBOX_SIZE=6 +CONFIG_TCPIP_TASK_STACK_SIZE=3072 +CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y +# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set +# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set +CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF +# CONFIG_PPP_SUPPORT is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y +CONFIG_SUPPORT_TERMIOS=y +CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 +# End of deprecated options diff --git a/code/version.cpp b/code/version.cpp index 14bf63a3..ba140d64 100644 --- a/code/version.cpp +++ b/code/version.cpp @@ -1,4 +1,4 @@ -const char* GIT_REV="eac5134"; -const char* GIT_TAG=""; -const char* GIT_BRANCH="master"; -const char* BUILD_TIME="2022-01-01 08:54"; \ No newline at end of file +const char* GIT_REV="8e8897c"; +const char* GIT_TAG="v10.0.2"; +const char* GIT_BRANCH="rolling"; +const char* BUILD_TIME="2022-01-03 17:21"; \ No newline at end of file diff --git a/firmware/bootloader.bin b/firmware/bootloader.bin index 7449ed4a..047f9fe7 100644 Binary files a/firmware/bootloader.bin and b/firmware/bootloader.bin differ diff --git a/firmware/firmware.bin b/firmware/firmware.bin index 26e8b3df..3d6a4da5 100644 Binary files a/firmware/firmware.bin and b/firmware/firmware.bin differ diff --git a/sd-card/config/config.ini b/sd-card/config/config.ini index 2a0f50bb..f73bc3ec 100644 --- a/sd-card/config/config.ini +++ b/sd-card/config/config.ini @@ -20,7 +20,7 @@ FlipImageSize = false /config/ref1.jpg 442 142 [Digits] -Model = /config/dig1330s1q.tflite +Model = /config/dig-s1-q-20220102.tflite ;LogImageLocation = /log/digit ;LogfileRetentionInDays = 3 ModelInputSize = 20 32 diff --git a/sd-card/config/dig-s1-q-20220102.tflite b/sd-card/config/dig-s1-q-20220102.tflite new file mode 100644 index 00000000..19a515b8 Binary files /dev/null and b/sd-card/config/dig-s1-q-20220102.tflite differ diff --git a/sd-card/config/dig-s1-q-20220102.zip b/sd-card/config/dig-s1-q-20220102.zip new file mode 100644 index 00000000..13861d21 Binary files /dev/null and b/sd-card/config/dig-s1-q-20220102.zip differ diff --git a/sd-card/config/dig1310s3q.tflite b/sd-card/config/dig1310s3q.tflite deleted file mode 100644 index ba637434..00000000 Binary files a/sd-card/config/dig1310s3q.tflite and /dev/null differ