summaryrefslogtreecommitdiff
path: root/nn/common/include/Utils.h
blob: 1d4c6811c6600262dfbae74db0ffb3fd827dacce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 ANDROID_FRAMEWORKS_ML_NN_COMMON_UTILS_H
#define ANDROID_FRAMEWORKS_ML_NN_COMMON_UTILS_H

#include <android-base/logging.h>

#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "HalInterfaces.h"
#include "NeuralNetworks.h"
#include "ValidateHal.h"
#include "nnapi/TypeUtils.h"
#include "nnapi/Types.h"

namespace android {
namespace nn {

// The number of data types (OperandCode) defined in NeuralNetworks.h.
const int kNumberOfDataTypes = 16;

// The number of operation types (OperationCode) defined in NeuralNetworks.h.
const int kNumberOfOperationTypes = 102;

// The number of execution preferences defined in NeuralNetworks.h.
const int kNumberOfPreferences = 3;

// The number of data types (OperandCode) defined in NeuralNetworksOEM.h.
const int kNumberOfDataTypesOEM = 2;

// The number of operation types (OperationCode) defined in NeuralNetworksOEM.h.
const int kNumberOfOperationTypesOEM = 1;

// The lowest number assigned to any OEM Code in NeuralNetworksOEM.h.
const int kOEMCodeBase = 10000;

/* IMPORTANT: if you change the following list, don't
 * forget to update the corresponding 'tags' table in
 * the initVlogMask() function implemented in Utils.cpp.
 */
enum VLogFlags { MODEL = 0, COMPILATION, EXECUTION, CPUEXE, MANAGER, DRIVER, MEMORY };

#define VLOG_IS_ON(TAG) ((vLogMask & (1 << (TAG))) != 0)

#define VLOG(TAG)                 \
    if (LIKELY(!VLOG_IS_ON(TAG))) \
        ;                         \
    else                          \
        LOG(INFO)

extern int vLogMask;
void initVLogMask();

#ifdef NN_DEBUGGABLE
#define SHOW_IF_DEBUG(msg) msg
#else
#define SHOW_IF_DEBUG(msg) ""
#endif

// DEPRECATED(b/118737105). Use CHECK.
#define nnAssert(v) CHECK(v)

#define NN_RETURN_IF_ERROR(expr)                      \
    do {                                              \
        int _errorCode = (expr);                      \
        if (_errorCode != ANEURALNETWORKS_NO_ERROR) { \
            return _errorCode;                        \
        }                                             \
    } while (0)

// The NN_RET_CHECK family of macros defined below is similar to the CHECK family defined in
// system/libbase/include/android-base/logging.h
//
// The difference is that NN_RET_CHECK macros use LOG(ERROR) instead of LOG(FATAL)
// and return false instead of aborting.

// Logs an error and returns false. Append context using << after. For example:
//
//   NN_RET_CHECK_FAIL() << "Something went wrong";
//
// The containing function must return a bool.
#define NN_RET_CHECK_FAIL()                   \
    return ::android::nn::FalseyErrorStream() \
           << "NN_RET_CHECK failed (" << __FILE__ << ":" << __LINE__ << "): "

// Logs an error and returns false if condition is false. Extra logging can be appended using <<
// after. For example:
//
//   NN_RET_CHECK(false) << "Something went wrong";
//
// The containing function must return a bool.
#define NN_RET_CHECK(condition) \
    while (UNLIKELY(!(condition))) NN_RET_CHECK_FAIL() << #condition << " "

// Helper for NN_CHECK_xx(x, y) macros.
#define NN_RET_CHECK_OP(LHS, RHS, OP)                                                       \
    for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);                      \
         UNLIKELY(!(_values.lhs.v OP _values.rhs.v));                                       \
         /* empty */)                                                                       \
    NN_RET_CHECK_FAIL()                                                                     \
            << #LHS << " " << #OP << " " << #RHS << " (" << #LHS << " = "                   \
            << ::android::base::LogNullGuard<decltype(_values.lhs.v)>::Guard(_values.lhs.v) \
            << ", " << #RHS << " = "                                                        \
            << ::android::base::LogNullGuard<decltype(_values.rhs.v)>::Guard(_values.rhs.v) \
            << ") "

