-
Notifications
You must be signed in to change notification settings - Fork 4.2k
GH-50779: [C++][Parquet] Replace remaining RapidJSON usage with simdjson #50781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,52 +17,93 @@ | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| #include "parquet/geospatial/util_json_internal.h" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #include <simdjson.h> | ||||||||||||||||||||||
| #include <string> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #include "arrow/extension_type.h" | ||||||||||||||||||||||
| #include "arrow/json/rapidjson_defs.h" // IWYU pragma: keep | ||||||||||||||||||||||
| #include "arrow/json/json_writer_internal.h" | ||||||||||||||||||||||
| #include "arrow/result.h" | ||||||||||||||||||||||
| #include "arrow/util/simdjson_internal.h" | ||||||||||||||||||||||
| #include "arrow/util/string.h" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #include <rapidjson/document.h> | ||||||||||||||||||||||
| #include <rapidjson/writer.h> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #include "parquet/exception.h" | ||||||||||||||||||||||
| #include "parquet/types.h" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| namespace parquet { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| namespace { | ||||||||||||||||||||||
| ::arrow::Result<std::string> GeospatialGeoArrowCrsToParquetCrs( | ||||||||||||||||||||||
| const ::arrow::rapidjson::Document& document) { | ||||||||||||||||||||||
| namespace rj = ::arrow::rapidjson; | ||||||||||||||||||||||
| simdjson::ondemand::object object) { | ||||||||||||||||||||||
| auto crs_field = object["crs"]; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (!document.HasMember("crs") || document["crs"].IsNull()) { | ||||||||||||||||||||||
| if (crs_field.error() == simdjson::NO_SUCH_FIELD) { | ||||||||||||||||||||||
| // Parquet GEOMETRY/GEOGRAPHY do not have a concept of a null/missing | ||||||||||||||||||||||
| // CRS, but an omitted one is more likely to have meant "lon/lat" than | ||||||||||||||||||||||
| // a truly unspecified one (i.e., Engineering CRS with arbitrary XY units) | ||||||||||||||||||||||
| return ""; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const auto& json_crs = document["crs"]; | ||||||||||||||||||||||
| if (json_crs.IsString() && (json_crs == "EPSG:4326" || json_crs == "OGC:CRS84")) { | ||||||||||||||||||||||
| // crs can be left empty because these cases both correspond to | ||||||||||||||||||||||
| // longitude/latitude in WGS84 according to the Parquet specification | ||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(auto json_crs, ::arrow::internal::GetSimdjsonResult( | ||||||||||||||||||||||
| crs_field, "Failed to get 'crs' field: ")); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(bool is_null, ::arrow::internal::IsJsonNull(json_crs)); | ||||||||||||||||||||||
| if (is_null) { | ||||||||||||||||||||||
| return ""; | ||||||||||||||||||||||
| } else if (json_crs.IsObject()) { | ||||||||||||||||||||||
| // Attempt to detect common PROJJSON representations of longitude/latitude and return | ||||||||||||||||||||||
| // an empty crs to maximize compatibility with readers that do not implement CRS | ||||||||||||||||||||||
| // support. PROJJSON stores this in the "id" member like: | ||||||||||||||||||||||
| // {..., "id": {"authority": "...", "code": "..."}} | ||||||||||||||||||||||
| if (json_crs.HasMember("id")) { | ||||||||||||||||||||||
| const auto& identifier = json_crs["id"]; | ||||||||||||||||||||||
| if (identifier.HasMember("authority") && identifier.HasMember("code")) { | ||||||||||||||||||||||
| if (identifier["authority"] == "OGC" && identifier["code"] == "CRS84") { | ||||||||||||||||||||||
| return ""; | ||||||||||||||||||||||
| } else if (identifier["authority"] == "EPSG" && identifier["code"] == "4326") { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (auto string = ::arrow::internal::GetJsonAs<std::string_view>(json_crs); | ||||||||||||||||||||||
| string.ok()) { | ||||||||||||||||||||||
| if (*string == "EPSG:4326" || *string == "OGC:CRS84") { | ||||||||||||||||||||||
| // crs can be left empty because these cases both correspond to | ||||||||||||||||||||||
| // longitude/latitude in WGS84 according to the Parquet specification | ||||||||||||||||||||||
| return ""; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // If we could not detect a longitude/latitude CRS, just write the string to the | ||||||||||||||||||||||
| // LogicalType crs (being sure to unescape a JSON string into a regular string) | ||||||||||||||||||||||
| return std::string(*string); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE( | ||||||||||||||||||||||
| auto crs_object, | ||||||||||||||||||||||
| ::arrow::internal::GetJsonAs<simdjson::ondemand::object>(json_crs)); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Attempt to detect common PROJJSON representations of longitude/latitude and return | ||||||||||||||||||||||
| // an empty crs to maximize compatibility with readers that do not implement CRS | ||||||||||||||||||||||
| // support. PROJJSON stores this in the "id" member like: | ||||||||||||||||||||||
| // {..., "id": {"authority": "...", "code": "..."}} | ||||||||||||||||||||||
| auto id_field = crs_object["id"]; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (id_field.error() != simdjson::NO_SUCH_FIELD) { | ||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(auto identifier, ::arrow::internal::GetSimdjsonResult( | ||||||||||||||||||||||
| id_field, "Failed to get 'id' field: ")); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| auto authority_field = identifier["authority"]; | ||||||||||||||||||||||
| auto code_field = identifier["code"]; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (authority_field.error() != simdjson::NO_SUCH_FIELD && | ||||||||||||||||||||||
| code_field.error() != simdjson::NO_SUCH_FIELD) { | ||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(auto authority, | ||||||||||||||||||||||
| ::arrow::internal::GetSimdjsonResult( | ||||||||||||||||||||||
| authority_field, "Failed to get 'authority' field: ")); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(auto code, ::arrow::internal::GetSimdjsonResult( | ||||||||||||||||||||||
| code_field, "Failed to get 'code' field: ")); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(auto authority_string, | ||||||||||||||||||||||
| ::arrow::internal::GetJsonAs<std::string_view>(authority)); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| auto code_string = ::arrow::internal::GetJsonAs<std::string_view>(code); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (code_string.ok()) { | ||||||||||||||||||||||
| if ((authority_string == "OGC" && *code_string == "CRS84") || | ||||||||||||||||||||||
| (authority_string == "EPSG" && *code_string == "4326")) { | ||||||||||||||||||||||
|
Comment on lines
+96
to
+100
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about using
Suggested change
|
||||||||||||||||||||||
| return ""; | ||||||||||||||||||||||
| } else if (identifier["authority"] == "EPSG" && identifier["code"].IsInt() && | ||||||||||||||||||||||
| identifier["code"].GetInt() == 4326) { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } else if (authority_string == "EPSG") { | ||||||||||||||||||||||
| auto code_int = ::arrow::internal::GetJsonAs<int64_t>(code); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (code_int.ok() && *code_int == 4326) { | ||||||||||||||||||||||
| return ""; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
@@ -71,14 +112,26 @@ ::arrow::Result<std::string> GeospatialGeoArrowCrsToParquetCrs( | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| // If we could not detect a longitude/latitude CRS, just write the string to the | ||||||||||||||||||||||
| // LogicalType crs (being sure to unescape a JSON string into a regular string) | ||||||||||||||||||||||
| if (json_crs.IsString()) { | ||||||||||||||||||||||
| return json_crs.GetString(); | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| rj::StringBuffer buffer; | ||||||||||||||||||||||
| rj::Writer<rj::StringBuffer> writer(buffer); | ||||||||||||||||||||||
| json_crs.Accept(writer); | ||||||||||||||||||||||
| return buffer.GetString(); | ||||||||||||||||||||||
| RETURN_NOT_OK(::arrow::internal::GetSimdjsonResult(crs_object.reset(), | ||||||||||||||||||||||
| "Failed to reset 'crs' object: ") | ||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that all diff --git a/cpp/src/arrow/util/simdjson_internal.h b/cpp/src/arrow/util/simdjson_internal.h
index 8ffb741da4..187dd3aaa5 100644
--- a/cpp/src/arrow/util/simdjson_internal.h
+++ b/cpp/src/arrow/util/simdjson_internal.h
@@ -85,7 +85,7 @@ template <typename T>
Result<T> GetSimdjsonResult(simdjson::simdjson_result<T> result, std::string_view error) {
T value;
if (auto error_code = std::move(result).get(value); error_code != simdjson::SUCCESS) {
- return Status::Invalid(error, simdjson::error_message(error_code));
+ return Status::Invalid(error, ": ", simdjson::error_message(error_code));
}
return value;
}(BTW, |
||||||||||||||||||||||
| .status()); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(auto raw_crs, | ||||||||||||||||||||||
| ::arrow::internal::GetSimdjsonResult( | ||||||||||||||||||||||
| crs_object.raw_json(), "Failed to get raw 'crs' JSON: ")); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| std::string minified(raw_crs.size(), '\0'); | ||||||||||||||||||||||
| size_t minified_len = 0; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (auto error = | ||||||||||||||||||||||
| simdjson::minify(raw_crs.data(), raw_crs.size(), minified.data(), minified_len); | ||||||||||||||||||||||
| error != simdjson::SUCCESS) { | ||||||||||||||||||||||
| return ::arrow::Status::Invalid("Failed to minify CRS JSON: ", | ||||||||||||||||||||||
| simdjson::error_message(error)); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| minified.resize(minified_len); | ||||||||||||||||||||||
| return minified; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Utility for ensuring that a Parquet CRS is valid JSON when written to | ||||||||||||||||||||||
|
|
@@ -125,18 +178,18 @@ ::arrow::Result<std::string> MakeGeoArrowCrsMetadata( | |||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| std::string EscapeCrsAsJsonIfRequired(std::string_view crs) { | ||||||||||||||||||||||
| namespace rj = ::arrow::rapidjson; | ||||||||||||||||||||||
| rj::Document document; | ||||||||||||||||||||||
| if (document.Parse(crs.data(), crs.length()).HasParseError()) { | ||||||||||||||||||||||
| rj::StringBuffer buffer; | ||||||||||||||||||||||
| rj::Writer<rj::StringBuffer> writer(buffer); | ||||||||||||||||||||||
| rj::Value v; | ||||||||||||||||||||||
| v.SetString(crs.data(), static_cast<int32_t>(crs.size())); | ||||||||||||||||||||||
| v.Accept(writer); | ||||||||||||||||||||||
| return std::string(buffer.GetString()); | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| return std::string(crs); | ||||||||||||||||||||||
| simdjson::ondemand::parser parser; | ||||||||||||||||||||||
| simdjson::padded_string json(crs); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (parser.iterate(json).error() != simdjson::SUCCESS) { | ||||||||||||||||||||||
| ::arrow::json::JsonWriter writer; | ||||||||||||||||||||||
| writer.String(crs); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| auto escaped = writer.GetString().ValueUnsafe(); | ||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we change the return type to |
||||||||||||||||||||||
| return std::string(escaped); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return std::string(crs); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| } // namespace | ||||||||||||||||||||||
|
|
@@ -149,24 +202,42 @@ ::arrow::Result<std::shared_ptr<const LogicalType>> LogicalTypeFromGeoArrowMetad | |||||||||||||||||||||
| return LogicalType::Geometry(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| namespace rj = ::arrow::rapidjson; | ||||||||||||||||||||||
| rj::Document document; | ||||||||||||||||||||||
| if (document.Parse(serialized_data.data(), serialized_data.length()).HasParseError()) { | ||||||||||||||||||||||
| simdjson::ondemand::parser parser; | ||||||||||||||||||||||
| simdjson::padded_string json(serialized_data); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| simdjson::ondemand::document document; | ||||||||||||||||||||||
| if (auto error = parser.iterate(json).get(document); error != simdjson::SUCCESS) { | ||||||||||||||||||||||
| return ::arrow::Status::Invalid("Invalid serialized JSON data: ", serialized_data); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(std::string crs, GeospatialGeoArrowCrsToParquetCrs(document)); | ||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE( | ||||||||||||||||||||||
| auto object, ::arrow::internal::GetSimdjsonResult(document.get_object(), | ||||||||||||||||||||||
| "Failed to get JSON object: ")); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (document.HasMember("edges") && document["edges"] == "planar") { | ||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(std::string crs, GeospatialGeoArrowCrsToParquetCrs(object)); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| auto edges_field = object["edges"]; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (edges_field.error() == simdjson::NO_SUCH_FIELD) { | ||||||||||||||||||||||
| return LogicalType::Geometry(crs); | ||||||||||||||||||||||
| } else if (document.HasMember("edges") && document["edges"] == "spherical") { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(auto edges, ::arrow::internal::GetSimdjsonResult( | ||||||||||||||||||||||
| edges_field, "Failed to get 'edges' field: ")); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ARROW_ASSIGN_OR_RAISE(auto edges_string, | ||||||||||||||||||||||
| ::arrow::internal::GetJsonAs<std::string_view>(edges)); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (edges_string == "planar") { | ||||||||||||||||||||||
| return LogicalType::Geometry(crs); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (edges_string == "spherical") { | ||||||||||||||||||||||
| return LogicalType::Geography(crs, | ||||||||||||||||||||||
| LogicalType::EdgeInterpolationAlgorithm::SPHERICAL); | ||||||||||||||||||||||
| } else if (document.HasMember("edges")) { | ||||||||||||||||||||||
| return ::arrow::Status::Invalid("Unsupported GeoArrow edge type: ", serialized_data); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return LogicalType::Geometry(crs); | ||||||||||||||||||||||
| return ::arrow::Status::Invalid("Unsupported GeoArrow edge type: ", serialized_data); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ::arrow::Result<std::shared_ptr<::arrow::DataType>> GeoArrowTypeFromLogicalType( | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,16 +24,13 @@ | |
| #include <sstream> | ||
| #include <string> | ||
|
|
||
| #include "arrow/json/rapidjson_defs.h" // IWYU pragma: keep | ||
| #include "arrow/json/json_writer_internal.h" | ||
| #include "arrow/util/checked_cast.h" | ||
| #include "arrow/util/compression.h" | ||
| #include "arrow/util/decimal.h" | ||
| #include "arrow/util/float16.h" | ||
| #include "arrow/util/logging_internal.h" | ||
|
|
||
| #include <rapidjson/document.h> | ||
| #include <rapidjson/writer.h> | ||
|
|
||
| #include "parquet/exception.h" | ||
| #include "parquet/thrift_internal.h" | ||
| #include "parquet/types.h" | ||
|
|
@@ -1785,13 +1782,9 @@ namespace { | |
| void WriteCrsKeyAndValue(const std::string_view crs, std::ostream& json) { | ||
| // There is no restriction on the crs value here, and it may contain quotes | ||
| // or backslashes that would result in invalid JSON if unescaped. | ||
| namespace rj = ::arrow::rapidjson; | ||
| rj::StringBuffer buffer; | ||
| rj::Writer<rj::StringBuffer> writer(buffer); | ||
| rj::Value v; | ||
| v.SetString(crs.data(), static_cast<int32_t>(crs.size())); | ||
| v.Accept(writer); | ||
| json << R"(, "crs": )" << buffer.GetString(); | ||
| ::arrow::json::JsonWriter writer; | ||
| writer.String(crs); | ||
| json << R"(, "crs": )" << writer.GetString().ValueUnsafe(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use |
||
| } | ||
| } // namespace | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we avoid
if (...; ...)for readability?