| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2014 Travis Geiselbrecht |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_RANGE_CHECK_H_ |
| #define ZIRCON_KERNEL_INCLUDE_KERNEL_RANGE_CHECK_H_ |
| |
| #include <fbl/algorithm.h> |
| #include <ktl/algorithm.h> |
| #include <ktl/limits.h> |
| |
| // Is the range [offset, offset + len] fully inside the range [0, max] ? |
| template <typename O, typename L> |
| static inline bool InRange(O offset, L len, O max) { |
| static_assert(ktl::numeric_limits<O>::is_signed == false, "InRange requires unsigned type O"); |
| static_assert(ktl::numeric_limits<L>::is_signed == false, "InRange requires unsigned type L"); |
| |
| // trim offset/len to the range |
| if (offset + len < offset) { |
| return false; // offset + len wrapped |
| } |
| |
| // we started off the end of the range |
| if (offset > max) { |
| return false; |
| } |
| |
| // does the end exceed the range? |
| if (offset + len > max) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Is the range [offset, offset + len] fully inside the range [min, max]? |
| template <typename O, typename L> |
| static inline bool InRange(O offset, L len, O min, O max) { |
| // Underflow is not tested as it gets caught in the next call. |
| return InRange(offset - min, len, max - min); |
| } |
| |
| // utility function to trim offset + len to trim_to_len |
| // returns new length in *len_out |
| // returns false if out of range |
| // may return length 0 if it precisely trims |
| // NOTE: only use unsigned lengths |
| template <typename O, typename L> |
| static inline bool TrimRange(O offset, L len, O trim_to_len, L* len_out) { |
| static_assert(ktl::numeric_limits<O>::is_signed == false, "TrimRange requires unsigned type O"); |
| static_assert(ktl::numeric_limits<L>::is_signed == false, "TrimRange requires unsigned type L"); |
| |
| // start off returning the initial value |
| *len_out = len; |
| |
| // trim offset/len to the range |
| if (offset + len < offset) { |
| return false; // offset + len wrapped |
| } |
| |
| // we started off the end of the range |
| if (offset > trim_to_len) { |
| return false; |
| } |
| |
| // trim to the range |
| if (offset + len > trim_to_len) { |
| *len_out = static_cast<L>(trim_to_len - offset); |
| } |
| |
| return true; |
| } |
| |
| // given two offset/length pairs, determine if they overlap at all |
| template <typename O, typename L> |
| static inline bool Intersects(O offset1, L len1, O offset2, L len2) { |
| static_assert(ktl::numeric_limits<O>::is_signed == false, "Intersects requires unsigned type O"); |
| static_assert(ktl::numeric_limits<L>::is_signed == false, "Intersects requires unsigned type L"); |
| |
| // Can't overlap a zero-length region. |
| if (len1 == 0 || len2 == 0) { |
| return false; |
| } |
| |
| if (offset1 <= offset2) { |
| // doesn't intersect, 1 is completely below 2 |
| if (offset1 + len1 <= offset2) { |
| return false; |
| } |
| } else if (offset1 >= offset2 + len2) { |
| // 1 is completely above 2 |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // given two offset/length pairs, determine if they overlap and compute the intersection |
| // returns results in *offset_out and *len_out |
| template <typename O, typename L> |
| static inline bool GetIntersect(O offset1, L len1, O offset2, L len2, O* offset_out, L* len_out) { |
| static_assert(ktl::numeric_limits<O>::is_signed == false, |
| "GetIntersect requires unsigned type O"); |
| static_assert(ktl::numeric_limits<L>::is_signed == false, |
| "GetIntersect requires unsigned type L"); |
| |
| // see if they intersect at all |
| if (!Intersects(offset1, len1, offset2, len2)) { |
| return false; |
| } |
| |
| // they intersect in some way, 2 cases |
| if (offset1 < offset2) { |
| // range 1 starts lower then range 2, but must extend into it or across it |
| *offset_out = offset2; |
| *len_out = ktl::min((offset1 + len1) - offset2, len2); |
| } else { // (offset2 <= offset1) |
| // range 2 starts lower then range 1, but must extend into it or across it |
| // also range 1 and two may start at the same address |
| *offset_out = offset1; |
| *len_out = ktl::min((offset2 + len2) - offset1, len1); |
| } |
| |
| return true; |
| } |
| |
| #endif // ZIRCON_KERNEL_INCLUDE_KERNEL_RANGE_CHECK_H_ |