// Logs an error and returns false if a condition between x and y does not hold. Extra logging can
// be appended using << after. For example:
//
//   NN_RET_CHECK_EQ(a, b) << "Something went wrong";
//
// The values must implement the appropriate comparison operator as well as
// `operator<<(std::ostream&, ...)`.
// The containing function must return a bool.
#define NN_RET_CHECK_EQ(x, y) NN_RET_CHECK_OP(x, y, ==)
#define NN_RET_CHECK_NE(x, y) NN_RET_CHECK_OP(x, y, !=)
#define NN_RET_CHECK_LE(x, y) NN_RET_CHECK_OP(x, y, <=)
#define NN_RET_CHECK_LT(x, y) NN_RET_CHECK_OP(x, y, <)
#define NN_RET_CHECK_GE(x, y) NN_RET_CHECK_OP(x, y, >=)
#define NN_RET_CHECK_GT(x, y) NN_RET_CHECK_OP(x, y, >)

// Make an TimeoutDuration from a duration in nanoseconds. If the value exceeds
// the max duration, return the maximum expressible duration.
TimeoutDuration makeTimeoutDuration(uint64_t nanoseconds);

// Type to represent a deadline time point across processes.
using Deadline = std::chrono::steady_clock::time_point;

// Make an Deadline from a duration. If the sum of the current time and the
// duration exceeds the max time, return a time point holding the maximum
// expressible time.
Deadline makeDeadline(TimeoutDuration duration);
inline Deadline makeDeadline(uint64_t duration) {
    return makeDeadline(makeTimeoutDuration(duration));
}

// Convenience function. If the duration is provided, this function creates a
// Deadline using makeDeadline. If the duration is not provided, this function
// returns std::nullopt.
inline std::optional<Deadline> makeDeadline(OptionalTimeoutDuration duration) {
    return duration.has_value() ? makeDeadline(*duration) : std::optional<Deadline>{};
}
inline std::optional<Deadline> makeDeadline(std::optional<uint64_t> duration) {
    return duration.has_value() ? makeDeadline(*duration) : std::optional<Deadline>{};
}

// Make an optional Deadline from an OptionalTimePoint. If
// timePoint.nanosecondsSinceEpoch cannot be represented in Deadline, return a
// time point holding the maximum Deadline. If the OptionalTimePoint is none,
// this function returns std::nullopt.
std::optional<Deadline> makeDeadline(const V1_3::OptionalTimePoint& timePoint);

// Returns true if the deadline has passed. Returns false if either the deadline
// has not been exceeded or if the deadline is not present.
bool hasDeadlinePassed(const std::optional<Deadline>& deadline);

// Make an OptionalTimePoint from an optional Deadline. If the Deadline is not
// provided, this function returns none for OptionalTimePoint.
OptionalTimePoint makeTimePoint(const std::optional<Deadline>& deadline);

// Ensure that every user of FalseyErrorStream is linked to the
// correct instance, using the correct LOG_TAG
namespace {

// A wrapper around LOG(ERROR) that can be implicitly converted to bool (always evaluates to false).
// Used to implement stream logging in NN_RET_CHECK.
class FalseyErrorStream {
    DISALLOW_COPY_AND_ASSIGN(FalseyErrorStream);

   public:
    FalseyErrorStream() {}

    template <typename T>
    FalseyErrorStream& operator<<(const T& value) {
        mBuffer << value;
        return *this;
    }

    ~FalseyErrorStream() { LOG(ERROR) << mBuffer.str(); }

    operator bool() const { return false; }

   private:
    std::ostringstream mBuffer;
};

template <HalVersion version>
struct VersionedType {};

template <>
struct VersionedType<HalVersion::V1_2> {
    using OperandPerformance = V1_2::Capabilities::OperandPerformance;
    using OperandType = V1_2::OperandType;
};

template <>
struct VersionedType<HalVersion::V1_3> {
    using OperandPerformance = V1_3::Capabilities::OperandPerformance;
    using OperandType = V1_3::OperandType;
};

template <HalVersion version>
using VersionedOperandPerformance = typename VersionedType<version>::OperandPerformance;
template <HalVersion version>
using VersionedOperandType = typename VersionedType<version>::OperandType;

}  // namespace

// Return a vector with one entry for each non-extension OperandType except
// SUBGRAPH, set to the specified PerformanceInfo value.  The vector will be
// sorted by OperandType.
//
// Control flow (OperandType::SUBGRAPH) operation performance is specified
// separately using Capabilities::ifPerformance and
// Capabilities::whilePerformance.
template <HalVersion version>
hardware::hidl_vec<VersionedOperandPerformance<version>> nonExtensionOperandPerformance(
        V1_0::PerformanceInfo perf);

