• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef CODEC_CAPABILITIES__UTILS_H_
18 
19 #define CODEC_CAPABILITIES__UTILS_H_
20 
21 #include <algorithm>
22 #include <cerrno>
23 #include <cmath>
24 #include <cstdlib>
25 #include <numeric>
26 #include <optional>
27 #include <regex>
28 #include <string>
29 #include <vector>
30 
31 #include <utils/Log.h>
32 
33 #include <media/stagefright/foundation/AUtils.h>
34 
35 namespace android {
36 
37 struct ProfileLevel {
38     uint32_t mProfile;
39     uint32_t mLevel;
40     bool operator <(const ProfileLevel &o) const {
41         return mProfile < o.mProfile || (mProfile == o.mProfile && mLevel < o.mLevel);
42     }
43 };
44 
45 struct Feature {
46     std::string mName;
47     int mValue;
48     bool mDefault;
49     bool mInternal;
FeatureFeature50     Feature(std::string name, int value, bool def, bool internal) {
51         mName = name;
52         mValue = value;
53         mDefault = def;
54         mInternal = internal;
55     }
FeatureFeature56     Feature(std::string name, int value, bool def) :
57         Feature(name, value, def, false /* internal */) {}
58 };
59 
60 /**
61  * Immutable class for describing the range of two numeric values.
62  *
63  * To make it immutable, all data are private and all functions are const.
64  *
65  * From frameworks/base/core/java/android/util/Range.java
66  */
67 template<typename T>
68 struct Range {
RangeRange69     Range() : lower_(), upper_() {}
70 
RangeRange71     Range(T l, T u) : lower_(l), upper_(u) {}
72 
emptyRange73     constexpr bool empty() const { return lower_ > upper_; }
74 
lowerRange75     T lower() const { return lower_; }
76 
upperRange77     T upper() const { return upper_; }
78 
79     // Check if a value is in the range.
containsRange80     bool contains(T value) const {
81         return lower_ <= value && upper_ >= value;
82     }
83 
containsRange84     bool contains(Range<T> range) const {
85         return (range.lower_ >= lower_) && (range.upper_ <= upper_);
86     }
87 
88     // Clamp a value in the range
clampRange89     T clamp(T value) const{
90         if (value < lower_) {
91             return lower_;
92         } else if (value > upper_) {
93             return upper_;
94         } else {
95             return value;
96         }
97     }
98 
99     // Return the intersected range
intersectRange100     Range<T> intersect(Range<T> range) const {
101         if (lower_ >= range.lower() && range.upper() >= upper_) {
102             // range includes this
103             return *this;
104         } else if (range.lower() >= lower_ && range.upper() <= upper_) {
105             // this includes range
106             return range;
107         } else {
108             // if ranges are disjoint returns an empty Range(lower > upper)
109             Range<T> result = Range<T>(std::max(lower_, range.lower_),
110                     std::min(upper_, range.upper_));
111             if (result.empty()) {
112                 ALOGV("Failed to intersect 2 ranges as they are disjoint");
113             }
114             return result;
115         }
116     }
117 
118     /**
119      * Returns the intersection of this range and the inclusive range
120      * specified by {@code [lower, upper]}.
121      * <p>
122      * See {@link #intersect(Range)} for more details.</p>
123      *
124      * @param lower a non-{@code null} {@code T} reference
125      * @param upper a non-{@code null} {@code T} reference
126      * @return the intersection of this range and the other range
127      */
intersectRange128     Range<T> intersect(T lower, T upper) {
129         Range<T> range = Range<T>(lower, upper);
130         return this->intersect(range);
131     }
132 
133     /**
134      * Returns the smallest range that includes this range and
135      * another range.
136      *
137      * E.g. if a < b < c < d, the
138      * extension of [a, c] and [b, d] ranges is [a, d].
139      * As the endpoints are object references, there is no guarantee
140      * which specific endpoint reference is used from the input ranges:
141      *
142      * E.g. if a == a' < b < c, the
143      * extension of [a, b] and [a', c] ranges could be either
144      * [a, c] or ['a, c], where ['a, c] could be either the exact
145      * input range, or a newly created range with the same endpoints.
146      *
147      * @param range a non-null Range<T> reference
148      * @return the extension of this range and the other range.
149      */
extendRange150     Range<T> extend(Range<T> range) {
151         return Range<T>(std::min(lower_, range.lower_), std::max(upper_, range.upper_));
152     }
153 
alignRange154     Range<T> align(T align) {
155         return this->intersect(
156                 divUp(lower_, align) * align, (upper_ / align) * align);
157     }
158 
factorRange159     Range<T> factor(T factor) {
160         if (factor == 1) {
161             return *this;
162         }
163         return Range(divUp(this->lower(), factor), this->upper() / factor);
164     }
165 
166     // parse a string into a range
ParseRange167     static std::optional<Range<T>> Parse(const std::string &str) {
168         if (str.empty()) {
169             ALOGW("could not parse empty integer range");
170             return std::nullopt;
171         }
172         long long lower, upper;
173         std::regex regex("^([0-9]+)-([0-9]+)$");
174         std::smatch match;
175         errno = 0;
176         if (std::regex_match(str, match, regex)) {
177             lower = std::strtoll(match[1].str().c_str(), NULL, 10);
178             upper = std::strtoll(match[2].str().c_str(), NULL, 10);
179         } else {
180             char *end;
181             lower = upper = std::strtoll(str.c_str(), &end, 10);
182             if (*end != '\0') {
183                 ALOGW("could not parse integer range: %s", str.c_str());
184                 return std::nullopt;
185             }
186         }
187 
188         if (errno == ERANGE || lower < std::numeric_limits<T>::min()
189                 || std::numeric_limits<T>::max() < upper || upper < lower) {
190             ALOGW("could not parse integer range: %s", str.c_str());
191             return std::nullopt;
192         }
193 
194         return std::make_optional<Range<T>>((T)lower, (T)upper);
195     }
196 
RangeForRange197     static Range<T> RangeFor(double v) {
198         return Range((T)v, (T)ceil(v));
199     }
200 
201 private:
202     T lower_;
203     T upper_;
204 };
205 
206 static const Range<int32_t> POSITIVE_INT32 = Range<int32_t>(1, INT32_MAX);
207 
208 // found stuff that is not supported by framework (=> this should not happen)
209 constexpr int ERROR_CAPABILITIES_UNRECOGNIZED   = (1 << 0);
210 // found profile/level for which we don't have capability estimates
211 constexpr int ERROR_CAPABILITIES_UNSUPPORTED    = (1 << 1);
212 // have not found any profile/level for which we don't have capability estimate
213 constexpr int ERROR_CAPABILITIES_NONE_SUPPORTED = (1 << 2);
214 
215 /**
216  * Sorts distinct (non-intersecting) range array in ascending order.
217  * From frameworks/base/media/java/android/media/Utils.java
218  */
219 template<typename T>
sortDistinctRanges(std::vector<Range<T>> * ranges)220 void sortDistinctRanges(std::vector<Range<T>> *ranges) {
221     std::sort(ranges->begin(), ranges->end(),
222             [](Range<T> r1, Range<T> r2) {
223         if (r1.upper() < r2.lower()) {
224             return true;
225         } else if (r1.lower() > r2.upper()) {
226             return false;
227         } else {
228             ALOGE("sample rate ranges must be distinct.");
229             return false;
230         }
231     });
232 }
233 
234 /**
235  * Returns the intersection of two sets of non-intersecting ranges
236  * From frameworks/base/media/java/android/media/Utils.java
237  * @param one a sorted set of non-intersecting ranges in ascending order
238  * @param another another sorted set of non-intersecting ranges in ascending order
239  * @return the intersection of the two sets, sorted in ascending order
240  */
241 template<typename T>
intersectSortedDistinctRanges(const std::vector<Range<T>> & one,const std::vector<Range<T>> & another)242 std::vector<Range<T>> intersectSortedDistinctRanges(
243         const std::vector<Range<T>> &one, const std::vector<Range<T>> &another) {
244     std::vector<Range<T>> result;
245     int ix = 0;
246     for (Range<T> range : another) {
247         while (ix < one.size() && one[ix].upper() < range.lower()) {
248             ++ix;
249         }
250         while (ix < one.size() && one[ix].upper() < range.upper()) {
251             result.push_back(range.intersect(one[ix]));
252             ++ix;
253         }
254         if (ix == one.size()) {
255             break;
256         }
257         if (one[ix].lower() <= range.upper()) {
258             result.push_back(range.intersect(one[ix]));
259         }
260     }
261     return result;
262 }
263 
264 /**
265  * Immutable class for describing width and height dimensions in pixels.
266  */
267 struct VideoSize {
268     /**
269      * Create a new immutable VideoSize instance.
270      *
271      * @param width The width of the size, in pixels
272      * @param height The height of the size, in pixels
273      */
274     VideoSize(int32_t width, int32_t height);
275 
276     // default constructor
277     VideoSize();
278 
279     /**
280      * Get the width of the size (in pixels).
281      * @return width
282      */
283     int32_t getWidth() const;
284 
285     /**
286      * Get the height of the size (in pixels).
287      * @return height
288      */
289     int32_t getHeight() const;
290 
291     /**
292      * Check if this size is equal to another size.
293      *
294      * Two sizes are equal if and only if both their widths and heights are
295      * equal.
296      *
297      * A size object is never equal to any other type of object.
298      *
299      * @return true if the objects were equal, false otherwise
300      */
301     bool equals(VideoSize other) const;
302 
303     bool empty() const;
304 
305     std::string toString() const;
306 
307     /**
308      * Parses the specified string as a size value.
309      *
310      * The ASCII characters {@code \}{@code u002a} ('*') and
311      * {@code \}{@code u0078} ('x') are recognized as separators between
312      * the width and height.
313      *
314      * For any {@code VideoSize s}: {@code VideoSize::ParseSize(s.toString()).equals(s)}.
315      * However, the method also handles sizes expressed in the
316      * following forms:
317      *
318      * "<i>width</i>{@code x}<i>height</i>" or
319      * "<i>width</i>{@code *}<i>height</i>" {@code => new VideoSize(width, height)},
320      * where <i>width</i> and <i>height</i> are string integers potentially
321      * containing a sign, such as "-10", "+7" or "5".
322      *
323      * <pre>{@code
324      * VideoSize::ParseSize("3*+6").equals(new VideoSize(3, 6)) == true
325      * VideoSize::ParseSize("-3x-6").equals(new VideoSize(-3, -6)) == true
326      * VideoSize::ParseSize("4 by 3") => throws NumberFormatException
327      * }</pre>
328      *
329      * @param string the string representation of a size value.
330      * @return the size value represented by {@code string}.
331      */
332     static std::optional<VideoSize> ParseSize(std::string str);
333 
334     static std::optional<std::pair<VideoSize, VideoSize>> ParseSizeRange(const std::string str);
335 
336     static Range<int32_t> GetAllowedDimensionRange();
337 
338 private:
339     int32_t mWidth;
340     int32_t mHeight;
341 };
342 
343 // This is used for the std::map<VideoSize> in VideoCapabilities
344 struct VideoSizeCompare {
operatorVideoSizeCompare345     bool operator() (const VideoSize& lhs, const VideoSize& rhs) const {
346         if (lhs.getWidth() == rhs.getWidth()) {
347             return lhs.getHeight() < rhs.getHeight();
348         } else {
349             return lhs.getWidth() < rhs.getWidth();
350         }
351     }
352 };
353 
354 /**
355  * An immutable data type representation a rational number.
356  *
357  * Contains a pair of ints representing the numerator and denominator of a
358  * Rational number.
359  */
360 struct Rational {
361     /**
362      * <p>Create a {@code Rational} with a given numerator and denominator.</p>
363      *
364      * <p>The signs of the numerator and the denominator may be flipped such that the denominator
365      * is always positive. Both the numerator and denominator will be converted to their reduced
366      * forms (see {@link #equals} for more details).</p>
367      *
368      * <p>For example,
369      * <ul>
370      * <li>a rational of {@code 2/4} will be reduced to {@code 1/2}.
371      * <li>a rational of {@code 1/-1} will be flipped to {@code -1/1}
372      * <li>a rational of {@code 5/0} will be reduced to {@code 1/0}
373      * <li>a rational of {@code 0/5} will be reduced to {@code 0/1}
374      * </ul>
375      * </p>
376      *
377      * @param numerator the numerator of the rational
378      * @param denominator the denominator of the rational
379      *
380      * @see #equals
381      */
RationalRational382     Rational(int32_t numerator, int32_t denominator) {
383         if (denominator < 0) {
384             numerator = -numerator;
385             denominator = -denominator;
386         }
387 
388         // Convert to reduced form
389         if (denominator == 0 && numerator > 0) {
390             mNumerator = 1; // +Inf
391             mDenominator = 0;
392         } else if (denominator == 0 && numerator < 0) {
393             mNumerator = -1; // -Inf
394             mDenominator = 0;
395         } else if (denominator == 0 && numerator == 0) {
396             mNumerator = 0; // NaN
397             mDenominator = 0;
398         } else if (numerator == 0) {
399             mNumerator = 0;
400             mDenominator = 1;
401         } else {
402             int gcd = std::gcd(numerator, denominator);
403 
404             mNumerator = numerator / gcd;
405             mDenominator = denominator / gcd;
406         }
407     }
408 
409     // default constructor;
RationalRational410     Rational() {
411         Rational(0, 0);
412     }
413 
414     /**
415      * Gets the numerator of the rational.
416      *
417      * <p>The numerator will always return {@code 1} if this rational represents
418      * infinity (that is, the denominator is {@code 0}).</p>
419      */
getNumeratorRational420     int32_t getNumerator() const {
421         return mNumerator;
422     }
423 
424     /**
425      * Gets the denominator of the rational
426      *
427      * <p>The denominator may return {@code 0}, in which case the rational may represent
428      * positive infinity (if the numerator was positive), negative infinity (if the numerator
429      * was negative), or {@code NaN} (if the numerator was {@code 0}).</p>
430      *
431      * <p>The denominator will always return {@code 1} if the numerator is {@code 0}.
432      */
getDenominatorRational433     int32_t getDenominator() const {
434         return mDenominator;
435     }
436 
437     /**
438      * Indicates whether this rational is a <em>Not-a-Number (NaN)</em> value.
439      *
440      * <p>A {@code NaN} value occurs when both the numerator and the denominator are {@code 0}.</p>
441      *
442      * @return {@code true} if this rational is a <em>Not-a-Number (NaN)</em> value;
443      *         {@code false} if this is a (potentially infinite) number value
444      */
isNaNRational445     bool isNaN() const {
446         return mDenominator == 0 && mNumerator == 0;
447     }
448 
449     /**
450      * Indicates whether this rational represents an infinite value.
451      *
452      * <p>An infinite value occurs when the denominator is {@code 0} (but the numerator is not).</p>
453      *
454      * @return {@code true} if this rational is a (positive or negative) infinite value;
455      *         {@code false} if this is a finite number value (or {@code NaN})
456      */
isInfiniteRational457     bool isInfinite() const {
458         return mNumerator != 0 && mDenominator == 0;
459     }
460 
461     /**
462      * Indicates whether this rational represents a finite value.
463      *
464      * <p>A finite value occurs when the denominator is not {@code 0}; in other words
465      * the rational is neither infinity or {@code NaN}.</p>
466      *
467      * @return {@code true} if this rational is a (positive or negative) infinite value;
468      *         {@code false} if this is a finite number value (or {@code NaN})
469      */
isFiniteRational470     bool isFinite() const {
471         return mDenominator != 0;
472     }
473 
474     /**
475      * Indicates whether this rational represents a zero value.
476      *
477      * <p>A zero value is a {@link #isFinite finite} rational with a numerator of {@code 0}.</p>
478      *
479      * @return {@code true} if this rational is finite zero value;
480      *         {@code false} otherwise
481      */
isZeroRational482     bool isZero() const {
483         return isFinite() && mNumerator == 0;
484     }
485 
486     /**
487      * Return a string representation of this rational, e.g. {@code "1/2"}.
488      *
489      * <p>The following rules of conversion apply:
490      * <ul>
491      * <li>{@code NaN} values will return {@code "NaN"}
492      * <li>Positive infinity values will return {@code "Infinity"}
493      * <li>Negative infinity values will return {@code "-Infinity"}
494      * <li>All other values will return {@code "numerator/denominator"} where {@code numerator}
495      * and {@code denominator} are substituted with the appropriate numerator and denominator
496      * values.
497      * </ul></p>
498      */
toStringRational499     std::string toString() const {
500         if (isNaN()) {
501             return "NaN";
502         } else if (isPosInf()) {
503             return "Infinity";
504         } else if (isNegInf()) {
505             return "-Infinity";
506         } else {
507             return std::to_string(mNumerator) + "/" + std::to_string(mDenominator);
508         }
509     }
510 
511     /**
512      * Returns the value of the specified number as a {@code double}.
513      *
514      * <p>The {@code double} is calculated by converting both the numerator and denominator
515      * to a {@code double}; then returning the result of dividing the numerator by the
516      * denominator.</p>
517      *
518      * @return the divided value of the numerator and denominator as a {@code double}.
519      */
asDoubleRational520     double asDouble() const {
521         double num = mNumerator;
522         double den = mDenominator;
523 
524         return num / den;
525     }
526 
527     /**
528      * Returns the value of the specified number as a {@code float}.
529      *
530      * <p>The {@code float} is calculated by converting both the numerator and denominator
531      * to a {@code float}; then returning the result of dividing the numerator by the
532      * denominator.</p>
533      *
534      * @return the divided value of the numerator and denominator as a {@code float}.
535      */
asfloatRational536     float asfloat() const {
537         float num = mNumerator;
538         float den = mDenominator;
539 
540         return num / den;
541     }
542 
543     /**
544      * Returns the value of the specified number as a {@code int}.
545      *
546      * <p>{@link #isInfinite Finite} rationals are converted to an {@code int} value
547      * by dividing the numerator by the denominator; conversion for non-finite values happens
548      * identically to casting a floating point value to an {@code int}, in particular:
549      *
550      * @return the divided value of the numerator and denominator as a {@code int}.
551      */
asInt32Rational552     int32_t asInt32() const {
553         // Mimic float to int conversion rules from JLS 5.1.3
554 
555         if (isPosInf()) {
556             return INT32_MAX;
557         } else if (isNegInf()) {
558             return INT32_MIN;
559         } else if (isNaN()) {
560             return 0;
561         } else { // finite
562             return mNumerator / mDenominator;
563         }
564     }
565 
566     /**
567      * Returns the value of the specified number as a {@code long}.
568      *
569      * <p>{@link #isInfinite Finite} rationals are converted to an {@code long} value
570      * by dividing the numerator by the denominator; conversion for non-finite values happens
571      * identically to casting a floating point value to a {@code long}, in particular:
572      *
573      * @return the divided value of the numerator and denominator as a {@code long}.
574      */
asInt64Rational575     int64_t asInt64() const {
576         // Mimic float to long conversion rules from JLS 5.1.3
577 
578         if (isPosInf()) {
579             return INT64_MAX;
580         } else if (isNegInf()) {
581             return INT64_MIN;
582         } else if (isNaN()) {
583             return 0;
584         } else { // finite
585             return mNumerator / mDenominator;
586         }
587     }
588 
589     /**
590      * Returns the value of the specified number as a {@code short}.
591      *
592      * <p>{@link #isInfinite Finite} rationals are converted to a {@code short} value
593      * identically to {@link #intValue}; the {@code int} result is then truncated to a
594      * {@code short} before returning the value.</p>
595      *
596      * @return the divided value of the numerator and denominator as a {@code short}.
597      */
asInt16Rational598     int16_t asInt16() const {
599         return (int16_t) asInt32();
600     }
601 
602     /**
603      * Compare this rational to the specified rational to determine their natural order.
604      *
605      * Nan is considered to be equal to itself and greater than all other
606      * Rational values. Otherwise, if the objects are not equal, then
607      * the following rules apply:
608      *
609      * Positive infinity is greater than any other finite number (or negative infinity)
610      * Negative infinity is less than any other finite number (or positive infinity)
611      * The finite number represented by this rational is checked numerically
612      * against the other finite number by converting both rationals to a common denominator multiple
613      * and comparing their numerators.
614      *
615      * @param another the rational to be compared
616      *
617      * @return a negative integer, zero, or a positive integer as this object is less than,
618      *         equal to, or greater than the specified rational.
619      */
620     // bool operator> (const Rational& another) {
compareToRational621     int compareTo(Rational another) const {
622         if (equals(another)) {
623             return 0;
624         } else if (isNaN()) { // NaN is greater than the other non-NaN value
625             return 1;
626         } else if (another.isNaN()) { // the other NaN is greater than this non-NaN value
627             return -1;
628         } else if (isPosInf() || another.isNegInf()) {
629             return 1; // positive infinity is greater than any non-NaN/non-posInf value
630         } else if (isNegInf() || another.isPosInf()) {
631             return -1; // negative infinity is less than any non-NaN/non-negInf value
632         }
633 
634         // else both this and another are finite numbers
635 
636         // make the denominators the same, then compare numerators. int64_t to avoid overflow
637         int64_t thisNumerator = ((int64_t)mNumerator) * another.mDenominator;
638         int64_t otherNumerator = ((int64_t)another.mNumerator) * mDenominator;
639 
640         // avoid underflow from subtraction by doing comparisons
641         if (thisNumerator < otherNumerator) {
642             return -1;
643         } else if (thisNumerator > otherNumerator) {
644             return 1;
645         } else {
646             // This should be covered by #equals, but have this code path just in case
647             return 0;
648         }
649     }
650 
651     bool operator > (const Rational& another) const {
652         return compareTo(another) > 0;
653     }
654 
655     bool operator >= (const Rational& another) const {
656         return compareTo(another) >= 0;
657     }
658 
659     bool operator < (const Rational& another) const {
660         return compareTo(another) < 0;
661     }
662 
663     bool operator <= (const Rational& another) const {
664         return compareTo(another) <= 0;
665     }
666 
667     bool operator == (const Rational& another) const {
668         return equals(another);
669     }
670 
671     static std::optional<Range<Rational>> ParseRange(const std::string str);
672 
673     static Range<Rational> ScaleRange(Range<Rational> range, int32_t num, int32_t den);
674 
675 private:
676     int32_t mNumerator;
677     int32_t mDenominator;
678 
isPosInfRational679     bool isPosInf() const {
680         return mDenominator == 0 && mNumerator > 0;
681     }
682 
isNegInfRational683     bool isNegInf() const {
684         return mDenominator == 0 && mNumerator < 0;
685     }
686 
equalsRational687     bool equals(Rational other) const {
688         return (mNumerator == other.mNumerator && mDenominator == other.mDenominator);
689     }
690 
691     Rational scale(int32_t num, int32_t den);
692 
693     /**
694      * Parses the specified string as a rational value.
695      * The ASCII characters {@code \}{@code u003a} (':') and
696      * {@code \}{@code u002f} ('/') are recognized as separators between
697      * the numerator and denominator.
698      *
699      * For any {@code Rational r}: {@code Rational::parseRational(r.toString()).equals(r)}.
700      * However, the method also handles rational numbers expressed in the
701      * following forms:
702      *
703      * "<i>num</i>{@code /}<i>den</i>" or
704      * "<i>num</i>{@code :}<i>den</i>" {@code => new Rational(num, den);},
705      * where <i>num</i> and <i>den</i> are string integers potentially
706      * containing a sign, such as "-10", "+7" or "5".
707      *
708      * Rational::Parse("3:+6").equals(new Rational(1, 2)) == true
709      * Rational::Parse("-3/-6").equals(new Rational(1, 2)) == true
710      * Rational::Parse("4.56") => return std::nullopt
711      *
712      * @param str the string representation of a rational value.
713      * @return the rational value wrapped by std::optional represented by str.
714      */
715     static std::optional<Rational> Parse(std::string str);
716 };
717 
718 static const Rational NaN = Rational(0, 0);
719 static const Rational POSITIVE_INFINITY = Rational(1, 0);
720 static const Rational NEGATIVE_INFINITY = Rational(-1, 0);
721 static const Rational ZERO = Rational(0, 1);
722 
723 }  // namespace android
724 
725 #endif  // CODEC_CAPABILITIES__UTILS_H_