summaryrefslogtreecommitdiffstats
path: root/src/arrow/cpp/src/arrow/util/int_util_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/arrow/cpp/src/arrow/util/int_util_internal.h')
-rw-r--r--src/arrow/cpp/src/arrow/util/int_util_internal.h153
1 files changed, 153 insertions, 0 deletions
diff --git a/src/arrow/cpp/src/arrow/util/int_util_internal.h b/src/arrow/cpp/src/arrow/util/int_util_internal.h
new file mode 100644
index 000000000..413670662
--- /dev/null
+++ b/src/arrow/cpp/src/arrow/util/int_util_internal.h
@@ -0,0 +1,153 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.
+
+#pragma once
+
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+#include "arrow/status.h"
+#include "arrow/util/macros.h"
+#include "arrow/util/visibility.h"
+
+// "safe-math.h" includes <intsafe.h> from the Windows headers.
+#include "arrow/util/windows_compatibility.h"
+#include "arrow/vendored/portable-snippets/safe-math.h"
+// clang-format off (avoid include reordering)
+#include "arrow/util/windows_fixup.h"
+// clang-format on
+
+namespace arrow {
+namespace internal {
+
+// Define functions AddWithOverflow, SubtractWithOverflow, MultiplyWithOverflow
+// with the signature `bool(T u, T v, T* out)` where T is an integer type.
+// On overflow, these functions return true. Otherwise, false is returned
+// and `out` is updated with the result of the operation.
+
+#define OP_WITH_OVERFLOW(_func_name, _psnip_op, _type, _psnip_type) \
+ static inline bool _func_name(_type u, _type v, _type* out) { \
+ return !psnip_safe_##_psnip_type##_##_psnip_op(out, u, v); \
+ }
+
+#define OPS_WITH_OVERFLOW(_func_name, _psnip_op) \
+ OP_WITH_OVERFLOW(_func_name, _psnip_op, int8_t, int8) \
+ OP_WITH_OVERFLOW(_func_name, _psnip_op, int16_t, int16) \
+ OP_WITH_OVERFLOW(_func_name, _psnip_op, int32_t, int32) \
+ OP_WITH_OVERFLOW(_func_name, _psnip_op, int64_t, int64) \
+ OP_WITH_OVERFLOW(_func_name, _psnip_op, uint8_t, uint8) \
+ OP_WITH_OVERFLOW(_func_name, _psnip_op, uint16_t, uint16) \
+ OP_WITH_OVERFLOW(_func_name, _psnip_op, uint32_t, uint32) \
+ OP_WITH_OVERFLOW(_func_name, _psnip_op, uint64_t, uint64)
+
+OPS_WITH_OVERFLOW(AddWithOverflow, add)
+OPS_WITH_OVERFLOW(SubtractWithOverflow, sub)
+OPS_WITH_OVERFLOW(MultiplyWithOverflow, mul)
+OPS_WITH_OVERFLOW(DivideWithOverflow, div)
+
+#undef OP_WITH_OVERFLOW
+#undef OPS_WITH_OVERFLOW
+
+// Define function NegateWithOverflow with the signature `bool(T u, T* out)`
+// where T is a signed integer type. On overflow, these functions return true.
+// Otherwise, false is returned and `out` is updated with the result of the
+// operation.
+
+#define UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, _type, _psnip_type) \
+ static inline bool _func_name(_type u, _type* out) { \
+ return !psnip_safe_##_psnip_type##_##_psnip_op(out, u); \
+ }
+
+#define SIGNED_UNARY_OPS_WITH_OVERFLOW(_func_name, _psnip_op) \
+ UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, int8_t, int8) \
+ UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, int16_t, int16) \
+ UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, int32_t, int32) \
+ UNARY_OP_WITH_OVERFLOW(_func_name, _psnip_op, int64_t, int64)
+
+SIGNED_UNARY_OPS_WITH_OVERFLOW(NegateWithOverflow, neg)
+
+#undef UNARY_OP_WITH_OVERFLOW
+#undef SIGNED_UNARY_OPS_WITH_OVERFLOW
+
+/// Signed addition with well-defined behaviour on overflow (as unsigned)
+template <typename SignedInt>
+SignedInt SafeSignedAdd(SignedInt u, SignedInt v) {
+ using UnsignedInt = typename std::make_unsigned<SignedInt>::type;
+ return static_cast<SignedInt>(static_cast<UnsignedInt>(u) +
+ static_cast<UnsignedInt>(v));
+}
+
+/// Signed subtraction with well-defined behaviour on overflow (as unsigned)
+template <typename SignedInt>
+SignedInt SafeSignedSubtract(SignedInt u, SignedInt v) {
+ using UnsignedInt = typename std::make_unsigned<SignedInt>::type;
+ return static_cast<SignedInt>(static_cast<UnsignedInt>(u) -
+ static_cast<UnsignedInt>(v));
+}
+
+/// Signed negation with well-defined behaviour on overflow (as unsigned)
+template <typename SignedInt>
+SignedInt SafeSignedNegate(SignedInt u) {
+ using UnsignedInt = typename std::make_unsigned<SignedInt>::type;
+ return static_cast<SignedInt>(~static_cast<UnsignedInt>(u) + 1);
+}
+
+/// Signed left shift with well-defined behaviour on negative numbers or overflow
+template <typename SignedInt, typename Shift>
+SignedInt SafeLeftShift(SignedInt u, Shift shift) {
+ using UnsignedInt = typename std::make_unsigned<SignedInt>::type;
+ return static_cast<SignedInt>(static_cast<UnsignedInt>(u) << shift);
+}
+
+/// Upcast an integer to the largest possible width (currently 64 bits)
+
+template <typename Integer>
+typename std::enable_if<
+ std::is_integral<Integer>::value && std::is_signed<Integer>::value, int64_t>::type
+UpcastInt(Integer v) {
+ return v;
+}
+
+template <typename Integer>
+typename std::enable_if<
+ std::is_integral<Integer>::value && std::is_unsigned<Integer>::value, uint64_t>::type
+UpcastInt(Integer v) {
+ return v;
+}
+
+static inline Status CheckSliceParams(int64_t object_length, int64_t slice_offset,
+ int64_t slice_length, const char* object_name) {
+ if (ARROW_PREDICT_FALSE(slice_offset < 0)) {
+ return Status::Invalid("Negative ", object_name, " slice offset");
+ }
+ if (ARROW_PREDICT_FALSE(slice_length < 0)) {
+ return Status::Invalid("Negative ", object_name, " slice length");
+ }
+ int64_t offset_plus_length;
+ if (ARROW_PREDICT_FALSE(
+ internal::AddWithOverflow(slice_offset, slice_length, &offset_plus_length))) {
+ return Status::Invalid(object_name, " slice would overflow");
+ }
+ if (ARROW_PREDICT_FALSE(slice_offset + slice_length > object_length)) {
+ return Status::Invalid(object_name, " slice would exceed ", object_name, " length");
+ }
+ return Status::OK();
+}
+
+} // namespace internal
+} // namespace arrow