// Update the vector entry corresponding to the specified OperandType with the
// specified PerformanceInfo value.  The vector must already have an entry for
// that OperandType, and must be sorted by OperandType.
void update(hardware::hidl_vec<V1_2::Capabilities::OperandPerformance>* operandPerformance,
            V1_2::OperandType type, V1_0::PerformanceInfo perf);
void update(hardware::hidl_vec<V1_3::Capabilities::OperandPerformance>* operandPerformance,
            V1_3::OperandType type, V1_0::PerformanceInfo perf);

// Look for a vector entry corresponding to the specified OperandType.  If
// found, return the associated PerformanceInfo.  If not, return a pessimistic
// PerformanceInfo (FLT_MAX).  The vector must be sorted by OperandType.
V1_0::PerformanceInfo lookup(
        const hardware::hidl_vec<V1_2::Capabilities::OperandPerformance>& operandPerformance,
        V1_2::OperandType type);
V1_0::PerformanceInfo lookup(
        const hardware::hidl_vec<V1_3::Capabilities::OperandPerformance>& operandPerformance,
        V1_3::OperandType type);

// Returns true if an operand type is an extension type.
bool isExtensionOperandType(V1_3::OperandType type);

// Returns true if an operation type is an extension type.
bool isExtensionOperationType(V1_3::OperationType type);

// Returns the amount of space needed to store a value of the specified
// dimensions and type. For a tensor with unspecified rank or at least one
// unspecified dimension, returns zero.
//
// Aborts if the specified type is an extension type.
// Aborts if the size would overflow the return type.
//
// See also TypeManager::getSizeOfData(OperandType, const std::vector<uint32_t>&).
uint32_t nonExtensionOperandSizeOfData(V1_3::OperandType type,
                                       const std::vector<uint32_t>& dimensions);
uint32_t nonExtensionOperandSizeOfData(OperandType type, const std::vector<uint32_t>& dimensions);

// Returns the amount of space needed to store a value of the dimensions and
// type of this operand. For a tensor with unspecified rank or at least one
// unspecified dimension, returns zero.
//
// Aborts if the specified type is an extension type.
// Aborts if the size would overflow the return type.
//
// See also TypeManager::getSizeOfData(const Operand&).
inline uint32_t nonExtensionOperandSizeOfData(const Operand& operand) {
    return nonExtensionOperandSizeOfData(operand.type, operand.dimensions);
}
inline uint32_t nonExtensionOperandSizeOfData(const V1_3::Operand& operand) {
    return nonExtensionOperandSizeOfData(operand.type, operand.dimensions);
}

// Returns the amount of space needed to store a value of the specified
// dimensions and element size. For a tensor with unspecified rank or at least
// one unspecified dimension, returns zero.
//
// Aborts if the size would overflow the return type.
//
// See also TypeManager::getSizeOfData(const Operand&).
uint32_t sizeOfTensorData(uint32_t sizeOfElement, const std::vector<uint32_t>& dimensions);

// Returns true if the amount of space needed to store a value of the specified
// dimensions and element size overflows the uint32_t type.
//
// Aborts if the specified type is an extension type.
//
// See also TypeManager::sizeOfDataOverflowsUInt32(OperandType, const std::vector<uint32_t>&).
bool nonExtensionOperandSizeOfDataOverflowsUInt32(OperandType type,
                                                  const std::vector<uint32_t>& dimensions);
bool nonExtensionOperandSizeOfDataOverflowsUInt32(V1_3::OperandType type,
                                                  const std::vector<uint32_t>& dimensions);

// Returns true if the amount of space needed to store a value of the specified
// dimensions and element size overflows the uint32_t type.
//
// See also TypeManager::sizeOfDataOverflowsUInt32(OperandType, const std::vector<uint32_t>&).
bool sizeOfTensorDataOverflowsUInt32(uint32_t elementSize, const std::vector<uint32_t>& dimensions);

// Returns true if a non-extension operand type is a scalar type.
//
// Aborts if the specified type is an extension type.
//
// See also TypeManager::isTensorType(OperandType).
bool nonExtensionOperandTypeIsScalar(int type);

// Returns the name of the operation type in ASCII.
std::string getOperationName(V1_3::OperationType opCode);

