// Export visible API
#include "cyson.h"
#include <library/cpp/yson_pull/yson.h>
#include <library/cpp/yson_pull/detail/reader.h>
#include <library/cpp/yson_pull/detail/writer.h>
#include <library/cpp/yson_pull/detail/input/stream.h>
#include <library/cpp/yson_pull/detail/input/stdio_file.h>
#include <library/cpp/yson_pull/detail/output/buffered.h>
#include <library/cpp/yson_pull/detail/output/stream.h>
#include <library/cpp/yson_pull/detail/output/stdio_file.h>
#include <util/generic/ptr.h>
#include <util/generic/strbuf.h>
#include <util/generic/string.h>
namespace {
template <typename T>
void safe_assign_string(TString& dest, T&& value) noexcept {
try {
dest = std::forward<T>(value);
} catch (...) {
// Suppress exception
}
}
} // anonymous namespace
struct yson_reader {
NYsonPull::NDetail::gen_reader_impl<false> impl;
TString error_message;
yson_reader(NYsonPull::NInput::IStream* stream, NYsonPull::EStreamType mode)
: impl(*stream, mode)
{
error_message.reserve(64);
}
yson_event_type safe_get_next_event() noexcept {
try {
auto& event = impl.next_event();
return static_cast<yson_event_type>(event.Type());
} catch (...) {
safe_assign_string(error_message, CurrentExceptionMessage());
return YSON_EVENT_ERROR;
}
}
};
struct yson_writer {
THolder<NYsonPull::IConsumer> consumer;
TString error_message;
yson_writer(THolder<NYsonPull::IConsumer> consumer_)
: consumer{std::move(consumer_)} {
error_message.reserve(64);
}
template <typename T>
yson_writer_result safe_write(T&& func) noexcept {
try {
func(*consumer);
return YSON_WRITER_RESULT_OK;
} catch (const NYsonPull::NException::TBadOutput& err) {
safe_assign_string(error_message, err.what());
return YSON_WRITER_RESULT_BAD_STREAM;
} catch (...) {
safe_assign_string(error_message, CurrentExceptionMessage());
return YSON_WRITER_RESULT_ERROR;
}
}
};
namespace {
class callback_error: public std::exception {
public:
const char* what() const noexcept override {
return "User callback returned error result code";
}
};
class c_yson_input_stream: public NYsonPull::NInput::IStream {
void* ctx_;
yson_input_stream_func callback_;
public:
c_yson_input_stream(void* ctx, yson_input_stream_func callback)
: ctx_{ctx}
, callback_{callback} {
}
protected:
result do_fill_buffer() override {
const char* ptr;
size_t length;
switch (callback_(ctx_, &ptr, &length)) {
case YSON_INPUT_STREAM_RESULT_OK:
buffer().reset(
reinterpret_cast<const ui8*>(ptr),
reinterpret_cast<const ui8*>(ptr) + length);
return result::have_more_data;
case YSON_INPUT_STREAM_RESULT_EOF:
return result::at_end;
default:
case YSON_INPUT_STREAM_RESULT_ERROR:
throw callback_error();
}
}
};
class c_yson_output_stream: public NYsonPull::NDetail::NOutput::TBuffered<c_yson_output_stream> {
using base_type = NYsonPull::NDetail::NOutput::TBuffered<c_yson_output_stream>;
void* ctx_;
yson_output_stream_func callback_;
public:
c_yson_output_stream(void* ctx, yson_output_stream_func callback, size_t buffer_size)
: base_type(buffer_size)
, ctx_{ctx}
, callback_{callback} {
}
void write(TStringBuf data) {
switch (callback_(ctx_, data.data(), data.size())) {
case YSON_OUTPUT_STREAM_RESULT_OK:
return;
default:
case YSON_OUTPUT_STREAM_RESULT_ERROR:
throw callback_error();
}
}
};
// Type marshalling
const yson_string* to_yson_string(const NYsonPull::TScalar& value) {
assert(value.Type() == NYsonPull::EScalarType::String);
auto* result = &value.AsUnsafeValue().AsString;
return reinterpret_cast<const yson_string*>(result);
}
yson_input_stream* to_yson_input_stream(NYsonPull::NInput::IStream* ptr) {
return reinterpret_cast<yson_input_stream*>(ptr);
}
NYsonPull::NInput::IStream* from_yson_input_stream(yson_input_stream* ptr) {
return reinterpret_cast<NYsonPull::NInput::IStream*>(ptr);
}
yson_output_stream* to_yson_output_stream(NYsonPull::NOutput::IStream* ptr) {
return reinterpret_cast<yson_output_stream*>(ptr);
}
NYsonPull::NOutput::IStream* from_yson_output_stream(yson_output_stream* ptr) {
return reinterpret_cast<NYsonPull::NOutput::IStream*>(ptr);
}
// Exception-handling new/delete wrappers
template <typename T, typename... Args>
T* safe_new(Args&&... args) noexcept {
try {
return new T(std::forward<Args>(args)...);
} catch (...) {
return nullptr;
}
}
template <typename T>
void safe_delete(T* ptr) noexcept {
assert(ptr != nullptr);
try {
delete ptr;
} catch (...) {
// Suppress destructor exceptions
}
}
template <typename T, typename... Args>
yson_writer* safe_new_writer(yson_output_stream* stream, Args&&... args) noexcept {
try {
auto impl = MakeHolder<T>(
*from_yson_output_stream(stream),
std::forward<Args>(args)...);
return new yson_writer(std::move(impl));
} catch (...) {
return nullptr;
}
}
} // anonymous namespace
extern "C" {
// Input stream
yson_input_stream* yson_input_stream_from_string(const char* ptr, size_t length) {
auto buf = TStringBuf{ptr, length};
auto* result = safe_new<NYsonPull::NDetail::NInput::TOwned<TMemoryInput>>(buf);
return to_yson_input_stream(result);
}
yson_input_stream* yson_input_stream_from_file(FILE* file, size_t buffer_size) {
auto* result = safe_new<NYsonPull::NDetail::NInput::TStdioFile>(file, buffer_size);
return to_yson_input_stream(result);
}
yson_input_stream* yson_input_stream_from_fd(int fd, size_t buffer_size) {
auto* result = safe_new<NYsonPull::NDetail::NInput::TFHandle>(fd, buffer_size);
return to_yson_input_stream(result);
}
yson_input_stream* yson_input_stream_new(void* ctx, yson_input_stream_func callback) {
auto* result = safe_new<c_yson_input_stream>(ctx, callback);
return to_yson_input_stream(result);
}
void yson_input_stream_delete(yson_input_stream* stream) {
assert(stream != nullptr);
safe_delete(from_yson_input_stream(stream));
}
// Reader
yson_reader* yson_reader_new(yson_input_stream* stream, yson_stream_type mode) {
assert(stream != nullptr);
return safe_new<yson_reader>(
from_yson_input_stream(stream),
static_cast<NYsonPull::EStreamType>(mode));
}
void yson_reader_delete(yson_reader* reader) {
assert(reader != nullptr);
safe_delete(reader);
}
yson_event_type yson_reader_get_next_event(yson_reader* reader) {
assert(reader != nullptr);
return reader->safe_get_next_event();
}
const char* yson_reader_get_error_message(yson_reader* reader) {
assert(reader != nullptr);
return reader->error_message.c_str();
}
yson_scalar_type yson_reader_get_scalar_type(yson_reader* reader) {
assert(reader != nullptr);
auto& event = reader->impl.last_event();
return static_cast<yson_scalar_type>(event.AsScalar().Type());
}
int yson_reader_get_boolean(yson_reader* reader) {
assert(reader != nullptr);
auto& event = reader->impl.last_event();
return static_cast<int>(event.AsScalar().AsBoolean());
}
i64 yson_reader_get_int64(yson_reader* reader) {
assert(reader != nullptr);
auto& event = reader->impl.last_event();
return event.AsScalar().AsInt64();
}
ui64 yson_reader_get_uint64(yson_reader* reader) {
assert(reader != nullptr);
auto& event = reader->impl.last_event();
return event.AsScalar().AsUInt64();
}
double yson_reader_get_float64(yson_reader* reader) {
assert(reader != nullptr);
auto& event = reader->impl.last_event();
return event.AsScalar().AsFloat64();
}
const yson_string* yson_reader_get_string(yson_reader* reader) {
assert(reader != nullptr);
return to_yson_string(reader->impl.last_event().AsScalar());
}
// Output stream
yson_output_stream* yson_output_stream_from_file(FILE* file, size_t buffer_size) {
auto* result = safe_new<NYsonPull::NDetail::NOutput::TStdioFile>(file, buffer_size);
return to_yson_output_stream(result);
}
yson_output_stream* yson_output_stream_from_fd(int fd, size_t buffer_size) {
auto* result = safe_new<NYsonPull::NDetail::NOutput::TFHandle>(fd, buffer_size);
return to_yson_output_stream(result);
}
yson_output_stream* yson_output_stream_new(void* ctx, yson_output_stream_func callback, size_t buffer_size) {
auto* result = safe_new<c_yson_output_stream>(ctx, callback, buffer_size);
return to_yson_output_stream(result);
}
void yson_output_stream_delete(yson_output_stream* stream) {
assert(stream != nullptr);
safe_delete(from_yson_output_stream(stream));
}
// Writer
yson_writer* yson_writer_new_binary(yson_output_stream* stream, yson_stream_type mode) {
assert(stream != nullptr);
return safe_new_writer<NYsonPull::NDetail::TBinaryWriterImpl>(
stream,
static_cast<NYsonPull::EStreamType>(mode));
}
yson_writer* yson_writer_new_text(yson_output_stream* stream, yson_stream_type mode) {
assert(stream != nullptr);
return safe_new_writer<NYsonPull::NDetail::TTextWriterImpl>(
stream,
static_cast<NYsonPull::EStreamType>(mode));
}
yson_writer* yson_writer_new_pretty_text(yson_output_stream* stream, yson_stream_type mode, size_t indent) {
assert(stream != nullptr);
return safe_new_writer<NYsonPull::NDetail::TPrettyWriterImpl>(
stream,
static_cast<NYsonPull::EStreamType>(mode),
indent);
}
void yson_writer_delete(yson_writer* writer) {
assert(writer != nullptr);
safe_delete(writer);
}
const char* yson_writer_get_error_message(yson_writer* writer) {
assert(writer != nullptr);
return writer->error_message.c_str();
}
yson_writer_result yson_writer_write_begin_stream(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnBeginStream();
});
}
yson_writer_result yson_writer_write_end_stream(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnEndStream();
});
}
yson_writer_result yson_writer_write_begin_list(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnBeginList();
});
}
yson_writer_result yson_writer_write_end_list(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnEndList();
});
}
yson_writer_result yson_writer_write_begin_map(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnBeginMap();
});
}
yson_writer_result yson_writer_write_end_map(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnEndMap();
});
}
yson_writer_result yson_writer_write_begin_attributes(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnBeginAttributes();
});
}
yson_writer_result yson_writer_write_end_attributes(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnEndAttributes();
});
}
yson_writer_result yson_writer_write_entity(yson_writer* writer) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnEntity();
});
}
yson_writer_result yson_writer_write_key(yson_writer* writer, const char* ptr, size_t length) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnKey({ptr, length});
});
}
yson_writer_result yson_writer_write_string(yson_writer* writer, const char* ptr, size_t length) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnScalarString({ptr, length});
});
}
yson_writer_result yson_writer_write_int64(yson_writer* writer, i64 value) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnScalarInt64(value);
});
}
yson_writer_result yson_writer_write_uint64(yson_writer* writer, ui64 value) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnScalarUInt64(value);
});
}
yson_writer_result yson_writer_write_boolean(yson_writer* writer, int value) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnScalarBoolean(static_cast<bool>(value));
});
}
yson_writer_result yson_writer_write_float64(yson_writer* writer, double value) {
assert(writer != nullptr);
return writer->safe_write([=](NYsonPull::IConsumer& consumer) {
consumer.OnScalarFloat64(value);
});
}
} // extern "C"