| // 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. | |
| #include <string> | |
| #include "runtime/string-value.inline.h" | |
| #include "testutil/gtest-util.h" | |
| #include "util/cpu-info.h" | |
| #include "common/names.h" | |
| namespace impala { | |
| StringValue FromStdString(const string& str) { | |
| char* ptr = const_cast<char*>(str.c_str()); | |
| int len = str.size(); | |
| return StringValue(ptr, len); | |
| } | |
| void TestCompareImpl(StringValue* svs, int NUM_STRINGS) { | |
| for (int i = 0; i < NUM_STRINGS; ++i) { | |
| for (int j = 0; j < NUM_STRINGS; ++j) { | |
| if (i == j) { | |
| // Same string | |
| EXPECT_TRUE(svs[i].Eq(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_FALSE(svs[i].Ne(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_FALSE(svs[i].Lt(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_FALSE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Le(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Ge(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Compare(svs[j]) == 0) << "i=" << i << " j=" << j; | |
| } else if (i < j) { | |
| // svs[i] < svs[j] | |
| EXPECT_FALSE(svs[i].Eq(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Ne(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Lt(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_FALSE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Le(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_FALSE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Compare(svs[j]) < 0) << "i=" << i << " j=" << j; | |
| } else { | |
| // svs[i] > svs[j] | |
| EXPECT_FALSE(svs[i].Eq(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Ne(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_FALSE(svs[i].Lt(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_FALSE(svs[i].Le(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j; | |
| EXPECT_TRUE(svs[i].Compare(svs[j]) > 0) << "i=" << i << " j=" << j; | |
| } | |
| } | |
| } | |
| } | |
| class StringValueTest : public ::testing::Test { | |
| protected: | |
| void SmallifySV(StringValue* sv) { sv->Smallify(); } | |
| void SmallifySVExpect(StringValue* sv, bool expect_to_succeed) { | |
| if (expect_to_succeed) { | |
| EXPECT_TRUE(sv->Smallify()); | |
| } else { | |
| EXPECT_FALSE(sv->Smallify()); | |
| } | |
| } | |
| void TestLargestSmallerString(StringValue& sv, const string& expected) { | |
| EXPECT_EQ(sv.LargestSmallerString(), expected); | |
| sv.Smallify(); | |
| EXPECT_EQ(sv.LargestSmallerString(), expected); | |
| } | |
| void TestLeastLargerString(StringValue& sv, const string& expected) { | |
| EXPECT_EQ(sv.LeastLargerString(), expected); | |
| sv.Smallify(); | |
| EXPECT_EQ(sv.LeastLargerString(), expected); | |
| } | |
| }; | |
| TEST_F(StringValueTest, TestCompare) { | |
| string empty_str = ""; | |
| string str1_str("\0", 1); | |
| string str2_str("\0xy", 3); | |
| string str3_str = "abc"; | |
| string str4_str("abc\0def", 7); | |
| string str5_str = "abcdef"; | |
| string str6_str = "xyz"; | |
| string str7_str("xyz\0", 4); | |
| // Include a few long strings so we test the SSE path | |
| string str8_str("yyyyyyyyyyyyyyyy\0yyyyyyyyyyyyyyyyyy", 35); | |
| string str9_str("yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", 34); | |
| const int NUM_STRINGS = 10; | |
| // Must be in lexical order | |
| StringValue svs[NUM_STRINGS]; | |
| svs[0] = FromStdString(empty_str); | |
| svs[1] = FromStdString(str1_str); | |
| svs[2] = FromStdString(str2_str); | |
| svs[3] = FromStdString(str3_str); | |
| svs[4] = FromStdString(str4_str); | |
| svs[5] = FromStdString(str5_str); | |
| svs[6] = FromStdString(str6_str); | |
| svs[7] = FromStdString(str7_str); | |
| svs[8] = FromStdString(str8_str); | |
| svs[9] = FromStdString(str9_str); | |
| TestCompareImpl(svs, NUM_STRINGS); | |
| for (int i = 0; i < NUM_STRINGS; ++i) { | |
| SmallifySV(&svs[i]); | |
| } | |
| TestCompareImpl(svs, NUM_STRINGS); | |
| } | |
| void TestUnpaddedCharLength(StringValue* chars) { | |
| EXPECT_EQ(StringValue::UnpaddedCharLength(chars[0].Ptr(), 2), 2); | |
| EXPECT_EQ(StringValue::UnpaddedCharLength(chars[1].Ptr(), 4), 2); | |
| EXPECT_EQ(StringValue::UnpaddedCharLength(chars[2].Ptr(), 5), 3); | |
| EXPECT_EQ(StringValue::UnpaddedCharLength(chars[3].Ptr(), 5), 5); | |
| EXPECT_EQ(StringValue::UnpaddedCharLength(chars[4].Ptr(), 1), 0); | |
| EXPECT_EQ(StringValue::UnpaddedCharLength(chars[5].Ptr(), 0), 0); | |
| EXPECT_EQ(StringValue::UnpaddedCharLength(chars[6].Ptr(), 20), 17); | |
| } | |
| TEST_F(StringValueTest, TestCharFunctions) { | |
| string char0_str("hi", 2); | |
| string char1_str("hi ", 4); | |
| string char2_str(" hi ", 5); | |
| string char3_str("12345", 5); | |
| string char4_str(" ", 1); | |
| string char5_str("", 0); | |
| string char6_str(" 0123456789ABCD "); | |
| const int NUM_CHARS = 7; | |
| StringValue chars[NUM_CHARS]; | |
| chars[0] = FromStdString(char0_str); | |
| chars[1] = FromStdString(char1_str); | |
| chars[2] = FromStdString(char2_str); | |
| chars[3] = FromStdString(char3_str); | |
| chars[4] = FromStdString(char4_str); | |
| chars[5] = FromStdString(char5_str); | |
| chars[6] = FromStdString(char6_str); | |
| TestUnpaddedCharLength(chars); | |
| for (int i = 0; i < NUM_CHARS; ++i) { | |
| SmallifySV(&chars[i]); | |
| } | |
| TestUnpaddedCharLength(chars); | |
| StringValue::PadWithSpaces(chars[3].Ptr(), 5, 4); | |
| EXPECT_EQ(chars[3].Ptr()[4], ' '); | |
| EXPECT_EQ(chars[3].Ptr()[3], '4'); | |
| StringValue::PadWithSpaces(chars[6].Ptr(), 20, 10); | |
| EXPECT_EQ(chars[6].Ptr()[10], ' '); | |
| EXPECT_EQ(chars[6].Ptr()[9], '6'); | |
| } | |
| void TestConvertToUInt64Impl(StringValue* svs) { | |
| EXPECT_EQ(svs[0].ToUInt64(), 0); | |
| EXPECT_EQ(svs[1].ToUInt64(), 0x100000000000000); | |
| EXPECT_EQ(svs[2].ToUInt64(), 0x102000000000000); | |
| EXPECT_EQ(svs[3].ToUInt64(), 0x102030000000000); | |
| // extra character(s) does not change the result | |
| EXPECT_EQ(svs[4].ToUInt64(), 0x102030405060707); | |
| EXPECT_EQ(svs[5].ToUInt64(), 0x102030405060707); | |
| EXPECT_EQ(svs[6].ToUInt64(), 0x102030405060707); | |
| } | |
| TEST_F(StringValueTest, TestConvertToUInt64) { | |
| // Test converting StringValues to uint64_t which utilizes up to first 8 bytes. | |
| const int NUM_STRINGS = 7; | |
| string strings[NUM_STRINGS]; | |
| strings[0] = ""; | |
| strings[1] = "\1"; | |
| strings[2] = "\1\2"; | |
| strings[3] = "\1\2\3"; | |
| strings[4] = "\1\2\3\4\5\6\7\7"; | |
| strings[5] = "\1\2\3\4\5\6\7\7\7"; | |
| strings[6] = "\1\2\3\4\5\6\7\7\7\7\7\7\7\7\7"; | |
| // Must be in lexical order | |
| StringValue svs[NUM_STRINGS]; | |
| for (int i = 0; i < NUM_STRINGS; ++i) { | |
| svs[i] = FromStdString(strings[i]); | |
| } | |
| TestConvertToUInt64Impl(svs); | |
| for (int i = 0; i < NUM_STRINGS; ++i) { | |
| SmallifySV(&svs[i]); | |
| } | |
| TestConvertToUInt64Impl(svs); | |
| } | |
| // Test finding the largest smaller strings. | |
| TEST_F(StringValueTest, TestLargestSmallerString) { | |
| string oneKbNullStr(1024, 0x00); | |
| string a1023NullStr(1023, 0x00); | |
| EXPECT_EQ(StringValue(oneKbNullStr).LargestSmallerString(), a1023NullStr); | |
| StringValue asv(const_cast<char*>("\x12\xef"), 2); | |
| TestLargestSmallerString(asv, "\x12\xee"); | |
| StringValue bsv(const_cast<char*>("\x12\x00"), 2); | |
| TestLargestSmallerString(bsv, "\x12"); | |
| // "0x00" is the smallest non-empty string. | |
| string oneNullStr("\00", 1); | |
| StringValue oneNullStrSv(oneNullStr); | |
| TestLargestSmallerString(oneNullStrSv, ""); | |
| // The empty string is the absolute smallest string. | |
| StringValue emptySv(const_cast<char*>("")); | |
| TestLargestSmallerString(emptySv, ""); | |
| } | |
| // Test finding the least larger strings. | |
| TEST_F(StringValueTest, TestLeastLargerString) { | |
| string nullStr(const_cast<char*>("\x00"), 1); | |
| StringValue nullStrSv(nullStr); | |
| TestLeastLargerString(nullStrSv, string("\x01", 1)); | |
| string a10230xFFStr(1023, 0xff); | |
| string oneKbStr(1023, 0xff); | |
| oneKbStr.append(1, 0x00); | |
| StringValue a10230xFFStrSv(a10230xFFStr); | |
| TestLeastLargerString(a10230xFFStrSv, oneKbStr); | |
| StringValue asv(const_cast<char*>("\x12\xef"), 2); | |
| TestLeastLargerString(asv, "\x12\xf0"); | |
| StringValue bsv(const_cast<char*>("\x12\xff"), 2); | |
| TestLeastLargerString(bsv, "\x13"); | |
| string smallLimit(11, 0xff); | |
| StringValue smallLimitSv(smallLimit); | |
| string smallLimitLeastLarger = smallLimit + '\0'; | |
| TestLeastLargerString(smallLimitSv, smallLimitLeastLarger); | |
| StringValue emptySv(const_cast<char*>("")); | |
| TestLeastLargerString(emptySv, string("\00", 1)); | |
| } | |
| TEST_F(StringValueTest, TestConstructors) { | |
| // Test that all strings are non-small initially. | |
| StringValue def_ctor; | |
| EXPECT_FALSE(def_ctor.IsSmall()); | |
| StringValue copy_ctor(def_ctor); | |
| EXPECT_FALSE(copy_ctor.IsSmall()); | |
| // Modify 'copy_ctor' to make Clang Tidy happy. | |
| SmallifySVExpect(©_ctor, true); | |
| StringValue char_ctor(const_cast<char*>("small")); | |
| EXPECT_FALSE(char_ctor.IsSmall()); | |
| StringValue char_n(const_cast<char*>("small"), 5); | |
| EXPECT_FALSE(char_n.IsSmall()); | |
| string small_str("small"); | |
| StringValue string_ctor(small_str); | |
| EXPECT_FALSE(string_ctor.IsSmall()); | |
| } | |
| TEST_F(StringValueTest, TestSmallify) { | |
| StringValue nullstr; | |
| StringValue empty(const_cast<char*>(""), 0); | |
| StringValue one_char(const_cast<char*>("a"), 1); | |
| StringValue limit(const_cast<char*>("0123456789A"), 11); | |
| StringValue over_the_limit(const_cast<char*>("0123456789AB"), 12); | |
| StringValue nullstr_clone(nullstr); | |
| StringValue empty_clone(empty); | |
| StringValue one_char_clone(one_char); | |
| StringValue limit_clone(limit); | |
| StringValue over_the_limit_clone(over_the_limit); | |
| SmallifySVExpect(&nullstr, true); | |
| SmallifySVExpect(&empty, true); | |
| SmallifySVExpect(&one_char, true); | |
| SmallifySVExpect(&limit, true); | |
| SmallifySVExpect(&over_the_limit, false); | |
| EXPECT_EQ(nullstr, nullstr_clone); | |
| EXPECT_NE(nullstr.Ptr(), nullstr_clone.Ptr()); | |
| EXPECT_EQ(empty, empty_clone); | |
| EXPECT_NE(empty.Ptr(), empty_clone.Ptr()); | |
| EXPECT_EQ(one_char, one_char_clone); | |
| EXPECT_NE(one_char.Ptr(), one_char_clone.Ptr()); | |
| EXPECT_EQ(limit, limit_clone); | |
| EXPECT_NE(limit.Ptr(), limit_clone.Ptr()); | |
| EXPECT_EQ(over_the_limit, over_the_limit_clone); | |
| EXPECT_EQ(over_the_limit.Ptr(), over_the_limit_clone.Ptr()); | |
| } | |
| } | |