// Returns the name of the operand type in ASCII.
std::string getOperandTypeName(V1_3::OperandType type);

// Whether an operand of tensor type has unspecified dimensions.
//
// Undefined behavior if the operand type is a scalar type.
bool tensorHasUnspecifiedDimensions(int type, const uint32_t* dim, uint32_t dimCount);
bool tensorHasUnspecifiedDimensions(V1_3::OperandType type,
                                    const std::vector<uint32_t>& dimensions);
bool tensorHasUnspecifiedDimensions(OperandType type, const std::vector<uint32_t>& dimensions);
bool tensorHasUnspecifiedDimensions(OperandType type, const Dimensions& dimensions);
bool tensorHasUnspecifiedDimensions(const Operand& operand);
bool tensorHasUnspecifiedDimensions(const V1_3::Operand& operand);
bool tensorHasUnspecifiedDimensions(const ANeuralNetworksOperandType* type);

// Returns the number of padding bytes needed to align data of the
// specified length.  It aligns object of length:
// 2, 3 on a 2 byte boundary,
// 4+ on a 4 byte boundary.
// We may want to have different alignments for tensors.
// TODO: This is arbitrary, more a proof of concept.  We need
// to determine what this should be.
uint32_t alignBytesNeeded(uint32_t index, size_t length);

// Does a detailed LOG(INFO) of the model
void logModelToInfo(const V1_0::Model& model);
void logModelToInfo(const V1_1::Model& model);
void logModelToInfo(const V1_2::Model& model);
void logModelToInfo(const V1_3::Model& model);
void logModelToInfo(const Model& model);

inline std::string toString(uint32_t obj) {
    return std::to_string(obj);
}

template <typename Type>
std::string toString(const std::vector<Type>& range) {
    std::string os = "[";
    for (size_t i = 0; i < range.size(); ++i) {
        os += (i == 0 ? "" : ", ") + toString(range[i]);
    }
    return os += "]";
}

template <typename A, typename B>
std::string toString(const std::pair<A, B>& pair) {
    std::ostringstream oss;
    oss << "(" << pair.first << ", " << pair.second << ")";
    return oss.str();
}

inline std::ostream& operator<<(std::ostream& os, const HalVersion& halVersion) {
    switch (halVersion) {
        case HalVersion::UNKNOWN:
            return os << "UNKNOWN HAL version";
        case HalVersion::V1_0:
            return os << "HAL version 1.0";
        case HalVersion::V1_1:
            return os << "HAL version 1.1";
        case HalVersion::V1_2:
            return os << "HAL version 1.2";
        case HalVersion::V1_3:
            return os << "HAL version 1.3";
    }
}

inline bool validCode(uint32_t codeCount, uint32_t codeCountOEM, uint32_t code) {
    return (code < codeCount) || (code >= kOEMCodeBase && (code - kOEMCodeBase) < codeCountOEM);
}

bool validateOperandSymmPerChannelQuantParams(
        const V1_3::Operand& halOperand,
        const ANeuralNetworksSymmPerChannelQuantParams& channelQuant, const char* tag);

// Validates an operand type.
//
// extensionOperandTypeInfo must be nullptr iff the type is not an extension type.
//
// If allowPartial is true, the dimensions may be underspecified.
int validateOperandType(const ANeuralNetworksOperandType& type,
                        const Extension::OperandTypeInformation* const extensionOperandTypeInfo,
                        const char* tag, bool allowPartial);
int validateOperandList(uint32_t count, const uint32_t* list, uint32_t operandCount,
                        const char* tag);

// A set of functions to help validate models containing IF or WHILE operations.
struct SubgraphValidationHelper {
    // Checks if a given operand is a SUBGRAPH operand with a valid offset.
    std::function<bool(const Operand&)> isValidSubgraphReference;
    // Gets the input count of a subgraph referenced by a given operand.
    std::function<uint32_t(const Operand&)> getSubgraphInputCount;
    // Gets the output count of a subgraph referenced by a given operand.
    std::function<uint32_t(const Operand&)> getSubgraphOutputCount;
    // Gets the specified input operand of a subgraph referenced by a given operand.
    std::function<const Operand*(const Operand&, uint32_t)> getSubgraphInputOperand;
    // Gets the specified output operand of a subgraph referenced by a given operand.
    std::function<const Operand*(const Operand&, uint32_t)> getSubgraphOutputOperand;
    // Whether control flow operations with inner or outer input or output
    // operands of unknown size are allowed.
    bool allowControlFlowOperationWithOperandOfUnknownSize;
};

