Skip to content

Commit ea3d079

Browse files
chkuang-ga-maurice
authored andcommitted
Fix hash calculation when the Variant is a vector
Specifically fix it when the Variant has more than 10 elements. Variant can be a vector or a map which can be converted into a vector. All the hash value for unit test was verified to be accepted by rt db servers. PiperOrigin-RevId: 274064076
1 parent a5f4a38 commit ea3d079

File tree

1 file changed

+63
-36
lines changed

1 file changed

+63
-36
lines changed

database/src/desktop/util_desktop.cc

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -452,43 +452,50 @@ bool ParseInteger(const char* str, int32_t* output) {
452452
return is_int;
453453
}
454454

455-
// Convert one level of map to vector if applicable.
456-
// Convert map to vector if:
455+
// A Variant map can be converted into a Variant vector if:
457456
// 1. map is not empty and
458457
// 2. All the key are numeric and
459458
// 3. If less or equal to half of the keys in the array is missing.
459+
// Return whether the map can be converted into a vector, and output
460+
// max_index_out as the highest numeric key found in the map.
461+
bool CanConvertVariantMapToVector(const Variant& variant,
462+
int64_t* max_index_out) {
463+
if (!variant.is_map() || variant.map().empty()) return false;
464+
465+
int64_t max_index = -1;
466+
for (auto& it_child : variant.map()) {
467+
// Check if the key is numeric
468+
int32_t parse_value = 0;
469+
bool is_number = ParseInteger(it_child.first.string_value(), &parse_value);
470+
if (!is_number || parse_value < 0) {
471+
// If any one of the key is not numeric, there is no need to verify
472+
// other keys
473+
return false;
474+
}
475+
max_index = max_index < parse_value ? parse_value : max_index;
476+
}
477+
478+
if (max_index_out) *max_index_out = max_index;
479+
return max_index < (2 * variant.map().size());
480+
}
481+
482+
// Convert one level of map to vector if applicable.
460483
// This function assume no priority information remains in the variant.
461484
void ConvertMapToVector(Variant* variant) {
462485
assert(variant);
463486

464-
if (variant->is_map() && !variant->map().empty()) {
465-
int64_t max_index = -1;
466-
for (auto& it_child : variant->map()) {
467-
// Check if the key is numeric
468-
int32_t parse_value = 0;
469-
bool is_number =
470-
ParseInteger(it_child.first.string_value(), &parse_value);
471-
if (!is_number || parse_value < 0) {
472-
// If any one of the key is not numeric, there is no need to verify
473-
// other keys
474-
return;
475-
}
476-
max_index = max_index < parse_value ? parse_value : max_index;
477-
}
478-
479-
if (max_index < (2 * variant->map().size())) {
480-
Variant array_result(
481-
std::vector<Variant>(max_index + 1, Variant::Null()));
482-
for (int i = 0; i <= max_index; ++i) {
483-
std::stringstream ss;
484-
ss << i;
485-
auto it_child = variant->map().find(ss.str());
486-
if (it_child != variant->map().end()) {
487-
array_result.vector()[i] = it_child->second;
488-
}
487+
int64_t max_index = -1;
488+
if (CanConvertVariantMapToVector(*variant, &max_index)) {
489+
Variant array_result(std::vector<Variant>(max_index + 1, Variant::Null()));
490+
for (int i = 0; i <= max_index; ++i) {
491+
std::stringstream ss;
492+
ss << i;
493+
auto it_child = variant->map().find(ss.str());
494+
if (it_child != variant->map().end()) {
495+
array_result.vector()[i] = it_child->second;
489496
}
490-
*variant = array_result;
491497
}
498+
*variant = array_result;
492499
}
493500
}
494501

@@ -828,13 +835,26 @@ UtilLeafType GetLeafType(Variant::Type type) {
828835
// Store the pointers to the key and the value Variant in a map for sorting
829836
typedef std::pair<const Variant*, const Variant*> NodeSortingData;
830837

838+
// Comparer for std::sort to sort nodes by key as numeric value
839+
// Return true if right node is greater than left node
840+
bool KeyAsNumberSortingFunction(const NodeSortingData& left,
841+
const NodeSortingData& right) {
842+
return left.first->AsInt64().int64_value() <
843+
right.first->AsInt64().int64_value();
844+
}
845+
831846
// Private function to serialize all child nodes
832847
void ProcessChildNodes(std::stringstream* ss,
833-
std::vector<NodeSortingData>* nodes, bool saw_priority) {
834-
// If any node has priority, sort the vector
848+
std::vector<NodeSortingData>* nodes, bool saw_priority,
849+
bool can_convert_vector) {
850+
// If any node has priority, sort using priority.
835851
if (saw_priority) {
836852
QueryParams params;
853+
assert(params.order_by == QueryParams::kOrderByPriority);
837854
std::sort(nodes->begin(), nodes->end(), QueryParamsLesser(&params));
855+
} else if (can_convert_vector) {
856+
// If the nodes can be converted to a vector, sort using key as number.
857+
std::sort(nodes->begin(), nodes->end(), KeyAsNumberSortingFunction);
838858
}
839859

840860
// Serialize each child with its key and its hashed value
@@ -854,23 +874,30 @@ void AppendHashRepAsContainer(std::stringstream* ss, const Variant& data) {
854874
std::vector<NodeSortingData> nodes;
855875

856876
if (data.is_vector()) {
857-
// Convert vector into map before process it. The map use index string as
858-
// the key.
859-
Variant map = Variant::EmptyMap();
877+
// Store index as string to be used for hash calculating, since
878+
// NodeSortingData stores the pointer to the Variant.
879+
// This is to avoid making copies of Variant from data.
880+
std::vector<Variant> index_variants;
881+
index_variants.reserve(data.vector().size());
882+
bool saw_priority = false;
860883
for (int i = 0; i < data.vector().size(); ++i) {
861884
std::stringstream index_stream;
862885
index_stream << i;
863-
map.map()[index_stream.str()] = data.vector()[i];
886+
index_variants.push_back(index_stream.str());
887+
nodes.push_back(NodeSortingData(&index_variants[i], &data.vector()[i]));
888+
saw_priority =
889+
saw_priority || !GetVariantPriority(data.vector()[i]).is_null();
864890
}
865-
AppendHashRepAsContainer(ss, map);
891+
ProcessChildNodes(ss, &nodes, saw_priority, true);
866892
} else if (data.is_map()) {
867893
bool saw_priority = false;
868894
for (auto& it_child : data.map()) {
869895
nodes.push_back(NodeSortingData(&it_child.first, &it_child.second));
870896
saw_priority =
871897
saw_priority || !GetVariantPriority(it_child.second).is_null();
872898
}
873-
ProcessChildNodes(ss, &nodes, saw_priority);
899+
bool can_convert_vector = CanConvertVariantMapToVector(data, nullptr);
900+
ProcessChildNodes(ss, &nodes, saw_priority, can_convert_vector);
874901
}
875902
}
876903

0 commit comments

Comments
 (0)