|
15 | 15 | // specific language governing permissions and limitations |
16 | 16 | // under the License. |
17 | 17 |
|
| 18 | +#include "arrow/type.h" |
18 | 19 | #include "gtest/gtest.h" |
19 | 20 |
|
20 | | -#include "arrow/type.h" |
| 21 | +#include "arrow/builder.h" |
| 22 | +#include "arrow/test-util.h" |
| 23 | +#include "arrow/util/decimal.h" |
21 | 24 |
|
22 | 25 | namespace arrow { |
23 | 26 |
|
24 | | -TEST(TypesTest, TestDecimalType) { |
| 27 | +TEST(TypesTest, TestDecimal32Type) { |
25 | 28 | DecimalType t1(8, 4); |
26 | 29 |
|
27 | 30 | ASSERT_EQ(t1.type, Type::DECIMAL); |
28 | 31 | ASSERT_EQ(t1.precision, 8); |
29 | 32 | ASSERT_EQ(t1.scale, 4); |
30 | 33 |
|
31 | 34 | ASSERT_EQ(t1.ToString(), std::string("decimal(8, 4)")); |
| 35 | + |
| 36 | + // Test properties |
| 37 | + ASSERT_EQ(t1.byte_width(), 4); |
| 38 | + ASSERT_EQ(t1.bit_width(), 32); |
32 | 39 | } |
33 | 40 |
|
| 41 | +TEST(TypesTest, TestDecimal64Type) { |
| 42 | + DecimalType t1(12, 5); |
| 43 | + |
| 44 | + ASSERT_EQ(t1.type, Type::DECIMAL); |
| 45 | + ASSERT_EQ(t1.precision, 12); |
| 46 | + ASSERT_EQ(t1.scale, 5); |
| 47 | + |
| 48 | + ASSERT_EQ(t1.ToString(), std::string("decimal(12, 5)")); |
| 49 | + |
| 50 | + // Test properties |
| 51 | + ASSERT_EQ(t1.byte_width(), 8); |
| 52 | + ASSERT_EQ(t1.bit_width(), 64); |
| 53 | +} |
| 54 | + |
| 55 | +TEST(TypesTest, TestDecimal128Type) { |
| 56 | + DecimalType t1(27, 7); |
| 57 | + |
| 58 | + ASSERT_EQ(t1.type, Type::DECIMAL); |
| 59 | + ASSERT_EQ(t1.precision, 27); |
| 60 | + ASSERT_EQ(t1.scale, 7); |
| 61 | + |
| 62 | + ASSERT_EQ(t1.ToString(), std::string("decimal(27, 7)")); |
| 63 | + |
| 64 | + // Test properties |
| 65 | + ASSERT_EQ(t1.byte_width(), 16); |
| 66 | + ASSERT_EQ(t1.bit_width(), 128); |
| 67 | +} |
| 68 | + |
| 69 | +template <typename T> |
| 70 | +class DecimalTestBase { |
| 71 | + public: |
| 72 | + virtual std::vector<uint8_t> data( |
| 73 | + const std::vector<T>& input, size_t byte_width) const = 0; |
| 74 | + |
| 75 | + void test(int precision, const std::vector<T>& draw, |
| 76 | + const std::vector<uint8_t>& valid_bytes, |
| 77 | + const std::vector<uint8_t>& sign_bitmap = {}, int64_t offset = 0) const { |
| 78 | + auto type = std::make_shared<DecimalType>(precision, 4); |
| 79 | + int byte_width = type->byte_width(); |
| 80 | + auto pool = default_memory_pool(); |
| 81 | + auto builder = std::make_shared<DecimalBuilder>(pool, type); |
| 82 | + size_t null_count = 0; |
| 83 | + |
| 84 | + size_t size = draw.size(); |
| 85 | + builder->Reserve(size); |
| 86 | + |
| 87 | + for (size_t i = 0; i < size; ++i) { |
| 88 | + if (valid_bytes[i]) { |
| 89 | + builder->Append(draw[i]); |
| 90 | + } else { |
| 91 | + builder->AppendNull(); |
| 92 | + ++null_count; |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + std::shared_ptr<Buffer> expected_sign_bitmap; |
| 97 | + if (!sign_bitmap.empty()) { |
| 98 | + BitUtil::BytesToBits(sign_bitmap, &expected_sign_bitmap); |
| 99 | + } |
| 100 | + |
| 101 | + auto raw_bytes = data(draw, byte_width); |
| 102 | + auto expected_data = std::make_shared<Buffer>(raw_bytes.data(), size * byte_width); |
| 103 | + auto expected_null_bitmap = test::bytes_to_null_buffer(valid_bytes); |
| 104 | + int64_t expected_null_count = test::null_count(valid_bytes); |
| 105 | + auto expected = std::make_shared<DecimalArray>(type, size, expected_data, |
| 106 | + expected_null_bitmap, expected_null_count, offset, expected_sign_bitmap); |
| 107 | + |
| 108 | + std::shared_ptr<Array> out; |
| 109 | + ASSERT_OK(builder->Finish(&out)); |
| 110 | + ASSERT_TRUE(out->Equals(*expected)); |
| 111 | + } |
| 112 | +}; |
| 113 | + |
| 114 | +template <typename T> |
| 115 | +class DecimalTest : public DecimalTestBase<T> { |
| 116 | + public: |
| 117 | + std::vector<uint8_t> data( |
| 118 | + const std::vector<T>& input, size_t byte_width) const override { |
| 119 | + std::vector<uint8_t> result; |
| 120 | + result.reserve(input.size() * byte_width); |
| 121 | + // TODO(phillipc): There's probably a better way to do this |
| 122 | + constexpr static const size_t bytes_per_element = sizeof(T); |
| 123 | + for (size_t i = 0, j = 0; i < input.size(); ++i, j += bytes_per_element) { |
| 124 | + *reinterpret_cast<typename T::value_type*>(&result[j]) = input[i].value; |
| 125 | + } |
| 126 | + return result; |
| 127 | + } |
| 128 | +}; |
| 129 | + |
| 130 | +template <> |
| 131 | +class DecimalTest<Decimal128> : public DecimalTestBase<Decimal128> { |
| 132 | + public: |
| 133 | + std::vector<uint8_t> data( |
| 134 | + const std::vector<Decimal128>& input, size_t byte_width) const override { |
| 135 | + std::vector<uint8_t> result; |
| 136 | + result.reserve(input.size() * byte_width); |
| 137 | + constexpr static const size_t bytes_per_element = 16; |
| 138 | + for (size_t i = 0; i < input.size(); ++i) { |
| 139 | + uint8_t stack_bytes[bytes_per_element] = {0}; |
| 140 | + uint8_t* bytes = stack_bytes; |
| 141 | + bool is_negative; |
| 142 | + ToBytes(input[i], &bytes, &is_negative); |
| 143 | + |
| 144 | + for (size_t i = 0; i < bytes_per_element; ++i) { |
| 145 | + result.push_back(bytes[i]); |
| 146 | + } |
| 147 | + } |
| 148 | + return result; |
| 149 | + } |
| 150 | +}; |
| 151 | + |
| 152 | +class Decimal32BuilderTest : public ::testing::TestWithParam<int>, |
| 153 | + public DecimalTest<Decimal32> {}; |
| 154 | + |
| 155 | +class Decimal64BuilderTest : public ::testing::TestWithParam<int>, |
| 156 | + public DecimalTest<Decimal64> {}; |
| 157 | + |
| 158 | +class Decimal128BuilderTest : public ::testing::TestWithParam<int>, |
| 159 | + public DecimalTest<Decimal128> {}; |
| 160 | + |
| 161 | +TEST_P(Decimal32BuilderTest, NoNulls) { |
| 162 | + int precision = GetParam(); |
| 163 | + std::vector<Decimal32> draw = { |
| 164 | + Decimal32(1), Decimal32(2), Decimal32(2389), Decimal32(4), Decimal32(-12348)}; |
| 165 | + std::vector<uint8_t> valid_bytes = {true, true, true, true, true}; |
| 166 | + this->test(precision, draw, valid_bytes); |
| 167 | +} |
| 168 | + |
| 169 | +TEST_P(Decimal64BuilderTest, NoNulls) { |
| 170 | + int precision = GetParam(); |
| 171 | + std::vector<Decimal64> draw = { |
| 172 | + Decimal64(1), Decimal64(2), Decimal64(2389), Decimal64(4), Decimal64(-12348)}; |
| 173 | + std::vector<uint8_t> valid_bytes = {true, true, true, true, true}; |
| 174 | + this->test(precision, draw, valid_bytes); |
| 175 | +} |
| 176 | + |
| 177 | +TEST_P(Decimal128BuilderTest, NoNulls) { |
| 178 | + int precision = GetParam(); |
| 179 | + std::vector<Decimal128> draw = { |
| 180 | + Decimal128(1), Decimal128(-2), Decimal128(2389), Decimal128(4), Decimal128(-12348)}; |
| 181 | + std::vector<uint8_t> valid_bytes = {true, true, true, true, true}; |
| 182 | + std::vector<uint8_t> sign_bitmap = {false, true, false, false, true}; |
| 183 | + this->test(precision, draw, valid_bytes, sign_bitmap); |
| 184 | +} |
| 185 | + |
| 186 | +TEST_P(Decimal32BuilderTest, WithNulls) { |
| 187 | + int precision = GetParam(); |
| 188 | + std::vector<Decimal32> draw = { |
| 189 | + Decimal32(1), Decimal32(2), Decimal32(-1), Decimal32(4), Decimal32(-1)}; |
| 190 | + std::vector<uint8_t> valid_bytes = {true, true, false, true, false}; |
| 191 | + this->test(precision, draw, valid_bytes); |
| 192 | +} |
| 193 | + |
| 194 | +TEST_P(Decimal64BuilderTest, WithNulls) { |
| 195 | + int precision = GetParam(); |
| 196 | + std::vector<Decimal64> draw = { |
| 197 | + Decimal64(-1), Decimal64(2), Decimal64(-1), Decimal64(4), Decimal64(-1)}; |
| 198 | + std::vector<uint8_t> valid_bytes = {true, true, false, true, false}; |
| 199 | + this->test(precision, draw, valid_bytes); |
| 200 | +} |
| 201 | + |
| 202 | +TEST_P(Decimal128BuilderTest, WithNulls) { |
| 203 | + int precision = GetParam(); |
| 204 | + std::vector<Decimal128> draw = {Decimal128(1), Decimal128(2), Decimal128(-1), |
| 205 | + Decimal128(4), Decimal128(-1), Decimal128(1), Decimal128(2), |
| 206 | + Decimal128("230342903942.234234"), Decimal128("-23049302932.235234")}; |
| 207 | + std::vector<uint8_t> valid_bytes = { |
| 208 | + true, true, false, true, false, true, true, true, true}; |
| 209 | + std::vector<uint8_t> sign_bitmap = { |
| 210 | + false, false, false, false, false, false, false, false, true}; |
| 211 | + this->test(precision, draw, valid_bytes, sign_bitmap); |
| 212 | +} |
| 213 | + |
| 214 | +INSTANTIATE_TEST_CASE_P(Decimal32BuilderTest, Decimal32BuilderTest, |
| 215 | + ::testing::Range( |
| 216 | + DecimalPrecision<int32_t>::minimum, DecimalPrecision<int32_t>::maximum)); |
| 217 | +INSTANTIATE_TEST_CASE_P(Decimal64BuilderTest, Decimal64BuilderTest, |
| 218 | + ::testing::Range( |
| 219 | + DecimalPrecision<int64_t>::minimum, DecimalPrecision<int64_t>::maximum)); |
| 220 | +INSTANTIATE_TEST_CASE_P(Decimal128BuilderTest, Decimal128BuilderTest, |
| 221 | + ::testing::Range( |
| 222 | + DecimalPrecision<int128_t>::minimum, DecimalPrecision<int128_t>::maximum)); |
| 223 | + |
34 | 224 | } // namespace arrow |
0 commit comments