// Returns ANEURALNETWORKS_NO_ERROR if the corresponding operation is defined and can handle the
// provided operand types in the given HAL version, otherwise returns ANEURALNETWORKS_BAD_DATA.
// The last argument is only used for validating IF and WHILE operations.
int validateOperation(ANeuralNetworksOperationType opType, uint32_t inputCount,
                      const uint32_t* inputIndexes, uint32_t outputCount,
                      const uint32_t* outputIndexes, const std::vector<Operand>& operands,
                      HalVersion halVersion, const SubgraphValidationHelper& helper);

inline size_t getSizeFromInts(int lower, int higher) {
    return (uint32_t)(lower) + ((uint64_t)(uint32_t)(higher) << 32);
}

// Convert ANEURALNETWORKS_* result code to ErrorStatus.
// Not guaranteed to be a 1-to-1 mapping.
ErrorStatus convertResultCodeToErrorStatus(int resultCode);
V1_3::ErrorStatus convertResultCodeToHalErrorStatus(int resultCode);

// Convert ErrorStatus to ANEURALNETWORKS_* result code.
// Not guaranteed to be a 1-to-1 mapping.
int convertErrorStatusToResultCode(ErrorStatus status);
int convertErrorStatusToResultCode(V1_3::ErrorStatus status);

// Convert execution results to runtime format. Additionally checks that the
// returned results abide by the HAL specification, and logs an error if the
// result violates the specification.
std::tuple<int, std::vector<OutputShape>, Timing> getExecutionResult(
        V1_3::ErrorStatus status, const hardware::hidl_vec<V1_2::OutputShape>& outputShapes,
        const V1_2::Timing& timing);
std::tuple<int, std::vector<OutputShape>, Timing> getExecutionResult(
        ErrorStatus status, std::vector<OutputShape> outputShapes, Timing timing);

// Versioning

bool compliantWithV1_0(const V1_0::Capabilities& capabilities);
bool compliantWithV1_0(const V1_1::Capabilities& capabilities);
bool compliantWithV1_0(const V1_2::Capabilities& capabilities);
bool compliantWithV1_0(const V1_3::Capabilities& capabilities);
bool compliantWithV1_1(const V1_0::Capabilities& capabilities);
bool compliantWithV1_1(const V1_1::Capabilities& capabilities);
bool compliantWithV1_1(const V1_2::Capabilities& capabilities);
bool compliantWithV1_1(const V1_3::Capabilities& capabilities);
bool compliantWithV1_2(const V1_0::Capabilities& capabilities);
bool compliantWithV1_2(const V1_1::Capabilities& capabilities);
bool compliantWithV1_2(const V1_2::Capabilities& capabilities);
bool compliantWithV1_2(const V1_3::Capabilities& capabilities);
bool compliantWithV1_3(const V1_0::Capabilities& capabilities);
bool compliantWithV1_3(const V1_1::Capabilities& capabilities);
bool compliantWithV1_3(const V1_2::Capabilities& capabilities);
bool compliantWithV1_3(const V1_3::Capabilities& capabilities);

// If noncompliantOperations != nullptr, then
//     precondition: noncompliantOperations->empty()
//     postcondition: *noncompliantOperations consists of the indices of the noncompliant
//                    operations; if the compliance check fails for some reason
//                    other than a noncompliant operation,
//                    *noncompliantOperations consists of the indices of all operations
bool compliantWithV1_0(const V1_0::Model& model);
bool compliantWithV1_0(const V1_1::Model& model);
bool compliantWithV1_0(const V1_2::Model& model,
                       std::set<uint32_t>* noncompliantOperations = nullptr);
bool compliantWithV1_0(const V1_3::Model& model,
                       std::set<uint32_t>* noncompliantOperations = nullptr);
bool compliantWithV1_1(const V1_0::Model& model);
bool compliantWithV1_1(const V1_1::Model& model);
bool compliantWithV1_1(const V1_2::Model& model,
                       std::set<uint32_t>* noncompliantOperations = nullptr);
bool compliantWithV1_1(const V1_3::Model& model,
                       std::set<uint32_t>* noncompliantOperations = nullptr);
