Skip to content

Commit 973b4d2

Browse files
committed
GH-50859: [C++] Move simdjson utility definitions out of header
Now that simdjson_internal.cc exists, move non-template helper definitions into it and leave exported declarations in the header.
1 parent f011cc7 commit 973b4d2

2 files changed

Lines changed: 230 additions & 190 deletions

File tree

cpp/src/arrow/util/simdjson_internal.cc

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ namespace arrow::json {
2121

2222
namespace sj = simdjson::ondemand;
2323

24+
JsonWriter::JsonWriter() = default;
25+
2426
void JsonWriter::StartObject() {
2527
MaybeComma();
2628
builder_.start_object();
@@ -218,3 +220,207 @@ void JsonWriter::BoolField(std::string_view key, bool value) {
218220
}
219221

220222
} // namespace arrow::json
223+
224+
namespace arrow::internal {
225+
226+
const char* JsonTypeName(simdjson::dom::element_type type) {
227+
switch (type) {
228+
case simdjson::dom::element_type::ARRAY:
229+
return "array";
230+
case simdjson::dom::element_type::OBJECT:
231+
return "object";
232+
case simdjson::dom::element_type::INT64:
233+
case simdjson::dom::element_type::UINT64:
234+
case simdjson::dom::element_type::DOUBLE:
235+
return "number";
236+
case simdjson::dom::element_type::STRING:
237+
return "string";
238+
case simdjson::dom::element_type::BOOL:
239+
return "boolean";
240+
case simdjson::dom::element_type::NULL_VALUE:
241+
return "null";
242+
default:
243+
return "unknown";
244+
}
245+
}
246+
247+
Result<simdjson::dom::array> GetJsonArray(simdjson::dom::element value,
248+
std::string_view name) {
249+
if (!value.is_array()) {
250+
return Status::Invalid(name, " must be an array, got ", JsonTypeName(value.type()));
251+
}
252+
return ResolveSimdjsonResult(value.get_array(), "Failed to get JSON array");
253+
}
254+
255+
Result<int64_t> GetJsonInt(simdjson::dom::element value, std::string_view name,
256+
std::string_view expected) {
257+
if (!value.is_int64()) {
258+
return Status::Invalid(name, " must contain ", expected, ", got ",
259+
JsonTypeName(value.type()));
260+
}
261+
return ResolveSimdjsonResult(value.get_int64(), "Failed to get JSON integer");
262+
}
263+
264+
Result<simdjson::dom::object> ParseJsonObject(simdjson::dom::parser& parser,
265+
const std::string& json) {
266+
return ResolveSimdjsonResult(parser.parse(json).get_object(),
267+
"Invalid serialized JSON data");
268+
}
269+
270+
Result<std::optional<simdjson::dom::element>> GetOptionalJsonField(
271+
const simdjson::dom::object& object, std::string_view key) {
272+
auto field = object.at_key(key);
273+
if (field.error() == simdjson::NO_SUCH_FIELD) {
274+
return std::nullopt;
275+
}
276+
277+
ARROW_ASSIGN_OR_RAISE(
278+
auto value,
279+
ResolveSimdjsonResult(std::move(field), "Failed to get JSON object field"));
280+
281+
return std::optional<simdjson::dom::element>(std::move(value));
282+
}
283+
284+
Result<std::vector<int64_t>> GetJsonIntArray(simdjson::dom::element value,
285+
std::string_view name) {
286+
ARROW_ASSIGN_OR_RAISE(auto array, GetJsonArray(value, name));
287+
288+
std::vector<int64_t> result;
289+
result.reserve(array.size());
290+
291+
for (auto element : array) {
292+
ARROW_ASSIGN_OR_RAISE(auto number, GetJsonInt(element, name, "integers"));
293+
result.push_back(number);
294+
}
295+
296+
return result;
297+
}
298+
299+
Result<std::vector<std::optional<int64_t>>> GetJsonNullableIntArray(
300+
simdjson::dom::element value, std::string_view name) {
301+
ARROW_ASSIGN_OR_RAISE(auto array, GetJsonArray(value, name));
302+
303+
std::vector<std::optional<int64_t>> result;
304+
result.reserve(array.size());
305+
306+
for (auto element : array) {
307+
if (element.is_null()) {
308+
result.emplace_back(std::nullopt);
309+
} else {
310+
ARROW_ASSIGN_OR_RAISE(auto number, GetJsonInt(element, name, "integers or nulls"));
311+
result.emplace_back(number);
312+
}
313+
}
314+
315+
return result;
316+
}
317+
318+
Result<std::vector<std::string>> GetJsonStringArray(simdjson::dom::element value,
319+
std::string_view name) {
320+
ARROW_ASSIGN_OR_RAISE(auto array, GetJsonArray(value, name));
321+
322+
std::vector<std::string> result;
323+
result.reserve(array.size());
324+
325+
for (auto element : array) {
326+
if (!element.is_string()) {
327+
return Status::Invalid(name, " must contain strings, got ",
328+
JsonTypeName(element.type()));
329+
}
330+
331+
ARROW_ASSIGN_OR_RAISE(
332+
auto string,
333+
ResolveSimdjsonResult(element.get_string(), "Failed to get JSON string"));
334+
result.emplace_back(string);
335+
}
336+
337+
return result;
338+
}
339+
340+
const char* JsonTypeName(simdjson::ondemand::json_type type) {
341+
switch (type) {
342+
case simdjson::ondemand::json_type::array:
343+
return "array";
344+
case simdjson::ondemand::json_type::object:
345+
return "object";
346+
case simdjson::ondemand::json_type::number:
347+
return "number";
348+
case simdjson::ondemand::json_type::string:
349+
return "string";
350+
case simdjson::ondemand::json_type::boolean:
351+
return "boolean";
352+
case simdjson::ondemand::json_type::null:
353+
return "null";
354+
default:
355+
return "unknown";
356+
}
357+
}
358+
359+
Result<bool> IsJsonNull(simdjson::ondemand::value& value) {
360+
bool is_null;
361+
auto error_code = value.is_null().get(is_null);
362+
if (error_code != simdjson::SUCCESS) {
363+
return Status::Invalid("Error checking for JSON null: ",
364+
simdjson::error_message(error_code));
365+
}
366+
return is_null;
367+
}
368+
369+
Result<std::string> MinifyJson(std::string_view json) {
370+
std::string minified(json.size(), '\0');
371+
size_t minified_len = 0;
372+
373+
if (auto error =
374+
simdjson::minify(json.data(), json.size(), minified.data(), minified_len);
375+
error != simdjson::SUCCESS) {
376+
return Status::Invalid("Failed to minify JSON: ", simdjson::error_message(error));
377+
}
378+
379+
minified.resize(minified_len);
380+
return minified;
381+
}
382+
383+
Status ConsumeJsonValue(simdjson::ondemand::value value) {
384+
return VisitJsonValue(
385+
value, ValidateJsonObject, ValidateJsonArray,
386+
[](std::string_view) { return Status::OK(); }, [](bool) { return Status::OK(); },
387+
[]() { return Status::OK(); }, [](int64_t) { return Status::OK(); },
388+
[](uint64_t) { return Status::OK(); }, [](double) { return Status::OK(); },
389+
[](simdjson::ondemand::value) { return Status::OK(); });
390+
}
391+
392+
Status ValidateJsonObject(simdjson::ondemand::object object) {
393+
for (auto field_result : object) {
394+
ARROW_ASSIGN_OR_RAISE(
395+
auto field, ResolveSimdjsonResult(field_result, "Failed to iterate JSON object"));
396+
397+
RETURN_NOT_OK(ConsumeJsonValue(field.value()));
398+
}
399+
400+
return Status::OK();
401+
}
402+
403+
Status ValidateJsonArray(simdjson::ondemand::array array) {
404+
for (auto element_result : array) {
405+
ARROW_ASSIGN_OR_RAISE(
406+
auto value,
407+
ResolveSimdjsonResult(element_result, "Failed to iterate JSON array"));
408+
409+
RETURN_NOT_OK(ConsumeJsonValue(value));
410+
}
411+
412+
return Status::OK();
413+
}
414+
415+
Status ValidateJsonDocument(simdjson::ondemand::parser& parser,
416+
simdjson::padded_string& json) {
417+
ARROW_ASSIGN_OR_RAISE(
418+
auto document, ResolveSimdjsonResult(parser.iterate(json), "Failed to parse JSON"));
419+
420+
ARROW_ASSIGN_OR_RAISE(auto value, ResolveSimdjsonResult(document.get_value(),
421+
"Failed to get JSON value"));
422+
423+
return ConsumeJsonValue(value);
424+
}
425+
426+
} // namespace arrow::internal

0 commit comments

Comments
 (0)