bool compliantWithV1_2(const V1_0::Model& model);
bool compliantWithV1_2(const V1_1::Model& model);
bool compliantWithV1_2(const V1_2::Model& model,
                       std::set<uint32_t>* noncompliantOperations = nullptr);
bool compliantWithV1_2(const V1_3::Model& model,
                       std::set<uint32_t>* noncompliantOperations = nullptr);

V1_0::ErrorStatus convertToV1_0(V1_0::ErrorStatus status);
V1_0::ErrorStatus convertToV1_0(V1_3::ErrorStatus status);
V1_3::ErrorStatus convertToV1_3(V1_0::ErrorStatus status);
V1_3::ErrorStatus convertToV1_3(V1_3::ErrorStatus status);

V1_0::Capabilities convertToV1_0(const V1_0::Capabilities& capabilities);
V1_0::Capabilities convertToV1_0(const V1_1::Capabilities& capabilities);
V1_0::Capabilities convertToV1_0(const V1_2::Capabilities& capabilities);
V1_0::Capabilities convertToV1_0(const V1_3::Capabilities& capabilities);
V1_1::Capabilities convertToV1_1(const V1_0::Capabilities& capabilities);
V1_1::Capabilities convertToV1_1(const V1_1::Capabilities& capabilities);
V1_1::Capabilities convertToV1_1(const V1_2::Capabilities& capabilities);
V1_1::Capabilities convertToV1_1(const V1_3::Capabilities& capabilities);
V1_2::Capabilities convertToV1_2(const V1_0::Capabilities& capabilities);
V1_2::Capabilities convertToV1_2(const V1_1::Capabilities& capabilities);
V1_2::Capabilities convertToV1_2(const V1_2::Capabilities& capabilities);
V1_2::Capabilities convertToV1_2(const V1_3::Capabilities& capabilities);
V1_3::Capabilities convertToV1_3(const V1_0::Capabilities& capabilities);
V1_3::Capabilities convertToV1_3(const V1_1::Capabilities& capabilities);
V1_3::Capabilities convertToV1_3(const V1_2::Capabilities& capabilities);
V1_3::Capabilities convertToV1_3(const V1_3::Capabilities& capabilities);

V1_0::Model convertToV1_0(const V1_0::Model& model);
V1_0::Model convertToV1_0(const V1_1::Model& model);
V1_0::Model convertToV1_0(const V1_2::Model& model);
V1_0::Model convertToV1_0(const V1_3::Model& model);
V1_1::Model convertToV1_1(const V1_0::Model& model);
V1_1::Model convertToV1_1(const V1_1::Model& model);
V1_1::Model convertToV1_1(const V1_2::Model& model);
V1_1::Model convertToV1_1(const V1_3::Model& model);
V1_2::Model convertToV1_2(const V1_0::Model& model);
V1_2::Model convertToV1_2(const V1_1::Model& model);
V1_2::Model convertToV1_2(const V1_2::Model& model);
V1_2::Model convertToV1_2(const V1_3::Model& model);
V1_3::Model convertToV1_3(const V1_0::Model& model);
V1_3::Model convertToV1_3(const V1_1::Model& model);
V1_3::Model convertToV1_3(const V1_2::Model& model);
V1_3::Model convertToV1_3(const V1_3::Model& model);

V1_0::OperationType uncheckedConvertToV1_0(V1_3::OperationType type);
V1_1::OperationType uncheckedConvertToV1_1(V1_3::OperationType type);
V1_2::OperationType uncheckedConvertToV1_2(V1_3::OperationType type);

V1_0::Operand convertToV1_0(const V1_2::Operand& operand);
V1_0::Operand convertToV1_0(const V1_3::Operand& operand);
V1_2::Operand convertToV1_2(const V1_0::Operand& operand);
V1_2::Operand convertToV1_2(const V1_3::Operand& operand);
V1_3::Operand convertToV1_3(const V1_0::Operand& operand);
V1_3::Operand convertToV1_3(const V1_2::Operand& operand);
V1_3::Operand convertToV1_3(const V1_3::Operand& operand);

hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_0::Operand>& operands);
hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_2::Operand>& operands);
hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_3::Operand>& operands);
hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_0::Operand>& operands);
hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_2::Operand>& operands);
hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_3::Operand>& operands);
hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_0::Operand>& operands);
hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_2::Operand>& operands);
hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_3::Operand>& operands);

bool compliantWithV1_0(const V1_0::Request& request);
bool compliantWithV1_0(const V1_3::Request& request);
bool compliantWithV1_2(const V1_3::Request& request);

V1_0::Request convertToV1_0(const V1_0::Request& request);
V1_0::Request convertToV1_0(const V1_3::Request& request);
V1_0::Request convertToV1_2(const V1_3::Request& request);
V1_3::Request convertToV1_3(const V1_0::Request& request);
V1_3::Request convertToV1_3(const V1_3::Request& request);

bool compliantWithV1_0(V1_0::OperandLifeTime lifetime);
bool compliantWithV1_0(V1_3::OperandLifeTime lifetime);
bool compliantWithV1_3(V1_0::OperandLifeTime lifetime);
bool compliantWithV1_3(V1_3::OperandLifeTime lifetime);

V1_0::OperandLifeTime convertToV1_0(V1_0::OperandLifeTime lifetime);
V1_0::OperandLifeTime convertToV1_0(V1_3::OperandLifeTime lifetime);
V1_3::OperandLifeTime convertToV1_3(V1_0::OperandLifeTime lifetime);
V1_3::OperandLifeTime convertToV1_3(V1_3::OperandLifeTime lifetime);

constexpr V1_3::Priority convertToHalPriority(int32_t priority) {
    switch (priority) {
        case ANEURALNETWORKS_PRIORITY_LOW:
            return V1_3::Priority::LOW;
        case ANEURALNETWORKS_PRIORITY_MEDIUM:
            return V1_3::Priority::MEDIUM;
        case ANEURALNETWORKS_PRIORITY_HIGH:
            return V1_3::Priority::HIGH;
    }
    LOG(FATAL) << "unrecognized priority: " << priority;
    return {};
}

constexpr Priority convertToCanonicalPriority(int32_t priority) {
    switch (priority) {
        case ANEURALNETWORKS_PRIORITY_LOW:
            return Priority::LOW;
        case ANEURALNETWORKS_PRIORITY_MEDIUM:
            return Priority::MEDIUM;
        case ANEURALNETWORKS_PRIORITY_HIGH:
            return Priority::HIGH;
    }
    LOG(FATAL) << "unrecognized priority: " << priority;
    return {};
}

// The function syncWait() has the same semantics as the system function
// ::sync_wait(), except that the syncWait() return value is semantically
// richer.  The timeout parameter is in msecs.
enum class FenceState {
    ACTIVE,    // fence has not been signaled
    SIGNALED,  // fence has been signaled
    ERROR,     // fence has been placed in the error state
    UNKNOWN,   // either bad argument passed to syncWait(), or internal error
};
FenceState syncWait(int fd, int timeout);

#ifdef NN_DEBUGGABLE
uint32_t getProp(const char* str, uint32_t defaultValue = 0);
#endif  // NN_DEBUGGABLE

// DEPRECATED. Use checked conversions from nnapi/hal/1.X/Conversions.h.
Capabilities::OperandPerformance uncheckedConvert(
        const V1_3::Capabilities::OperandPerformance& operandPerformance);
Capabilities::PerformanceInfo uncheckedConvert(const V1_0::PerformanceInfo& performanceInfo);
Capabilities uncheckedConvert(const V1_3::Capabilities& capabilities);
DataLocation uncheckedConvert(const V1_0::DataLocation& location);
ErrorStatus uncheckedConvert(V1_0::ErrorStatus status);
ErrorStatus uncheckedConvert(V1_3::ErrorStatus status);
Extension::OperandTypeInformation uncheckedConvert(const V1_2::Extension::OperandTypeInformation&);
Extension uncheckedConvert(const V1_2::Extension& extension);
hardware::hidl_vec<uint8_t> uncheckedConvert(const Operand::ExtensionParams& params);
MeasureTiming uncheckedConvert(V1_2::MeasureTiming measure);
Memory uncheckedConvert(const hardware::hidl_memory& memory);
Model::ExtensionNameAndPrefix uncheckedConvert(const V1_2::Model::ExtensionNameAndPrefix&);
Model::Subgraph uncheckedConvert(const V1_3::Subgraph& subgraph);
Model uncheckedConvert(const V1_3::Model& model);
Operand::ExtensionParams uncheckedConvert(const hardware::hidl_vec<uint8_t>& params);
Operand::ExtraParams uncheckedConvert(const V1_2::Operand::ExtraParams& params);
Operand::LifeTime uncheckedConvert(V1_3::OperandLifeTime lifetime);
Operand::SymmPerChannelQuantParams uncheckedConvert(const V1_2::SymmPerChannelQuantParams& params);
OperandType uncheckedConvert(V1_3::OperandType operandType);
Operand uncheckedConvert(const V1_3::Operand& operand);
OperationType uncheckedConvert(V1_3::OperationType operationType);
Operation uncheckedConvert(const V1_3::Operation& operation);
OptionalTimeoutDuration uncheckedConvert(const V1_3::OptionalTimeoutDuration& timeoutDuration);
OutputShape uncheckedConvert(const V1_2::OutputShape& outputShape);
Request::Argument uncheckedConvert(const V1_0::RequestArgument& requestArgument);
Request::MemoryPool uncheckedConvert(const V1_3::Request::MemoryPool& memoryPool);
Request uncheckedConvert(const V1_3::Request& request);
std::vector<Extension> uncheckedConvert(const hardware::hidl_vec<V1_2::Extension>& extensions);
std::vector<Memory> uncheckedConvert(const hardware::hidl_vec<hardware::hidl_memory>& memories);
std::vector<Model::Subgraph> uncheckedConvert(const hardware::hidl_vec<V1_3::Subgraph>& subgraphs);
std::vector<Operand> uncheckedConvert(const hardware::hidl_vec<V1_3::Operand>& operands);
std::vector<OutputShape> uncheckedConvert(
        const hardware::hidl_vec<V1_2::OutputShape>& outputShapes);
std::vector<Request::MemoryPool> uncheckedConvert(
        const hardware::hidl_vec<V1_3::Request::MemoryPool>& memoryPools);
Timing uncheckedConvert(const V1_2::Timing& timing);

// DEPRECATED. Use conversions from nnapi/hal/1.X/Conversions.h.
hardware::hidl_memory convertToV1_0(const Memory& memory);
hardware::hidl_vec<hardware::hidl_memory> convertToV1_0(const std::vector<Memory>& memories);
hardware::hidl_vec<uint8_t> convertToV1_0(const Model::OperandValues& operandValues);
hardware::hidl_vec<V1_2::OutputShape> convertToV1_2(const std::vector<OutputShape>& outputShapes);
hardware::hidl_vec<V1_3::BufferRole> convertToV1_3(const std::vector<BufferRole>& bufferRoles);
V1_0::DataLocation convertToV1_0(const DataLocation& location);
V1_0::ErrorStatus convertToV1_0(ErrorStatus status);
V1_0::RequestArgument convertToV1_0(const Request::Argument& requestArgument);
V1_1::ExecutionPreference convertToV1_1(ExecutionPreference preference);
V1_2::MeasureTiming convertToV1_2(MeasureTiming measure);
V1_2::Model::ExtensionNameAndPrefix convertToV1_2(const Model::ExtensionNameAndPrefix&);
V1_2::Operand::ExtraParams convertToV1_2(const Operand::ExtraParams& params);
V1_2::OutputShape convertToV1_2(const OutputShape& outputShape);
V1_2::SymmPerChannelQuantParams convertToV1_2(const Operand::SymmPerChannelQuantParams& params);
V1_2::Timing convertToV1_2(const Timing& timing);
V1_3::BufferRole convertToV1_3(const BufferRole& bufferRole);
V1_3::ErrorStatus convertToV1_3(ErrorStatus status);
V1_3::Model convertToV1_3(const Model& model);
V1_3::Operand convertToV1_3(const Operand& operand);
V1_3::OperandLifeTime convertToV1_3(Operand::LifeTime lifetime);
V1_3::OperandType convertToV1_3(OperandType operandType);
V1_3::Operation convertToV1_3(const Operation& operation);
V1_3::OperationType convertToV1_3(OperationType operationType);
V1_3::OptionalTimeoutDuration convertToV1_3(const OptionalTimeoutDuration& timeoutDuration);
V1_3::OptionalTimePoint convertToV1_3(const OptionalTimePoint& timePoint);
V1_3::Priority convertToV1_3(Priority priority);
V1_3::Request convertToV1_3(const Request& request);
V1_3::Request::MemoryPool convertToV1_3(const Request::MemoryPool& memoryPool);
V1_3::Subgraph convertToV1_3(const Model::Subgraph& model);

}  // namespace nn
}  // namespace android

#endif  // ANDROID_FRAMEWORKS_ML_NN_COMMON_UTILS_H