#include "mapper.h"
#include "dynamic.h"
#include <upb/pb/encoder.h>
#include <upb/pb/decoder.h>
#include <cassert>
using namespace gpd;
using namespace gpd::transform;
using namespace std;
using namespace UMS_NS;
using namespace upb;
using namespace upb::pb;
using namespace upb::json;
#ifdef MULTIPLICITY
# define THX_DECLARE_AND_GET tTHX my_perl = cxt->my_perl
#else
# define THX_DECLARE_AND_GET
#endif
#define HAS_FULL_NOMG (PERL_VERSION >= 14)
#if PERL_VERSION < 18
# define SvREFCNT_dec_NN SvREFCNT_dec
#endif
namespace {
void unref_on_scope_leave(void *ref) {
((Refcounted *) ref)->unref();
}
void refcounted_mortalize(pTHX_ const Refcounted *ref) {
SAVEDESTRUCTOR(unref_on_scope_leave, ref);
}
void delete_upb_env(upb::Environment *env) {
delete env;
}
upb::Environment *make_localized_environment(pTHX_ upb::Status *report_errors_to) {
upb::Environment *env = new upb::Environment();
env->ReportErrorsTo(report_errors_to);
SAVEDESTRUCTOR(delete_upb_env, env);
return env;
}
}
Mapper::DecoderHandlers::DecoderHandlers(pTHX_ const Mapper *mapper) :
target_ref(NULL),
pending_transforms(aTHX),
decoder_transform(NULL),
decoder_transform_fieldtable(false) {
SET_THX_MEMBER;
mappers.push_back(mapper);
}
Mapper::DecoderHandlers::~DecoderHandlers() {
if (decoder_transform)
decoder_transform->destroy(aTHX);
}
void Mapper::DecoderHandlers::push_mapper(const Mapper *mapper) {
mappers.push_back(mapper);
track_seen_fields = mapper->get_track_seen_fields();
}
void Mapper::DecoderHandlers::pop_mapper() {
mappers.pop_back();
track_seen_fields = mappers.back()->get_track_seen_fields();
}
void Mapper::DecoderHandlers::prepare(HV *target) {
track_seen_fields = mappers[0]->get_track_seen_fields();
mappers.resize(1);
if (track_seen_fields) {
seen_fields.resize(1);
seen_fields.back().clear();
seen_fields.back().resize(mappers.back()->fields.size());
}
if (int oneof_count = mappers.back()->oneof_count) {
seen_oneof.resize(1);
seen_oneof.back().clear();
seen_oneof.back().resize(oneof_count, -1);
}
items.resize(1);
items[0] = (SV *) target;
map_keys.clear();
fieldtable_entries.clear();
target_ref = newRV_noinc(items[0]);
error.clear();
string = NULL;
SAVEDESTRUCTOR(&DecoderTransformQueue::static_clear, &pending_transforms);
SAVEDESTRUCTOR(&DecoderHandlers::static_clear, this);
pending_transforms.clear();
if (decoder_transform) {
if (decoder_transform_fieldtable) {
add_transform_fieldtable(target_ref, decoder_transform, NULL);
} else {
pending_transforms.add_transform(target_ref, decoder_transform, NULL);
}
}
if (mappers[0]->get_decode_blessed())
sv_bless(target_ref, mappers[0]->stash);
}
void Mapper::DecoderHandlers::finish() {
if (decoder_transform_fieldtable) {
finish_add_transform_fieldtable();
}
// this should be a no-op that only resizes the items vector
DecoderHandlers::static_clear(this);
// this can croak()
apply_transforms();
}
void Mapper::DecoderHandlers::static_clear(DecoderHandlers *cxt) {
THX_DECLARE_AND_GET;
for (vector<SV *>::iterator it = cxt->items.begin(), en = cxt->items.end(); it != en; ++it) {
SV *item = *it;
// This can happen for the last entry (map value has not been decoded)
if (!item)
continue;
// AVs/HVs are linked into the top-level target when they are created,
// while map key/value need to be cleaned up manually
if (SvTYPE(item) != SVt_PVHV && SvTYPE(item) != SVt_PVAV) {
SvREFCNT_dec(item);
}
}
cxt->items.clear();
for (vector<MapKey>::iterator it = cxt->map_keys.begin(), en = cxt->map_keys.end(); it != en; ++it) {
SvREFCNT_dec(it->key_sv);
}
cxt->map_keys.clear();
for (vector<DecoderFieldtable::Entry>::iterator it = cxt->fieldtable_entries.begin(), en = cxt->fieldtable_entries.end(); it != en; ++it) {
SvREFCNT_dec(it->value);
}
cxt->fieldtable_entries.clear();
}
SV *Mapper::DecoderHandlers::get_and_mortalize_target() {
return sv_2mortal(target_ref);
}
namespace {
#if PERL_VERSION < 18
#define READ_XDIGIT(s) ((0xf & (isDIGIT(*(s)) \
? (*(s)++) \
: (*(s)++ + 9))))
#endif
#if PERL_VERSION < 12
bool GPD_is_invariant_string(const U8 *s, STRLEN len) {
const U8 *end = s + (len ? len : strlen((const char *) s));
for (; s < end; ++s) {
if (!UTF8_IS_INVARIANT(*s))
return false;
}
return true;
}
#define is_invariant_string(s, len) GPD_is_invariant_string(s, len)
#endif
inline void set_perl_bool(pTHX_ SV *target, bool value) {
if (value)
sv_setiv(target, 1);
else
sv_setpvn(target, "", 0);
}
inline void set_numeric_bool(pTHX_ SV *target, bool value) {
sv_setiv(target, value ? 1 : 0);
}
}
string Mapper::Field::full_name() const {
if (field_def->is_extension())
return field_def->full_name();
else
return string(field_def->containing_type()->full_name()) +
'.' +
field_def->name();
}
FieldDef::Type Mapper::Field::map_value_type() const {
return mapper->fields[mapper->map_value_index].field_def->type();
}
const unordered_set<int32_t> &Mapper::Field::map_enum_values() const {
return mapper->fields[mapper->map_value_index].enum_values;
}
bool Mapper::DecoderHandlers::apply_defaults_and_check() {
const Mapper *mapper = mappers.back();
bool decode_explicit_defaults = mapper->decode_explicit_defaults;
bool check_required_fields = mapper->check_required_fields;
if (!decode_explicit_defaults && !check_required_fields)
return true;
const vector<bool> &seen = seen_fields.back();
const vector<Mapper::Field> &fields = mapper->fields;
for (int i = 0, n = fields.size(); i < n; ++i) {
const Mapper::Field &field = fields[i];
bool field_seen = seen[i];
if (!field_seen && decode_explicit_defaults && field.has_default) {
SV *target = get_target(&i);
// this is the case where we merged multiple message instances,
// so defaults have already been applied
if (SvOK(target))
continue;
mapper->apply_default(field, target);
} else if (!field_seen && check_required_fields && field.field_def->label() == UPB_LABEL_REQUIRED) {
error = "Missing required field " + field.full_name();
return false;
}
}
return true;
}
bool Mapper::DecoderHandlers::on_end_message(DecoderHandlers *cxt, upb::Status *status) {
if (!cxt->track_seen_fields)
return true;
else if (!status || status->ok()) {
return cxt->apply_defaults_and_check();
} else
return false;
}
Mapper::DecoderHandlers *Mapper::DecoderHandlers::on_start_string(DecoderHandlers *cxt, const int *field_index, size_t size_hint) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
cxt->string = cxt->get_target(field_index);
sv_grow(cxt->string, size_hint + 1);
SvPOK_on(cxt->string);
SvCUR_set(cxt->string, 0);
return cxt;
}
size_t Mapper::DecoderHandlers::on_append_string(DecoderHandlers *cxt, const int *field_index, const char *buf, size_t len) {
THX_DECLARE_AND_GET;
STRLEN cur = SvCUR(cxt->string);
char * pv = SvGROW(cxt->string, cur + len); // necessary for JSON, where the size hint is 0
memcpy(pv + cur, buf, len);
pv[cur + len] = 0;
SvCUR_set(cxt->string, cur + len);
return len;
}
bool Mapper::DecoderHandlers::on_end_string(DecoderHandlers *cxt, const int *field_index) {
const Mapper *mapper = cxt->mappers.back();
if (mapper->fields[*field_index].field_def->type() == UPB_TYPE_STRING)
SvUTF8_on(cxt->string);
cxt->string = NULL;
return true;
}
void Mapper::DecoderHandlers::on_string(DecoderHandlers *cxt, const int *field_index, const char *buf, size_t len, bool is_utf8) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
SV *string = cxt->get_target(field_index);
char *pv = sv_grow(string, len + 1);
SvPOK_on(string);
memcpy(pv, buf, len);
pv[len] = 0;
SvCUR_set(string, len);
if (is_utf8)
SvUTF8_on(string);
}
size_t Mapper::DecoderHandlers::on_string_key(DecoderHandlers *cxt, const int *field_index, const char *buf, size_t len) {
cxt->map_keys.back().set_buffer(buf, len);
return len;
}
Mapper::DecoderHandlers *Mapper::DecoderHandlers::on_start_sequence(DecoderHandlers *cxt, const int *field_index) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
SV *target = cxt->get_hash_item_target(field_index);
AV *av = NULL;
if (!SvROK(target)) {
av = newAV();
SvUPGRADE(target, SVt_RV);
SvROK_on(target);
SvRV_set(target, (SV *) av);
} else
av = (AV *) SvRV(target);
cxt->items.push_back((SV *) av);
return cxt;
}
bool Mapper::DecoderHandlers::on_end_sequence(DecoderHandlers *cxt, const int *field_index) {
cxt->items.pop_back();
return true;
}
Mapper::DecoderHandlers *Mapper::DecoderHandlers::on_start_map(DecoderHandlers *cxt, const int *field_index) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
const Mapper *mapper = cxt->mappers.back();
SV *target = cxt->get_hash_item_target(field_index);
HV *hv = NULL;
if (!SvROK(target)) {
hv = newHV();
SvUPGRADE(target, SVt_RV);
SvROK_on(target);
SvRV_set(target, (SV *) hv);
} else
hv = (HV *) SvRV(target);
cxt->push_mapper(mapper->fields[*field_index].mapper);
cxt->items.push_back((SV *) hv);
cxt->items.push_back(NULL);
cxt->map_keys.push_back(MapKey(newSV(0)));
return cxt;
}
bool Mapper::DecoderHandlers::on_end_map(DecoderHandlers *cxt, const int *field_index) {
THX_DECLARE_AND_GET;
cxt->pop_mapper();
cxt->items.pop_back();
cxt->items.pop_back();
SvREFCNT_dec(cxt->map_keys.back().key_sv);
cxt->map_keys.pop_back();
return true;
}
Mapper::DecoderHandlers *Mapper::DecoderHandlers::on_start_sub_message(DecoderHandlers *cxt, const int *field_index) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
const Mapper *mapper = cxt->mappers.back();
const Mapper *message_mapper = mapper->fields[*field_index].mapper;
SV *target = cxt->get_target(field_index);
if (!message_mapper->decoder_callbacks.decoder_transform_fieldtable) {
HV *hv = NULL;
if (!SvROK(target)) {
hv = newHV();
SvUPGRADE(target, SVt_RV);
SvROK_on(target);
SvRV_set(target, (SV *) hv);
} else
hv = (HV *) SvRV(target);
cxt->items.push_back((SV *) hv);
cxt->maybe_add_transform(
target,
message_mapper->decoder_callbacks.decoder_transform,
mapper->fields[*field_index].decoder_transform);
} else {
cxt->add_transform_fieldtable(
target,
message_mapper->decoder_callbacks.decoder_transform,
mapper->fields[*field_index].decoder_transform);
cxt->items.push_back((SV *) target);
}
cxt->push_mapper(message_mapper);
if (cxt->track_seen_fields) {
cxt->seen_fields.resize(cxt->seen_fields.size() + 1);
cxt->seen_fields.back().resize(cxt->mappers.back()->fields.size());
}
if (int oneof_count = cxt->mappers.back()->oneof_count) {
cxt->seen_oneof.resize(cxt->seen_oneof.size() + 1);
cxt->seen_oneof.back().resize(oneof_count, -1);
}
if (message_mapper->get_decode_blessed())
sv_bless(target, message_mapper->stash);
return cxt;
}
bool Mapper::DecoderHandlers::on_end_sub_message(DecoderHandlers *cxt, const int *field_index) {
const Mapper *message_mapper = cxt->mappers.back();
if (message_mapper->oneof_count)
cxt->seen_oneof.pop_back();
if (cxt->track_seen_fields)
cxt->seen_fields.pop_back();
cxt->pop_mapper();
if (message_mapper->decoder_callbacks.decoder_transform_fieldtable) {
cxt->finish_add_transform_fieldtable();
}
cxt->items.pop_back();
return true;
}
bool Mapper::DecoderHandlers::on_end_map_entry(DecoderHandlers *cxt, const int *field_index) {
THX_DECLARE_AND_GET;
size_t size = cxt->items.size();
HV *hash = (HV *) cxt->items[size - 2];
SV *key = cxt->map_keys.back().key_sv;
SV *value = (SV *) cxt->items[size - 1];
if (SvOK(key)) {
if (!value) {
value = newSV(0);
cxt->mappers.back()->apply_map_value_default(value);
}
hv_store_ent(hash, key, value, 0);
} else {
// having decoding of maps without keys is debatable
warn("Incomplete map entry: missing key");
}
SvOK_off(key);
cxt->items[size - 1] = NULL;
if (cxt->track_seen_fields)
cxt->seen_fields.back()[1] = false;
return true;
}
bool Mapper::DecoderHandlers::on_end_string_map_entry(DecoderHandlers *cxt, const int *field_index) {
THX_DECLARE_AND_GET;
size_t size = cxt->items.size();
HV *hash = (HV *) cxt->items[size - 2];
const char *key_buffer = cxt->map_keys.back().key_buffer;
size_t key_len = cxt->map_keys.back().key_len;
SV *value = (SV *) cxt->items[size - 1];
if (key_buffer) {
if (!value) {
value = newSV(0);
cxt->mappers.back()->apply_map_value_default(value);
}
// Scanning through the string in order to set the correct flags makes
// hv_common flaster and is an overall win, as silly as it might seem
int flags = is_invariant_string((const U8 *) key_buffer, key_len) ? 0 : HVhek_UTF8;
hv_common(hash, NULL,
key_buffer, key_len,
flags, HV_FETCH_ISSTORE, value, 0);
} else {
// having decoding of maps without keys is debatable
warn("Incomplete map entry: missing key");
}
cxt->map_keys.back().set_buffer(NULL, 0);
cxt->items[size - 1] = NULL;
return true;
}
template<class T>
bool Mapper::DecoderHandlers::on_nv(DecoderHandlers *cxt, const int *field_index, T val) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
sv_setnv(cxt->get_target(field_index), val);
return true;
}
template<class T>
bool Mapper::DecoderHandlers::on_iv(DecoderHandlers *cxt, const int *field_index, T val) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
sv_setiv(cxt->get_target(field_index), val);
return true;
}
template<class T>
bool Mapper::DecoderHandlers::on_uv(DecoderHandlers *cxt, const int *field_index, T val) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
sv_setuv(cxt->get_target(field_index), val);
return true;
}
bool Mapper::DecoderHandlers::on_enum(DecoderHandlers *cxt, const int *field_index, int32_t val) {
THX_DECLARE_AND_GET;
const Field &field = cxt->mappers.back()->fields[*field_index];
if (field.enum_values.find(val) == field.enum_values.end()) {
// this will use the default value later, it's intentional
// mark_seen is not called
if (SvTYPE(cxt->items.back()) == SVt_PVAV)
sv_setiv(cxt->get_target(field_index), field.field_def->default_int32());
return true;
}
cxt->mark_seen(field_index);
sv_setiv(cxt->get_target(field_index), val);
return true;
}
namespace {
bool set_bigint(pTHX_ SV *target, uint64_t value, bool negative) {
dSP;
// this code is horribly slow; it could be made slightly faster,
// but I doubt there is any point
char buffer[19] = "-0x"; // -0x8000000000000000
for (int i = 15; i >= 0; --i, value >>= 4) {
int digit = value & 0xf;
buffer[3 + i] = digit < 10 ? '0' + digit : 'a' + digit - 10;
}
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpvs("Math::BigInt")));
XPUSHs(sv_2mortal(newSVpvn(negative ? buffer : buffer + 1, negative ? 19 : 18)));
PUTBACK;
call_method("new", G_SCALAR);
SPAGAIN;
SV *res = POPs;
PUTBACK;
sv_setsv(target, res);
return true;
}
}
bool Mapper::DecoderHandlers::on_bigiv(DecoderHandlers *cxt, const int *field_index, int64_t val) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
if (val >= I32_MIN && val <= I32_MAX) {
sv_setiv(cxt->get_target(field_index), (IV) val);
return true;
} else {
THX_DECLARE_AND_GET;
return set_bigint(aTHX_ cxt->get_target(field_index), (uint64_t) val, val < 0);
}
}
bool Mapper::DecoderHandlers::on_biguv(DecoderHandlers *cxt, const int *field_index, uint64_t val) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
if (val <= U32_MAX) {
sv_setiv(cxt->get_target(field_index), (IV) val);
return true;
} else {
THX_DECLARE_AND_GET;
return set_bigint(aTHX_ cxt->get_target(field_index), val, false);
}
}
bool Mapper::DecoderHandlers::on_perl_bool(DecoderHandlers *cxt, const int *field_index, bool val) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
set_perl_bool(aTHX_ cxt->get_target(field_index), val);
return true;
}
bool Mapper::DecoderHandlers::on_numeric_bool(DecoderHandlers *cxt, const int *field_index, bool val) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
set_numeric_bool(aTHX_ cxt->get_target(field_index), val);
return true;
}
bool Mapper::DecoderHandlers::on_json_bool(DecoderHandlers *cxt, const int *field_index, bool val) {
const Mapper *mapper = cxt->mappers.back();
cxt->mark_seen(field_index);
mapper->set_json_bool(cxt->get_target(field_index), val);
return true;
}
SV *Mapper::DecoderHandlers::get_target(const int *field_index) {
const Mapper *mapper = mappers.back();
const Field &field = mapper->fields[*field_index];
switch (field.field_target) {
case TARGET_MAP_KEY:
return map_keys.back().key_sv;
case TARGET_MAP_VALUE: {
// here we could use sv_newmortal(), it would be equally efficient,
// but it makes the performance profile a bit more noisy
SV *sv = newSV(0);
items[items.size() - 1] = sv;
return sv;
}
case TARGET_ARRAY_ITEM: {
AV *av = (AV *) items.back();
return *av_store(av, av_top_index(av) + 1, newSV(0));
}
case TARGET_HASH_ITEM:
default: {
HV *hv = (HV *) items.back();
if (field.oneof_index != -1) {
int32_t seen = seen_oneof.back()[field.oneof_index];
if (seen != -1 && seen != *field_index) {
const Field &field = mapper->fields[seen];
hv_delete_ent(hv, field.name, G_DISCARD, field.name_hash);
}
seen_oneof.back()[field.oneof_index] = *field_index;
}
return HeVAL(hv_fetch_ent(hv, field.name, 1, field.name_hash));
}
case TARGET_FIELDTABLE_ITEM: {
SV *sv = newSV(0);
fieldtable_entries.push_back(DecoderFieldtable::Entry(field.field_def->number(), sv));
return sv;
}
}
}
SV *Mapper::DecoderHandlers::get_hash_item_target(const int *field_index) {
const Mapper *mapper = mappers.back();
const Field &field = mapper->fields[*field_index];
if (!mapper->decoder_callbacks.decoder_transform_fieldtable) {
HV *hv = (HV *) items.back();
return HeVAL(hv_fetch_ent(hv, field.name, 1, field.name_hash));
} else {
SV *sv = newSV(0);
fieldtable_entries.push_back(DecoderFieldtable::Entry(field.field_def->number(), sv));
return sv;
}
}
void Mapper::DecoderHandlers::add_transform_fieldtable(SV *target, const DecoderTransform *message_transform, const DecoderTransform *field_transform) {
size_t transform_index = pending_transforms.add_transform(target, message_transform, field_transform);
fieldtable_entries.push_back(DecoderFieldtable::Entry(transform_index, NULL));
}
void Mapper::DecoderHandlers::finish_add_transform_fieldtable() {
for (vector<DecoderFieldtable::Entry>::reverse_iterator it = fieldtable_entries.rbegin(), en = fieldtable_entries.rend(); it != en; ++it) {
if (it->value != NULL)
continue;
int size = it - fieldtable_entries.rbegin();
pending_transforms.finish_add_transform(
it->field,
size, &*(it - 1)
);
fieldtable_entries.erase(fieldtable_entries.end() - size - 1, fieldtable_entries.end());
break;
}
}
Mapper::EncoderState::EncoderState(Status *_status, MapperContext *_mapper_context) :
status(_status), mapper_context(_mapper_context) {
}
void Mapper::EncoderState::setup(Sink *_sink) {
sink = _sink;
}
Mapper::Mapper(pTHX_ Dynamic *_registry, const MessageDef *_message_def, const gpd::pb::Descriptor *_gpd_descriptor, HV *_stash, const MappingOptions &options) :
registry(_registry),
message_def(_message_def),
gpd_descriptor(_gpd_descriptor),
stash(_stash),
encoder_state(&status, &mapper_context),
decoder_callbacks(aTHX_ this),
decoder_field_data(_gpd_descriptor),
encoder_transform(NULL),
encoder_transform_fieldtable(false),
unknown_field_transform(NULL),
json_false(NULL),
json_true(NULL) {
SET_THX_MEMBER;
SvREFCNT_inc(stash);
registry->ref();
pb_encoder_handlers = Encoder::NewHandlers(message_def);
json_encoder_handlers = Printer::NewHandlers(message_def, false /* XXX option */);
decoder_handlers = Handlers::New(message_def);
oneof_count = message_def->oneof_count();
decode_explicit_defaults = options.explicit_defaults;
encode_defaults =
(message_def->syntax() == UPB_SYNTAX_PROTO2 &&
options.encode_defaults) ||
(message_def->syntax() == UPB_SYNTAX_PROTO3 &&
options.encode_defaults_proto3);
check_enum_values = options.check_enum_values;
decode_blessed = options.decode_blessed;
boolean_style = options.boolean_style;
if (options.boolean_style == MappingOptions::JSON) {
load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("JSON"), NULL);
json_true = gv_fetchpvs("JSON::true", 0, SVt_PVGV);
json_false = gv_fetchpvs("JSON::false", 0, SVt_PVGV);
if (json_true == NULL || json_false == NULL) {
croak("Unable to get JSON true/false values for %s", message_def->full_name());
}
SvREFCNT_inc(json_true);
SvREFCNT_inc(json_false);
}
// on older Perls it is not fully reliable because the check is performed before
// the SetMAGIC() call, so it is better to disable it entirely
fail_ref_coercion = HAS_FULL_NOMG ? options.fail_ref_coercion : false;
warn_context = WarnContext::get(aTHX);
if (!message_def->mapentry()) {
if (!decoder_handlers->SetEndMessageHandler(UpbMakeHandler(DecoderHandlers::on_end_message)))
croak("Unable to set upb end message handler for %s", message_def->full_name());
}
std::vector<Field*> fields_by_field_def_index;
fields.reserve(message_def->field_count());
fields_by_field_def_index.resize(message_def->field_count());
bool map_entry = message_def->mapentry();
bool has_required = false;
for (MessageDef::const_field_iterator it = message_def->field_begin(), en = message_def->field_end(); it != en; ++it) {
int index = fields.size();
fields.push_back(Field());
Field &field = fields.back();;
const FieldDef *field_def = *it;
const OneofDef *oneof_def = field_def->containing_oneof();
field.field_action = field.value_action = ACTION_INVALID;
fields_by_field_def_index[field_def->index()] = &fields.back();
has_required = has_required || field_def->label() == UPB_LABEL_REQUIRED;
field.field_def = field_def;
if (field_def->is_extension()) {
string temp = string() + "[" + field_def->full_name() + "]";
field.name = newSVpvn_share(temp.data(), temp.size(), 0);
} else {
string temp = string(field_def->name());
field.name = newSVpvn_share(temp.data(), temp.size(), 0);
}
field.name_hash = SvSHARED_HASH(field.name);
field.has_default = field.is_map = false;
field.mapper = NULL;
field.decoder_transform = NULL;
field.encoder_transform = NULL;
field.field_index = index;
field.oneof_index = -1;
FieldData field_data;
if (map_entry) {
if (field_def->number() == 1) {
field.field_target = TARGET_MAP_KEY;
map_key_index = index;
} else {
field.field_target = TARGET_MAP_VALUE;
map_value_index = index;
}
} else if (field_def->label() == UPB_LABEL_REPEATED) {
field.field_target = TARGET_ARRAY_ITEM;
} else {
field.field_target = TARGET_HASH_ITEM;
}
if (field_def->label() == UPB_LABEL_REPEATED &&
field_def->type() == UPB_TYPE_MESSAGE &&
field_def->message_subdef()->mapentry()) {
field.is_map = true;
}
#define GET_SELECTOR(KIND, TO) \
ok = ok && pb_encoder_handlers->GetSelector(field_def, UPB_HANDLER_##KIND, &field.selector.TO)
#define SET_VALUE_HANDLER(TYPE, FUNCTION) \
ok = ok && decoder_handlers->SetValueHandler<TYPE>(field_def, UpbBind(DecoderHandlers::FUNCTION, new int(index)))
#define SET_HANDLER(KIND, FUNCTION) \
ok = ok && decoder_handlers->Set##KIND##Handler(field_def, UpbBind(DecoderHandlers::FUNCTION, new int(index)))
bool ok = true;
bool has_default = true;
switch (field_def->type()) {
case UPB_TYPE_FLOAT:
GET_SELECTOR(FLOAT, primitive);
SET_VALUE_HANDLER(float, on_nv<float>);
field.default_nv = field_def->default_float();
field_data.action = FieldData::STORE_FLOAT;
field.value_action = ACTION_PUT_FLOAT;
break;
case UPB_TYPE_DOUBLE:
GET_SELECTOR(DOUBLE, primitive);
SET_VALUE_HANDLER(double, on_nv<double>);
field.default_nv = field_def->default_double();
field_data.action = FieldData::STORE_DOUBLE;
field.value_action = ACTION_PUT_DOUBLE;
break;
case UPB_TYPE_BOOL:
GET_SELECTOR(BOOL, primitive);
switch(MappingOptions::BoolStyle(boolean_style)) {
case MappingOptions::Perl:
SET_VALUE_HANDLER(bool, on_perl_bool);
field_data.action = FieldData::STORE_PERL_BOOL;
break;
case MappingOptions::Numeric:
SET_VALUE_HANDLER(bool, on_numeric_bool);
field_data.action = FieldData::STORE_NUMERIC_BOOL;
break;
case MappingOptions::JSON:
SET_VALUE_HANDLER(bool, on_json_bool);
field_data.action = FieldData::STORE_JSON_BOOL;
break;
}
field.default_bool = field_def->default_bool();
field.value_action = ACTION_PUT_BOOL;
break;
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
GET_SELECTOR(STARTSTR, str_start);
GET_SELECTOR(STRING, str_cont);
GET_SELECTOR(ENDSTR, str_end);
SET_HANDLER(StartString, on_start_string);
SET_HANDLER(String, on_append_string);
SET_HANDLER(EndString, on_end_string);
field.default_str = field_def->default_string(&field.default_str_len);
if (field.is_map_key()) {
field_data.action = FieldData::STORE_STRING_KEY;
field.value_action = ACTION_PUT_STRING;
} else if (field_def->type() == UPB_TYPE_STRING) {
field_data.action = FieldData::STORE_STRING;
field.value_action = ACTION_PUT_STRING;
} else {
field_data.action = FieldData::STORE_BYTES;
field.value_action = ACTION_PUT_BYTES;
}
break;
case UPB_TYPE_MESSAGE:
GET_SELECTOR(STARTSUBMSG, msg_start);
GET_SELECTOR(ENDSUBMSG, msg_end);
if (field.is_map) {
SET_HANDLER(EndSubMessage, on_end_map_entry);
} else {
SET_HANDLER(StartSubMessage, on_start_sub_message);
SET_HANDLER(EndSubMessage, on_end_sub_message);
}
has_default = false;
if (field.is_map) {
if (field_def->message_subdef()->FindFieldByNumber(1)->type() == UPB_TYPE_STRING) {
field_data.action = FieldData::STORE_STRING_MAP_MESSAGE;
} else {
field_data.action = FieldData::STORE_MAP_MESSAGE;
}
} else {
field_data.action = FieldData::STORE_MESSAGE;
}
field.value_action = ACTION_PUT_MESSAGE;
break;
case UPB_TYPE_ENUM: {
GET_SELECTOR(INT32, primitive);
if (check_enum_values)
SET_VALUE_HANDLER(int32_t, on_enum);
else
SET_VALUE_HANDLER(int32_t, on_iv<int32_t>);
field.default_iv = field_def->default_int32();
field_data.action = check_enum_values ?
FieldData::STORE_ENUM :
FieldData::STORE_INT32;
field.value_action = ACTION_PUT_ENUM;
if (check_enum_values) {
const EnumDef *enumdef = field_def->enum_subdef();
upb_enum_iter i;
for (upb_enum_begin(&i, enumdef); !upb_enum_done(&i); upb_enum_next(&i))
field.enum_values.insert(upb_enum_iter_number(&i));
}
}
break;
case UPB_TYPE_INT32:
GET_SELECTOR(INT32, primitive);
SET_VALUE_HANDLER(int32_t, on_iv<int32_t>);
field.default_iv = field_def->default_int32();
field_data.action =
field_def->descriptor_type() == UPB_DESCRIPTOR_TYPE_SINT32 ?
FieldData::STORE_ZIGZAG :
FieldData::STORE_INT32;
field.value_action = ACTION_PUT_INT32;
break;
case UPB_TYPE_UINT32:
GET_SELECTOR(UINT32, primitive);
SET_VALUE_HANDLER(uint32_t, on_uv<uint32_t>);
field.default_uv = field_def->default_uint32();
field_data.action = FieldData::STORE_UINT32;
field.value_action = ACTION_PUT_UINT32;
break;
case UPB_TYPE_INT64:
GET_SELECTOR(INT64, primitive);
if (options.use_bigints)
SET_VALUE_HANDLER(int64_t, on_bigiv);
else
SET_VALUE_HANDLER(int64_t, on_iv<int64_t>);
field.default_i64 = field_def->default_int64();
if (options.use_bigints)
field_data.action =
field_def->descriptor_type() == UPB_DESCRIPTOR_TYPE_SINT64 ?
FieldData::STORE_BIG_ZIGZAG :
FieldData::STORE_BIG_INT64;
else
field_data.action =
field_def->descriptor_type() == UPB_DESCRIPTOR_TYPE_SINT64 ?
FieldData::STORE_ZIGZAG :
FieldData::STORE_INT64;
field.value_action = ACTION_PUT_INT64;
break;
case UPB_TYPE_UINT64:
GET_SELECTOR(UINT64, primitive);
if (options.use_bigints)
SET_VALUE_HANDLER(uint64_t, on_biguv);
else
SET_VALUE_HANDLER(uint64_t, on_uv<uint64_t>);
field.default_u64 = field_def->default_uint64();
if (options.use_bigints)
field_data.action = FieldData::STORE_BIG_UINT64;
else
field_data.action = FieldData::STORE_UINT64;
field.value_action = ACTION_PUT_UINT64;
break;
default:
croak("Unhandled field type %d for field '%s'", field_def->type(), field.full_name().c_str());
}
if (has_default &&
field_def->label() == UPB_LABEL_OPTIONAL &&
!oneof_def) {
field.has_default = true;
}
if (field_def->label() == UPB_LABEL_REPEATED) {
GET_SELECTOR(STARTSEQ, seq_start);
GET_SELECTOR(ENDSEQ, seq_end);
if (field.is_map) {
SET_HANDLER(StartSequence, on_start_map);
SET_HANDLER(EndSequence, on_end_map);
field_data.repeated_type = FieldData::MAP_FIELD;
} else {
SET_HANDLER(StartSequence, on_start_sequence);
SET_HANDLER(EndSequence, on_end_sequence);
field_data.repeated_type = FieldData::REPEATED_FIELD;
}
} else {
field_data.repeated_type = FieldData::SCALAR_FIELD;
}
#undef GET_SELECTOR
#undef SET_VALUE_HANDLER
#undef SET_HANDLER
field.field_action = field.value_action;
if (field.is_map) {
field.field_action = ACTION_PUT_MAP;
} else if (field.field_def->label() == UPB_LABEL_REPEATED) {
field.field_action = ACTION_PUT_REPEATED;
} else if (!encode_defaults && field.has_default &&
!(field.is_map_key() || field.is_map_value())) {
field.field_action = field.value_action =
(ValueAction) (field.value_action + 1);
}
field_data.index = index;
decoder_field_data.add_field(field_def->number(), field_data);
if (!ok)
croak("Unable to get upb selector for field %s", field.full_name().c_str());
}
for (vector<Field>::iterator it = fields.begin(), en = fields.end(); it != en; ++it) {
if (it->field_def->is_extension()) {
extension_mapper_fields.push_back(new MapperField(aTHX_ this, &*it));
unref(); // to avoid ref loop
}
field_map.add(aTHX_ it->name, it->field_def->number(), &*it);
}
int oneof_index = 0;
for (MessageDef::const_oneof_iterator it = message_def->oneof_begin(), en = message_def->oneof_end(); it != en; ++it, ++oneof_index) {
const OneofDef *oneof_def = *it;
for (OneofDef::const_iterator it = oneof_def->begin(), en = oneof_def->end(); it != en; ++it) {
const FieldDef *field_def = *it;
Field *field = fields_by_field_def_index[field_def->index()];
field->oneof_index = oneof_index;
}
}
decoder_field_data.optimize_lookup();
check_required_fields = has_required && options.check_required_fields;
ignore_undef_fields = options.ignore_undef_fields;
field_map.optimize_lookup();
}
Mapper::~Mapper() {
for (vector<Field>::iterator it = fields.begin(), en = fields.end(); it != en; ++it) {
if (it->mapper)
it->mapper->unref();
if (it->decoder_transform)
it->decoder_transform->destroy(aTHX);
if (it->encoder_transform)
it->encoder_transform->destroy(aTHX);
}
for (vector<MapperField *>::iterator it = extension_mapper_fields.begin(), en = extension_mapper_fields.end(); it != en; ++it)
// this will make the mapper ref count to go negative, but it's OK
(*it)->unref();
if (encoder_transform)
encoder_transform->destroy(aTHX);
// make sure this only goes away after inner destructors have completed
refcounted_mortalize(aTHX_ registry);
SvREFCNT_dec(stash);
SvREFCNT_dec(json_true);
SvREFCNT_dec(json_false);
}
const char *Mapper::full_name() const {
return message_def->full_name();
}
const char *Mapper::package_name() const {
return HvNAME(stash);
}
MapperField *Mapper::find_extension(const std::string &name) const {
for (vector<MapperField *>::const_iterator it = extension_mapper_fields.begin(), en = extension_mapper_fields.end(); it != en; ++it) {
if (name == (*it)->name())
return *it;
}
return NULL;
}
int Mapper::field_count() const {
return fields.size();
}
const Mapper::Field *Mapper::get_field(int index) const {
return &fields[index];
}
SV *Mapper::message_descriptor() const {
SV *ref = newSV(0);
sv_setref_iv(ref, "Google::ProtocolBuffers::Dynamic::MessageDef", (IV) message_def);
return ref;
}
SV *Mapper::make_object(SV *data) const {
SV *obj = NULL;
if (data) {
if (!SvROK(data) || SvTYPE(SvRV(data)) != SVt_PVHV)
croak("Not a hash reference");
if (SvTEMP(data) && SvREFCNT(data) == 1) {
// steal
SvREFCNT_inc(data);
obj = data;
} else {
HV * hv = newHV();
sv_2mortal((SV *) hv);
HV *orig = (HV *) SvRV(data);
I32 keylen;
char *key;
hv_iterinit(orig);
while (SV *sv = hv_iternextsv(orig, &key, &keylen)) {
hv_store(hv, key, keylen, newSVsv(sv), 0);
}
obj = newRV_inc((SV *) hv);
}
} else {
obj = newRV_noinc((SV *) newHV());
}
if (decode_blessed)
sv_bless(obj, stash);
return obj;
}
void Mapper::resolve_mappers() {
for (vector<Field>::iterator it = fields.begin(), en = fields.end(); it != en; ++it) {
const FieldDef *field = it->field_def;
if (field->type() != UPB_TYPE_MESSAGE)
continue;
it->mapper = registry->find_mapper(field->message_subdef());
it->mapper->ref();
decoder_handlers->SetSubHandlers(it->field_def, it->mapper->decoder_handlers.get());
}
}
void Mapper::create_encoder_decoder() {
pb_decoder_method = DecoderMethod::New(DecoderMethodOptions(decoder_handlers.get()));
json_decoder_method = ParserMethod::New(message_def);
decoder_sink.Reset(decoder_handlers.get(), &decoder_callbacks);
}
static SV *scalar_option(pTHX_ HV *options, const char *name) {
SV **hv_value = hv_fetch(options, name, strlen(name), 0);
if (!hv_value)
return NULL;
return *hv_value;
}
static HV *hash_option(pTHX_ HV *options, const char *name) {
SV **hv_value = hv_fetch(options, name, strlen(name), 0);
if (!hv_value)
return NULL;
if (!SvROK(*hv_value) || SvTYPE(SvRV(*hv_value)) != SVt_PVHV)
croak("Expected hash reference for option key %s", name);
return (HV *) SvRV(*hv_value);
}
static DecoderTransform *make_decoder_transform(pTHX_ SV *scalar, bool fieldtable) {
if (SvROK(scalar)) {
SV *maybe_cv = SvRV(scalar);
if (SvTYPE(maybe_cv) != SVt_PVCV)
croak("Transformation function is a reference but not a code reference");
if (fieldtable)
croak("Fieldtable transformation function must be written in C");
return new DecoderTransform(SvREFCNT_inc(maybe_cv));
}
if (SvIOK(scalar)) {
if (fieldtable) {
return new DecoderTransform(INT2PTR(CDecoderTransformFieldtable, SvIV(scalar)));
} else {
return new DecoderTransform(INT2PTR(CDecoderTransform, SvIV(scalar)));
}
}
croak("Transformation function must be either a code reference or an integer representing a C function pointer");
}
static EncoderTransform *make_encoder_transform(pTHX_ SV *scalar, bool fieldtable) {
if (SvROK(scalar)) {
SV *maybe_cv = SvRV(scalar);
if (SvTYPE(maybe_cv) != SVt_PVCV)
croak("Transformation function is a reference but not a code reference");
if (fieldtable)
croak("Fieldtable transformation function must be written in C");
return new EncoderTransform(SvREFCNT_inc(maybe_cv));
}
if (SvIOK(scalar)) {
if (fieldtable) {
return new EncoderTransform(INT2PTR(CEncoderTransformFieldtable, SvIV(scalar)));
} else {
return new EncoderTransform(INT2PTR(CEncoderTransform, SvIV(scalar)));
}
}
croak("Transformation function must be an integer representing a C function pointer");
}
static UnknownFieldTransform *make_unknown_field_transform(pTHX_ SV *scalar) {
if (SvIOK(scalar)) {
return new UnknownFieldTransform(INT2PTR(CUnknownFieldTransform, SvIV(scalar)));
}
croak("Transformation function must be an integer representing a C function pointer");
}
void Mapper::set_decoder_options(HV *options) {
SV *transform = scalar_option(aTHX_ options, "transform");
SV *fieldtable_sv = scalar_option(aTHX_ options, "fieldtable");
HV *transform_fields = hash_option(aTHX_ options, "transform_fields");
bool fieldtable = fieldtable_sv ? SvTRUE(fieldtable_sv) : false;
if (fieldtable && (!transform && !transform_fields)) {
croak("Can't use fieldtable without transform");
}
if (transform) {
if (decoder_callbacks.decoder_transform)
decoder_callbacks.decoder_transform->destroy(aTHX);
decoder_callbacks.decoder_transform = make_decoder_transform(aTHX_ transform, fieldtable);
}
if (fieldtable) {
if (message_def->mapentry()) {
croak("Can't use fieldtable for map messages");
}
decoder_callbacks.decoder_transform_fieldtable = true;
for (vector<Field>::iterator it = fields.begin(), en = fields.end(); it != en; ++it) {
if (it->field_target == TARGET_HASH_ITEM) {
it->field_target = TARGET_FIELDTABLE_ITEM;
}
}
}
if (transform_fields) {
I32 keylen;
char *key;
hv_iterinit(transform_fields);
while (SV *value = hv_iternextsv(transform_fields, &key, &keylen)) {
Field *field = field_map.find_by_name(aTHX_ key, keylen);
if (field == NULL) {
croak("Unknown field name %.*s", keylen, key);
}
if (field->is_map ||
field->field_def->label() == UPB_LABEL_REPEATED ||
field->field_def->type() != UPB_TYPE_MESSAGE) {
croak("Can't apply transformation to field %.*s", keylen, key);
}
if (field->decoder_transform)
field->decoder_transform->destroy(aTHX);
field->decoder_transform = make_decoder_transform(aTHX_ value, fieldtable);
}
}
}
void Mapper::set_encoder_options(HV *options) {
SV *transform = scalar_option(aTHX_ options, "transform");
SV *fieldtable_sv = scalar_option(aTHX_ options, "fieldtable");
HV *transform_fields = hash_option(aTHX_ options, "transform_fields");
SV *unknown_field = scalar_option(aTHX_ options, "unknown_field");
bool fieldtable = fieldtable_sv ? SvTRUE(fieldtable_sv) : false;
if (fieldtable && (!transform && !transform_fields)) {
croak("Can't use fieldtable without transform");
}
if (transform) {
if (encoder_transform)
encoder_transform->destroy(aTHX);
encoder_transform = make_encoder_transform(aTHX_ transform, fieldtable);
}
if (fieldtable) {
if (message_def->mapentry()) {
croak("Can't use fieldtable for map messages");
}
encoder_transform_fieldtable = true;
}
if (unknown_field) {
if (unknown_field_transform)
unknown_field_transform->destroy(aTHX);
unknown_field_transform = make_unknown_field_transform(aTHX_ unknown_field);
}
if (transform_fields) {
I32 keylen;
char *key;
hv_iterinit(transform_fields);
while (SV *value = hv_iternextsv(transform_fields, &key, &keylen)) {
Field *field = field_map.find_by_name(aTHX_ key, keylen);
if (field == NULL) {
croak("Unknown field name %.*s", keylen, key);
}
if (field->is_map ||
field->field_def->label() == UPB_LABEL_REPEATED ||
field->field_def->type() != UPB_TYPE_MESSAGE) {
croak("Can't apply transformation to field %.*s", keylen, key);
}
if (field->encoder_transform)
field->encoder_transform->destroy(aTHX);
field->encoder_transform = make_encoder_transform(aTHX_ value, fieldtable);
if (field->field_action == ACTION_PUT_MESSAGE)
field->field_action = ACTION_PUT_FIELDTABLE;
if (field->value_action == ACTION_PUT_MESSAGE)
field->value_action = ACTION_PUT_FIELDTABLE;
}
}
}
bool Mapper::get_decode_blessed() const {
return decode_blessed;
}
bool Mapper::get_track_seen_fields() const {
return check_required_fields || decode_explicit_defaults;
}
void Mapper::set_bool(SV *target, bool value) const {
switch (MappingOptions::BoolStyle(boolean_style)) {
case MappingOptions::Perl:
set_perl_bool(aTHX_ target, value);
break;
case MappingOptions::Numeric:
set_numeric_bool(aTHX_ target, value);
break;
case MappingOptions::JSON:
set_json_bool(target, value);
break;
}
}
void Mapper::set_json_bool(SV *target, bool value) const {
sv_setsv(target, GvSV(value ? json_true : json_false));
}
SV *Mapper::encode(SV *ref) {
if (pb_decoder_method.get() == NULL)
croak("It looks like resolve_references() was not called (and please use map() anyway)");
upb::Environment *env = make_localized_environment(aTHX_ &status);
upb::pb::Encoder *pb_encoder = upb::pb::Encoder::Create(env, pb_encoder_handlers.get(), vector_sink.input());
encoder_state.setup(pb_encoder->input());
status.Clear();
warn_context->localize_warning_handler(aTHX);
warn_context->set_context(&mapper_context);
encoder_state.mapper_context->clear();
SV *result = NULL;
#if HAS_FULL_NOMG
SvGETMAGIC(ref);
#endif
if (encode_message(encoder_state, ref))
result = newSVpvn(vector_sink.data(), vector_sink.size());
return result;
}
SV *Mapper::encode_json(SV *ref) {
if (json_decoder_method.get() == NULL)
croak("It looks like resolve_references() was not called (and please use map() anyway)");
upb::Environment *env = make_localized_environment(aTHX_ &status);
upb::json::Printer *json_encoder = upb::json::Printer::Create(env, json_encoder_handlers.get(), vector_sink.input());
encoder_state.setup(json_encoder->input());
status.Clear();
warn_context->localize_warning_handler(aTHX);
warn_context->set_context(&mapper_context);
encoder_state.mapper_context->clear();
SV *result = NULL;
#if HAS_FULL_NOMG
SvGETMAGIC(ref);
#endif
if (encode_message(encoder_state, ref))
result = newSVpvn(vector_sink.data(), vector_sink.size());
return result;
}
SV *Mapper::decode_upb(const char *buffer, STRLEN bufsize) {
if (pb_decoder_method.get() == NULL)
croak("It looks like resolve_references() was not called (and please use map() anyway)");
upb::Environment *env = make_localized_environment(aTHX_ &status);
upb::pb::Decoder *pb_decoder = upb::pb::Decoder::Create(env, pb_decoder_method.get(), &decoder_sink);
status.Clear();
pb_decoder->Reset();
decoder_callbacks.prepare(newHV());
SV *result = NULL;
if (BufferSource::PutBuffer(buffer, bufsize, pb_decoder->input())) {
result = decoder_callbacks.get_and_mortalize_target();
}
decoder_callbacks.finish();
return SvREFCNT_inc(result);
}
SV *Mapper::decode_bbpb(const char *buffer, STRLEN bufsize) {
if (pb_decoder_method.get() == NULL)
croak("It looks like resolve_references() was not called (and please use map() anyway)");
status.Clear();
decoder_callbacks.prepare(newHV());
SV *result = NULL;
if (run_bbpb_decoder(this, buffer, bufsize)) {
result = decoder_callbacks.get_and_mortalize_target();
}
decoder_callbacks.finish();
return SvREFCNT_inc(result);
}
SV *Mapper::decode_json(const char *buffer, STRLEN bufsize) {
if (json_decoder_method.get() == NULL)
croak("It looks like resolve_references() was not called (and please use map() anyway)");
upb::Environment *env = make_localized_environment(aTHX_ &status);
upb::json::Parser *json_decoder = upb::json::Parser::Create(env, json_decoder_method.get(), NULL, &decoder_sink, true);
status.Clear();
decoder_callbacks.prepare(newHV());
SV *result = NULL;
if (BufferSource::PutBuffer(buffer, bufsize, json_decoder->input())) {
result = decoder_callbacks.get_and_mortalize_target();
}
decoder_callbacks.finish();
return SvREFCNT_inc(result);
}
bool Mapper::run_bbpb_decoder(Mapper *root_mapper, const char *buffer, STRLEN bufsize) {
DecoderHandlers *decoder_callbacks = &root_mapper->decoder_callbacks;
gpd::pb::Decoder pb_decoder;
pb_decoder.set_buffer(buffer, bufsize);
pb_decoder.start_message(&root_mapper->decoder_field_data);
const Mapper *mapper = root_mapper;
vector<const Mapper *> mappers;
for (;;) {
switch (pb_decoder.next_token()) {
case gpd::pb::TOKEN_FIELD: {
const FieldDataEntry *entry = pb_decoder.get_field_entry<FieldDataEntry>();
int idx = entry->data.index;
switch (entry->data.action) {
case FieldData::STORE_DOUBLE:
DecoderHandlers::on_nv(decoder_callbacks, &idx, pb_decoder.get_double());
break;
case FieldData::STORE_FLOAT:
DecoderHandlers::on_nv(decoder_callbacks, &idx, pb_decoder.get_float());
break;
case FieldData::STORE_INT64:
DecoderHandlers::on_iv(decoder_callbacks, &idx, pb_decoder.get_long());
break;
case FieldData::STORE_BIG_INT64:
DecoderHandlers::on_bigiv(decoder_callbacks, &idx, pb_decoder.get_long());
break;
case FieldData::STORE_INT32:
DecoderHandlers::on_iv(decoder_callbacks, &idx, pb_decoder.get_int());
break;
case FieldData::STORE_UINT64:
DecoderHandlers::on_uv(decoder_callbacks, &idx, pb_decoder.get_unsigned_long());
break;
case FieldData::STORE_BIG_UINT64:
DecoderHandlers::on_biguv(decoder_callbacks, &idx, pb_decoder.get_unsigned_long());
break;
case FieldData::STORE_UINT32:
DecoderHandlers::on_uv(decoder_callbacks, &idx, pb_decoder.get_unsigned_int());
break;
case FieldData::STORE_PERL_BOOL:
DecoderHandlers::on_perl_bool(decoder_callbacks, &idx, pb_decoder.get_unsigned_int());
break;
case FieldData::STORE_NUMERIC_BOOL:
DecoderHandlers::on_numeric_bool(decoder_callbacks, &idx, pb_decoder.get_unsigned_int());
break;
case FieldData::STORE_JSON_BOOL:
DecoderHandlers::on_json_bool(decoder_callbacks, &idx, pb_decoder.get_unsigned_int());
break;
case FieldData::STORE_STRING:
DecoderHandlers::on_string(decoder_callbacks, &idx, pb_decoder.get_string_buffer(), pb_decoder.get_string_length(), true);
break;
case FieldData::STORE_BYTES:
DecoderHandlers::on_string(decoder_callbacks, &idx, pb_decoder.get_string_buffer(), pb_decoder.get_string_length(), false);
break;
case FieldData::STORE_STRING_KEY:
DecoderHandlers::on_string_key(decoder_callbacks, &idx, pb_decoder.get_string_buffer(), pb_decoder.get_string_length());
break;
case FieldData::STORE_MESSAGE: {
const Mapper *field_mapper = mapper->fields[idx].mapper;
DecoderHandlers::on_start_sub_message(decoder_callbacks, &idx);
pb_decoder.start_message(&field_mapper->decoder_field_data);
mappers.push_back(mapper);
mapper = field_mapper;
}
break;
case FieldData::STORE_MAP_MESSAGE:
case FieldData::STORE_STRING_MAP_MESSAGE: {
const Mapper *field_mapper = mapper->fields[idx].mapper;
pb_decoder.start_message(&field_mapper->decoder_field_data);
mappers.push_back(mapper);
mapper = field_mapper;
}
break;
case FieldData::STORE_ENUM:
DecoderHandlers::on_enum(decoder_callbacks, &idx, pb_decoder.get_long());
break;
case FieldData::STORE_ZIGZAG:
DecoderHandlers::on_iv(decoder_callbacks, &idx, pb_decoder.get_zigzag_long());
break;
case FieldData::STORE_BIG_ZIGZAG:
DecoderHandlers::on_bigiv(decoder_callbacks, &idx, pb_decoder.get_zigzag_long());
break;
}
}
break;
case gpd::pb::TOKEN_START_SEQUENCE: {
const FieldDataEntry *entry = pb_decoder.get_field_entry<FieldDataEntry>();
if (entry->data.repeated_type == FieldData::REPEATED_FIELD) {
DecoderHandlers::on_start_sequence(decoder_callbacks, &entry->data.index);
} else {
DecoderHandlers::on_start_map(decoder_callbacks, &entry->data.index);
}
}
break;
case gpd::pb::TOKEN_END_SEQUENCE: {
const FieldDataEntry *entry = pb_decoder.get_field_entry<FieldDataEntry>();
if (entry->data.repeated_type == FieldData::REPEATED_FIELD) {
DecoderHandlers::on_end_sequence(decoder_callbacks, &entry->data.index);
} else {
DecoderHandlers::on_end_map(decoder_callbacks, &entry->data.index);
}
}
break;
case gpd::pb::TOKEN_END_MESSAGE: {
if (!DecoderHandlers::on_end_message(decoder_callbacks, &root_mapper->status))
return false;
if (mappers.size() == 0) {
return true;
}
pb_decoder.end_message();
mapper = mappers.back();
mappers.pop_back();
const FieldDataEntry *entry = pb_decoder.get_field_entry<FieldDataEntry>();
switch (entry->data.action) {
case FieldData::STORE_MESSAGE:
DecoderHandlers::on_end_sub_message(decoder_callbacks, &entry->data.index);
break;
case FieldData::STORE_MAP_MESSAGE:
DecoderHandlers::on_end_map_entry(decoder_callbacks, &entry->data.index);
break;
case FieldData::STORE_STRING_MAP_MESSAGE:
DecoderHandlers::on_end_string_map_entry(decoder_callbacks, &entry->data.index);
break;
default:
assert(false); // precondition: end of a message can be only for message/map entry
return false;
}
}
break;
case gpd::pb::TOKEN_ERROR: {
const char *decoder_error = pb_decoder.get_error_message();
if (decoder_error != NULL) {
root_mapper->status.SetErrorMessage(decoder_error);
}
}
return false;
case gpd::pb::TOKEN_UNKNOWN_FIELD:
// do nothing for now
break;
}
}
return true;
}
bool Mapper::check(SV *ref) {
if (pb_decoder_method.get() == NULL)
croak("It looks like resolve_references() was not called (and please use map() anyway)");
status.Clear();
return check(&status, ref);
}
const char *Mapper::last_error_message() const {
return !decoder_callbacks.error.empty() ? decoder_callbacks.error.c_str() :
!status.ok() ? status.error_message() :
"Unknown error";
}
namespace {
// this code is horribly slow; it could be made slightly faster,
// but I doubt there is any point
uint64_t extract_bits(pTHX_ SV *src, bool *negative) {
dSP;
PUSHMARK(SP);
XPUSHs(src);
PUTBACK;
call_method("as_hex", G_SCALAR);
SPAGAIN;
SV *res = POPs;
PUTBACK;
uint64_t integer = 0;
const char *buffer = SvPV_nolen(res);
*negative = buffer[0] == '-';
if (*negative)
++buffer;
buffer += 2; // skip 0x
while (*buffer) {
integer <<= 4;
integer |= READ_XDIGIT(buffer);
}
return integer;
}
bool is_coerced_ref(pTHX_ Status *status, const Mapper::Field &fd, SV *sv) {
// for overloaded values, we have no easy way to check if a specific
// overload method has been defined, so just pass the values through
//
// the call to Gv_AMG is necessary because SVf_AMAGIC is set on all stashes
// on modify, and only reset by Gv_AMG() if the stash does not have overload
// magic
if (!SvROK(sv) || (SvAMAGIC(sv) && Gv_AMG(SvSTASH(SvRV(sv)))))
return false;
status->SetFormattedErrorMessage(
"Reference used when a scalar value is expected for field '%s'",
fd.full_name().c_str()
);
return true;
}
bool fail_if_required(Status *status, const Mapper::Field &fd) {
if (fd.field_def->label() == UPB_LABEL_REQUIRED) {
status->SetFormattedErrorMessage(
"Missing required field '%s'",
fd.full_name().c_str());
return true;
}
return false;
}
inline bool SvPOK_utf8(SV *sv) {
return ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK|SVf_UTF8));
}
#if PERL_VERSION < 32
// this is a copied-and-modified version of SvPVutf8 and sv_2pvutf8
inline char *SvPVutf8_nomg_impl(pTHX_ SV *sv, STRLEN *lp) {
// condition and branch are from SvPVutf8
if (SvPOK_utf8(sv)) {
*lp = SvCUR(sv);
return SvPVX(sv);
} else {
// from the body of sv_2pvutf8
if (((SvREADONLY(sv) || SvFAKE(sv)) && !SvIsCOW(sv))
|| isGV_with_GP(sv) || SvROK(sv))
sv = sv_mortalcopy(sv);
sv_utf8_upgrade_nomg(sv);
return SvPV_nomg(sv, *lp);
}
}
#define SvPVutf8_nomg(sv, len) SvPVutf8_nomg_impl(aTHX_ sv, &len)
#endif
uint64_t get_uint64_nomg(pTHX_ SV *src) {
if (SvROK(src) && sv_derived_from(src, "Math::BigInt")) {
bool negative = false;
uint64_t value = extract_bits(aTHX_ src, &negative);
return negative ? ~value + 1 : value;
} else
return SvUV(src);
}
uint64_t get_uint64(pTHX_ SV *src) {
SvGETMAGIC(src);
return get_uint64_nomg(aTHX_ src);
}
int64_t get_int64_nomg(pTHX_ SV *src) {
if (SvROK(src) && sv_derived_from(src, "Math::BigInt")) {
bool negative = false;
uint64_t value = extract_bits(aTHX_ src, &negative);
return negative ? -value : value;
} else
return SvIV(src);
}
int64_t get_int64(pTHX_ SV *src) {
SvGETMAGIC(src);
return get_int64_nomg(aTHX_ src);
}
#define SvIV64(sv) get_int64(aTHX_ sv)
#define SvUV64(sv) get_uint64(aTHX_ sv)
#define SvIV64_nomg(sv) get_int64_nomg(aTHX_ sv)
#define SvUV64_nomg(sv) get_uint64_nomg(aTHX_ sv)
IV key_iv(pTHX_ const char *key, I32 keylen) {
UV value;
int numtype = grok_number(key, keylen, &value);
if (numtype & IS_NUMBER_IN_UV) {
if (numtype & IS_NUMBER_NEG) {
if (value < (UV) IV_MIN)
return - (IV) value;
} else {
if (value < (UV) IV_MAX)
return (IV) value;
}
}
// XXX warn
return 0;
}
#if HAS_FULL_NOMG
#define SvIV_enc SvIV_nomg
#define SvIV64_enc SvIV64_nomg
#define SvUV_enc SvUV_nomg
#define SvUV64_enc SvUV64_nomg
#define SvNV_enc SvNV_nomg
#define SvPV_enc SvPV_nomg
#define SvPVutf8_enc SvPVutf8_nomg
#define SvTRUE_enc SvTRUE_nomg
#else
#define SvIV_enc SvIV
#define SvIV64_enc SvIV64
#define SvUV_enc SvUV
#define SvUV64_enc SvUV64
#define SvNV_enc SvNV
#define SvPV_enc SvPV
#define SvPVutf8_enc SvPVutf8
#define SvTRUE_enc SvTRUE
#endif
UV key_uv(pTHX_ const char *key, I32 keylen) {
UV value;
int numtype = grok_number(key, keylen, &value);
if (numtype & IS_NUMBER_IN_UV) {
return value;
}
// XXX warn
return 0;
}
class SVGetter {
public:
SV *operator()(pTHX_ SV *src) const { return src; }
};
class NVGetter {
public:
NV operator()(pTHX_ SV *src) const { return SvNV_enc(src); }
};
class IVGetter {
public:
IV operator()(pTHX_ SV *src) const { return SvIV_enc(src); }
};
class UVGetter {
public:
UV operator()(pTHX_ SV *src) const { return SvUV_enc(src); }
};
class I64Getter {
public:
int64_t operator()(pTHX_ SV *src) const { return SvIV64_enc(src); }
};
class U64Getter {
public:
uint64_t operator()(pTHX_ SV *src) const { return SvUV64_enc(src); }
};
class StringGetter {
public:
void operator()(pTHX_ SV *src, string *dest) const {
STRLEN len;
const char *buf = SvPVutf8_enc(src, len);
dest->assign(buf, len);
}
};
class BytesGetter {
public:
void operator()(pTHX_ SV *src, string *dest) const {
STRLEN len;
const char *buf = SvPV_enc(src, len);
dest->assign(buf, len);
}
};
class BoolGetter {
public:
bool operator()(pTHX_ SV *src) const { return SvTRUE_enc(src); }
};
#define DEF_SIMPLE_SETTER(NAME, METHOD, TYPE) \
struct NAME { \
NAME(Status *status) { } \
bool operator()(pTHX_ Sink *sink, const Mapper::Field &fd, TYPE value) { \
return sink->METHOD(fd.selector.primitive, value); \
} \
}
DEF_SIMPLE_SETTER(Int32Emitter, PutInt32, int32_t);
DEF_SIMPLE_SETTER(Int64Emitter, PutInt64, int64_t);
DEF_SIMPLE_SETTER(UInt32Emitter, PutUInt32, uint32_t);
DEF_SIMPLE_SETTER(UInt64Emitter, PutUInt64, uint64_t);
DEF_SIMPLE_SETTER(FloatEmitter, PutFloat, float);
DEF_SIMPLE_SETTER(DoubleEmitter, PutDouble, double);
DEF_SIMPLE_SETTER(BoolEmitter, PutBool, bool);
#undef DEF_SIMPLE_SETTER
struct EnumEmitter {
Status *status;
EnumEmitter(Status *_status) { status = _status; }
bool operator()(pTHX_ Sink *sink, const Mapper::Field &fd, int32_t value) {
if (fd.enum_values.find(value) == fd.enum_values.end()) {
status->SetFormattedErrorMessage(
"Invalid enumeration value %d for field '%s'",
value,
fd.full_name().c_str()
);
return false;
}
return sink->PutInt32(fd.selector.primitive, value);
}
};
struct StringEmitter {
StringEmitter(Status *status) { }
bool operator()(pTHX_ Sink *sink, const Mapper::Field &fd, SV *value) {
STRLEN len;
const char *str = fd.value_action == Mapper::ACTION_PUT_STRING ? SvPVutf8(value, len) : SvPV(value, len);
Sink sub;
if (!sink->StartString(fd.selector.str_start, len, &sub))
return false;
sub.PutStringBuffer(fd.selector.str_cont, str, len, NULL);
return sink->EndString(fd.selector.str_end);
}
};
}
template<class G, class S>
bool Mapper::encode_from_array(EncoderState &state, const Mapper::Field &fd, AV *source) const {
G getter;
S setter(state.status);
Sink sub;
int size = av_top_index(source) + 1;
if (size == 0) {
// when emitting a packed repeated field, we must avoid invoking
// StartSequence if we have no values to encode. Not doing so causes us
// to emit a packed field with length=0, which is illegal and breaks
// decoders.
return true;
}
Sink *sink = state.sink;
if (!sink->StartSequence(fd.selector.seq_start, &sub))
return false;
MapperContext::Item &mapper_cxt = state.mapper_context->push_level(source);
for (int i = 0; i < size; ++i) {
mapper_cxt.set_array_index(i);
SV **item = av_fetch(source, i, 0);
if (!item)
return false;
#if HAS_FULL_NOMG
SvGETMAGIC(*item);
#endif
if (fail_ref_coercion && is_coerced_ref(aTHX_ state.status, fd, *item))
return false;
if (!setter(aTHX_ &sub, fd, getter(aTHX_ *item)))
return false;
}
state.mapper_context->pop_level();
return sink->EndSequence(fd.selector.seq_end);
}
bool Mapper::encode_from_message_array(EncoderState &state, const Mapper::Field &fd, AV *source) const {
int size = av_top_index(source) + 1;
Sink *sink = state.sink, sub;
if (!sink->StartSequence(fd.selector.seq_start, &sub))
return false;
MapperContext::Item &mapper_cxt = state.mapper_context->push_level(source);
for (int i = 0; i < size; ++i) {
mapper_cxt.set_array_index(i);
SV **item = av_fetch(source, i, 0);
if (!item)
return false;
Sink submsg;
EncoderState submsg_state(state, &submsg);
#if HAS_FULL_NOMG
SvGETMAGIC(*item);
#endif
if (!sub.StartSubMessage(fd.selector.msg_start, &submsg))
return false;
if (!encode_message(submsg_state, *item))
return false;
if (!sub.EndSubMessage(fd.selector.msg_end))
return false;
}
state.mapper_context->pop_level();
return sink->EndSequence(fd.selector.seq_end);
}
namespace {
HE *hv_fetch_ent_tied(pTHX_ HV *hv, SV *name, I32 lval, U32 hash) {
if (!hv_exists_ent(hv, name, hash))
return NULL;
return hv_fetch_ent(hv, name, lval, hash);
}
class TrackOneof {
public:
TrackOneof(int oneof_count) :
seen_oneof(oneof_count) {
}
bool mark_and_maybe_skip(int oneof_index) {
if (oneof_index != -1) {
if (seen_oneof[oneof_index])
return true;
seen_oneof[oneof_index] = true;
}
return false;
}
private:
vector<bool> seen_oneof;
};
class TrackSeen {
typedef Mapper::Field Field;
public:
TrackSeen(bool _check, const vector<Field> &_fields) :
check(_check),
seen_fields(check ? _fields.size() : 0),
fields(_fields) {
}
void mark(int index) {
if (check)
seen_fields[index] = true;
}
bool required_fields_present(Status *status) {
return !check || perform_check(status);
}
private:
bool perform_check(Status *status) {
for (int i = 0, max = fields.size(); i < max; ++i) {
const Field &field = fields[i];
if (!seen_fields[i] && fail_if_required(status, field)) {
return false;
}
}
return true;
}
bool check;
vector<bool> seen_fields;
const vector<Field> &fields;
};
}
bool Mapper::encode_simple_message_iterate_fields(EncoderState &state, SV *ref) const {
#if !HAS_FULL_NOMG
SvGETMAGIC(ref);
#endif
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Not a hash reference when encoding a %s value", message_def->full_name());
HV *hv = (HV *) SvRV(ref);
Sink *sink = state.sink;
if (!sink->StartMessage())
return false;
bool tied = SvTIED_mg((SV *) hv, PERL_MAGIC_tied);
MapperContext::Item &mapper_cxt = state.mapper_context->push_level(hv, MapperContext::Message);
TrackOneof track_oneof(oneof_count);
for (vector<Field>::const_iterator it = fields.begin(), en = fields.end(); it != en; ++it) {
mapper_cxt.set_hash_key(it->name);
HE *he = tied ? hv_fetch_ent_tied(aTHX_ hv, it->name, 0, it->name_hash) :
hv_fetch_ent(hv, it->name, 0, it->name_hash);
SV *value = NULL;
if (he) {
value = HeVAL(he);
#if HAS_FULL_NOMG
SvGETMAGIC(value);
#endif
}
if (!he || (ignore_undef_fields && !SvOK(value))) {
if (fail_if_required(state.status, *it))
return false;
else
continue;
} else if (track_oneof.mark_and_maybe_skip(it->oneof_index)) {
continue;
}
if (!encode_field(state, *it, value))
return false;
}
state.mapper_context->pop_level();
if (!sink->EndMessage(state.status))
return false;
return true;
}
bool Mapper::encode_transformed_fieldtable_message(EncoderState &state, SV *ref, EncoderTransform *encoder_transform) const {
#if !HAS_FULL_NOMG
SvGETMAGIC(ref);
#endif
Sink *sink = state.sink;
if (!sink->StartMessage())
return false;
MapperContext::Item &mapper_cxt = state.mapper_context->push_level(ref, MapperContext::Message);
EncoderFieldtable *target = NULL, target_copy;
encoder_transform->transform_fieldtable(aTHX_ &target, ref);
if (target == NULL) {
croak("Transform callback for message %s did not return a table", message_def->full_name());
return false;
}
if (target->size > 1) {
size_t size = target->size * sizeof(EncoderFieldtable::Entry);
target_copy.size = target->size;
target_copy.entries = (EncoderFieldtable::Entry *) memcpy(alloca(size), target->entries, size);
target = &target_copy;
}
TrackOneof track_oneof(oneof_count);
TrackSeen track_seen(check_required_fields, fields);
for (int i = 0, max = target->size; i < max; ++i) {
EncoderFieldtable::Entry *entry = target->entries + i;
const Field *it = field_map.find_by_number(entry->field);
if (it == NULL) {
continue;
}
mapper_cxt.set_hash_key(it->name);
if (track_oneof.mark_and_maybe_skip(it->oneof_index)) {
continue;
}
track_seen.mark(it->field_index);
SV *value = entry->value;
#if HAS_FULL_NOMG
SvGETMAGIC(value);
#endif
if (!encode_field(state, *it, value)) {
SvREFCNT_dec_NN(value);
return false;
}
SvREFCNT_dec_NN(value);
}
state.mapper_context->pop_level();
if (!track_seen.required_fields_present(state.status)) {
return false;
}
if (!sink->EndMessage(state.status))
return false;
return true;
}
bool Mapper::encode_transformed_message(EncoderState &state, SV *ref, EncoderTransform *encoder_transform) const {
#if !HAS_FULL_NOMG
SvGETMAGIC(ref);
#endif
SV *target = sv_newmortal();
encoder_transform->transform(aTHX_ target, ref);
return encode_simple_message_iterate_hash(state, target);
}
bool Mapper::encode_simple_message_iterate_hash(EncoderState &state, SV *ref) const {
#if !HAS_FULL_NOMG
SvGETMAGIC(ref);
#endif
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Not a hash reference when encoding a %s value", message_def->full_name());
HV *hv = (HV *) SvRV(ref);
Sink *sink = state.sink;
if (!sink->StartMessage())
return false;
TrackOneof track_oneof(oneof_count);
TrackSeen track_seen(check_required_fields, fields);
bool magical = SvMAGICAL((SV *) hv);
hv_iterinit(hv);
MapperContext::Item &mapper_cxt = state.mapper_context->push_level(hv, MapperContext::Message);
while (HE *entry = hv_iternext(hv)) {
mapper_cxt.set_hash_key(entry);
const Field *it = field_map.find_by_name(aTHX_ entry);
if (it == NULL) {
if (unknown_field_transform) {
UnknownFieldContext context;
state.mapper_context->fill_context(&context.mapper_context, &context.size);
unknown_field_transform->transform(aTHX_ &context, HeVAL(entry));
}
continue;
}
SV *value = UNLIKELY(magical) ? hv_iterval(hv, entry) : HeVAL(entry);
#if HAS_FULL_NOMG
SvGETMAGIC(value);
#endif
if (ignore_undef_fields && !SvOK(value)) {
continue;
}
if (track_oneof.mark_and_maybe_skip(it->oneof_index)) {
continue;
}
track_seen.mark(it->field_index);
if (!encode_field(state, *it, value))
return false;
}
if (!track_seen.required_fields_present(state.status)) {
return false;
}
state.mapper_context->pop_level();
if (!sink->EndMessage(state.status))
return false;
return true;
}
namespace {
inline bool put_string(Sink *sink, const Mapper::Field &fd, const char *str, STRLEN len) {
Sink sub;
if (!sink->StartString(fd.selector.str_start, len, &sub))
return false;
sub.PutStringBuffer(fd.selector.str_cont, str, len, NULL);
return sink->EndString(fd.selector.str_end);
}
inline bool is_default_string(const Mapper::Field &fd, const char *str, STRLEN len) {
return len == fd.default_str_len &&
(len == 0 || memcmp(str, fd.default_str, len) == 0);
}
}
bool Mapper::encode_field(EncoderState &state, const Field &fd, SV *ref) const {
ValueAction field_action = fd.field_action;
if (fail_ref_coercion && field_action < ACTION_PUT_MESSAGE && is_coerced_ref(aTHX_ state.status, fd, ref))
return false;
Sink *sink = state.sink;
switch (field_action) {
// do not encode field default value
case ACTION_PUT_FLOAT_ND: {
NV value = SvNV_enc(ref);
if (value == fd.default_nv)
return true;
return sink->PutFloat(fd.selector.primitive, value);
}
case ACTION_PUT_DOUBLE_ND: {
NV value = SvNV_enc(ref);
if (value == fd.default_nv)
return true;
return sink->PutDouble(fd.selector.primitive, value);
}
case ACTION_PUT_BOOL_ND: {
bool value = SvTRUE_enc(ref);
if (value == fd.default_bool)
return true;
return sink->PutBool(fd.selector.primitive, value);
}
case ACTION_PUT_STRING_ND: {
STRLEN len;
const char *str = SvPVutf8_enc(ref, len);
return is_default_string(fd, str, len) || put_string(sink, fd, str, len);
}
case ACTION_PUT_BYTES_ND: {
STRLEN len;
const char *str = SvPV_enc(ref, len);
return is_default_string(fd, str, len) || put_string(sink, fd, str, len);
}
case ACTION_PUT_ENUM_ND: {
IV value = SvIV_enc(ref);
if (value == fd.default_iv)
return true;
if (check_enum_values &&
fd.enum_values.find(value) == fd.enum_values.end()) {
state.status->SetFormattedErrorMessage(
"Invalid enumeration value %d for field '%s'",
value,
fd.full_name().c_str()
);
return false;
}
return sink->PutInt32(fd.selector.primitive, value);
}
case ACTION_PUT_INT32_ND: {
IV value = SvIV_enc(ref);
if (value == fd.default_iv)
return true;
return sink->PutInt32(fd.selector.primitive, value);
}
case ACTION_PUT_UINT32_ND: {
UV value = SvUV_enc(ref);
if (value == fd.default_uv)
return true;
return sink->PutUInt32(fd.selector.primitive, value);
}
case ACTION_PUT_INT64_ND: {
int64_t value = sizeof(IV) >= sizeof(int64_t) ? SvIV_enc(ref) : SvIV64_enc(ref);
if (value == fd.default_i64)
return true;
return sink->PutInt64(fd.selector.primitive, value);
}
case ACTION_PUT_UINT64_ND: {
uint64_t value = sizeof(UV) >= sizeof(int64_t) ? SvUV_enc(ref) : SvUV64_enc(ref);
if (value == fd.default_u64)
return true;
return sink->PutUInt64(fd.selector.primitive, value);
}
// encode field default value
case ACTION_PUT_FLOAT:
return sink->PutFloat(fd.selector.primitive, SvNV_enc(ref));
case ACTION_PUT_DOUBLE:
return sink->PutDouble(fd.selector.primitive, SvNV_enc(ref));
case ACTION_PUT_BOOL:
return sink->PutBool(fd.selector.primitive, SvTRUE_enc(ref));
case ACTION_PUT_STRING: {
STRLEN len;
const char *str = SvPVutf8_enc(ref, len);
return put_string(sink, fd, str, len);
}
case ACTION_PUT_BYTES: {
STRLEN len;
const char *str = SvPV_enc(ref, len);
return put_string(sink, fd, str, len);
}
case ACTION_PUT_ENUM: {
IV value = SvIV_enc(ref);
if (check_enum_values &&
fd.enum_values.find(value) == fd.enum_values.end()) {
state.status->SetFormattedErrorMessage(
"Invalid enumeration value %d for field '%s'",
value,
fd.full_name().c_str()
);
return false;
}
return sink->PutInt32(fd.selector.primitive, value);
}
case ACTION_PUT_INT32:
return sink->PutInt32(fd.selector.primitive, SvIV_enc(ref));
case ACTION_PUT_UINT32:
return sink->PutUInt32(fd.selector.primitive, SvUV_enc(ref));
case ACTION_PUT_INT64:
if (sizeof(IV) >= sizeof(int64_t))
return sink->PutInt64(fd.selector.primitive, SvIV_enc(ref));
else
return sink->PutInt64(fd.selector.primitive, SvIV64_enc(ref));
case ACTION_PUT_UINT64:
if (sizeof(UV) >= sizeof(int64_t))
return sink->PutInt64(fd.selector.primitive, SvUV_enc(ref));
else
return sink->PutUInt64(fd.selector.primitive, SvUV64_enc(ref));
// non-scalar fields
case ACTION_PUT_MESSAGE: {
Sink sub;
EncoderState sub_state(state, &sub);
if (!sink->StartSubMessage(fd.selector.msg_start, &sub))
return false;
if (!fd.mapper->encode_message(sub_state, ref))
return false;
return sink->EndSubMessage(fd.selector.msg_end);
}
case ACTION_PUT_FIELDTABLE: {
Sink sub;
EncoderState sub_state(state, &sub);
if (!sink->StartSubMessage(fd.selector.msg_start, &sub))
return false;
if (!fd.mapper->encode_transformed_fieldtable_message(sub_state, ref, fd.encoder_transform))
return false;
return sink->EndSubMessage(fd.selector.msg_end);
}
case ACTION_PUT_MAP:
return encode_from_perl_hash(state, fd, ref);
case ACTION_PUT_REPEATED:
return encode_from_perl_array(state, fd, ref);
default:
return false; // just in case
}
}
bool Mapper::encode_key(EncoderState &state, const Field &fd, const char *key, I32 keylen) const {
Sink *sink = state.sink;
switch (fd.value_action) {
case ACTION_PUT_BOOL: {
// follows what SvTRUE() does for strings
bool bval = keylen > 1 || (keylen == 1 && key[0] != '0');
return sink->PutBool(fd.selector.primitive, bval);
}
case ACTION_PUT_STRING: {
Sink sub;
if (!sink->StartString(fd.selector.str_start, keylen, &sub))
return false;
sub.PutStringBuffer(fd.selector.str_cont, key, keylen, NULL);
return sink->EndString(fd.selector.str_end);
}
case ACTION_PUT_INT32:
return sink->PutInt32(fd.selector.primitive, key_iv(aTHX_ key, keylen));
case ACTION_PUT_UINT32:
return sink->PutUInt32(fd.selector.primitive, key_uv(aTHX_ key, keylen));
case ACTION_PUT_INT64:
return sink->PutInt64(fd.selector.primitive, key_iv(aTHX_ key, keylen));
case ACTION_PUT_UINT64:
return sink->PutUInt64(fd.selector.primitive, key_uv(aTHX_ key, keylen));
default:
return false; // just in case
}
}
bool Mapper::encode_hash_kv(EncoderState &state, const char *key, STRLEN keylen, SV *value) const {
Sink *sink = state.sink;
if (!sink->StartMessage())
return false;
if (!encode_key(state, fields[map_key_index], key, keylen))
return false;
if (!encode_field(state, fields[map_value_index], value))
return false;
if (!sink->EndMessage(state.status))
return false;
return true;
}
bool Mapper::encode_from_perl_hash(EncoderState &state, const Field &fd, SV *ref) const {
#if !HAS_FULL_NOMG
SvGETMAGIC(ref);
#endif
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Not a hash reference when encoding field '%s'", fd.full_name().c_str());
HV *hash = (HV *) SvRV(ref);
Sink *sink = state.sink, repeated;
if (!sink->StartSequence(fd.selector.seq_start, &repeated))
return false;
bool magical = SvMAGICAL((SV *) hash);
hv_iterinit(hash);
MapperContext::Item &mapper_cxt = state.mapper_context->push_level(hash, MapperContext::Hash);
bool need_unicode = fd.mapper->fields[fd.mapper->map_key_index]
.value_action == ACTION_PUT_STRING;
while (HE *entry = hv_iternext(hash)) {
Sink key_value;
EncoderState key_value_state(state, &key_value);
SV *value = UNLIKELY(magical) ? hv_iterval(hash, entry) : HeVAL(entry);
const char *key;
STRLEN keylen;
if (ignore_undef_fields && !SvOK(value))
continue;
if (HeKLEN(entry) == HEf_SVKEY) {
key = SvPVutf8(HeKEY_sv(entry), keylen);
} else {
key = HeKEY(entry);
keylen = HeKLEN(entry);
// For string keys avoid the copy performed by bytes_to_utf8 if
// the string is invariant (ASCII).
//
// For non-string keys do not care much about the actual encoding, as
// they are either invariant or invalid.
if (!HeKUTF8(entry) && need_unicode &&
!is_invariant_string((const U8 *) key, keylen)) {
key = (const char *) bytes_to_utf8((U8*) HeKEY(entry), &keylen);
SAVEFREEPV(key);
}
}
mapper_cxt.set_hash_key(key, keylen);
#if HAS_FULL_NOMG
SvGETMAGIC(value);
#endif
if (!repeated.StartSubMessage(fd.selector.msg_start, &key_value))
return false;
if (!fd.mapper->encode_hash_kv(key_value_state, key, keylen, value))
return false;
if (!repeated.EndSubMessage(fd.selector.msg_end))
return false;
}
state.mapper_context->pop_level();
return sink->EndSequence(fd.selector.seq_end);
}
bool Mapper::encode_from_perl_array(EncoderState &state, const Field &fd, SV *ref) const {
#if !HAS_FULL_NOMG
SvGETMAGIC(ref);
#endif
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVAV)
croak("Not an array reference when encoding field '%s'", fd.full_name().c_str());
AV *array = (AV *) SvRV(ref);
switch (fd.value_action) {
case ACTION_PUT_FLOAT:
return encode_from_array<NVGetter, FloatEmitter>(state, fd, array);
case ACTION_PUT_DOUBLE:
return encode_from_array<NVGetter, DoubleEmitter>(state, fd, array);
case ACTION_PUT_BOOL:
return encode_from_array<BoolGetter, BoolEmitter>(state, fd, array);
case ACTION_PUT_STRING:
return encode_from_array<SVGetter, StringEmitter>(state, fd, array);
case ACTION_PUT_BYTES:
return encode_from_array<SVGetter, StringEmitter>(state, fd, array);
case ACTION_PUT_MESSAGE:
return fd.mapper->encode_from_message_array(state, fd, array);
case ACTION_PUT_ENUM:
if (check_enum_values)
return encode_from_array<IVGetter, EnumEmitter>(state, fd, array);
else
return encode_from_array<IVGetter, Int32Emitter>(state, fd, array);
case ACTION_PUT_INT32:
return encode_from_array<IVGetter, Int32Emitter>(state, fd, array);
case ACTION_PUT_UINT32:
return encode_from_array<UVGetter, UInt32Emitter>(state, fd, array);
case ACTION_PUT_INT64:
if (sizeof(IV) >= sizeof(int64_t))
return encode_from_array<IVGetter, Int64Emitter>(state, fd, array);
else
return encode_from_array<I64Getter, Int64Emitter>(state, fd, array);
case ACTION_PUT_UINT64:
if (sizeof(IV) >= sizeof(int64_t))
return encode_from_array<UVGetter, UInt64Emitter>(state, fd, array);
else
return encode_from_array<U64Getter, UInt64Emitter>(state, fd, array);
default:
return false; // just in case
}
}
bool Mapper::check_from_message_array(Status *status, const Mapper::Field &fd, AV *source) const {
int size = av_top_index(source) + 1;
for (int i = 0; i < size; ++i) {
SV **item = av_fetch(source, i, 0);
if (!item)
return false;
SvGETMAGIC(*item);
if (!check(status, *item))
return false;
}
return true;
}
bool Mapper::check_from_enum_array(Status *status, const Mapper::Field &fd, AV *source) const {
int size = av_top_index(source) + 1;
for (int i = 0; i < size; ++i) {
SV **item = av_fetch(source, i, 0);
if (!item)
return false;
IV value = SvIV(*item);
if (fd.enum_values.find(value) == fd.enum_values.end()) {
status->SetFormattedErrorMessage(
"Invalid enumeration value %d for field '%s'",
value,
fd.full_name().c_str()
);
return false;
}
}
return true;
}
bool Mapper::check(Status *status, SV *ref) const {
SvGETMAGIC(ref);
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Not a hash reference when checking a %s value", message_def->full_name());
HV *hv = (HV *) SvRV(ref);
I32 count = hv_iterinit(hv);
bool ok = true;
for (int i = 0; i < count; ++i) {
char *key;
I32 keylen;
SV *value = hv_iternextsv(hv, &key, &keylen);
const Field *field = field_map.find_by_name(aTHX_ key, keylen);
if (field == NULL) {
status->SetFormattedErrorMessage(
"Unknown field '%.*s' during check",
keylen, key);
return false;
}
if (field->field_def->label() == UPB_LABEL_REPEATED)
ok = ok && check_from_perl_array(status, *field, value);
else
ok = ok && check(status, *field, value);
}
return ok;
}
bool Mapper::check(Status *status, const Field &fd, SV *ref) const {
switch (fd.value_action) {
case ACTION_PUT_MESSAGE:
return fd.mapper->check(status, ref);
case ACTION_PUT_ENUM: {
if (!check_enum_values)
return true;
IV value = SvIV(ref);
if (fd.enum_values.find(value) == fd.enum_values.end()) {
status->SetFormattedErrorMessage(
"Invalid enumeration value %d for field '%s'",
value,
fd.full_name().c_str()
);
return false;
}
return true;
}
default:
// I doubt there is any point in performing "strict" type checks
// on scalar values, due to coercion
return true;
}
}
bool Mapper::check_from_perl_array(Status *status, const Field &fd, SV *ref) const {
SvGETMAGIC(ref);
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVAV)
croak("Not an array reference when encoding field '%s'", fd.full_name().c_str());
AV *array = (AV *) SvRV(ref);
switch (fd.value_action) {
case ACTION_PUT_MESSAGE:
return fd.mapper->check_from_message_array(status, fd, array);
case ACTION_PUT_ENUM:
if (check_enum_values)
return check_from_enum_array(status, fd, array);
else
return true;
default:
// I doubt there is any point in performing "strict" type checks
// on scalar values, due to coercion
return true;
}
}
void Mapper::apply_default(const Field &field, SV *target) const {
switch (field.field_def->type()) {
case UPB_TYPE_FLOAT:
sv_setnv(target, field.field_def->default_float());
break;
case UPB_TYPE_DOUBLE:
sv_setnv(target, field.field_def->default_double());
break;
case UPB_TYPE_BOOL:
set_bool(target, field.field_def->default_bool());
break;
case UPB_TYPE_BYTES:
case UPB_TYPE_STRING: {
size_t len;
const char *val = field.field_def->default_string(&len);
if (!val) {
val = "";
len = 0;
}
sv_setpvn(target, val, len);
if (field.field_def->type() == UPB_TYPE_STRING)
SvUTF8_on(target);
}
break;
case UPB_TYPE_ENUM:
case UPB_TYPE_INT32:
sv_setiv(target, field.field_def->default_int32());
break;
case UPB_TYPE_UINT32:
sv_setuv(target, field.field_def->default_uint32());
break;
case UPB_TYPE_INT64:
sv_setiv(target, field.field_def->default_int64());
break;
case UPB_TYPE_UINT64:
sv_setuv(target, field.field_def->default_uint64());
break;
case UPB_TYPE_MESSAGE:
// no default for messages
break;
}
}
void Mapper::apply_map_value_default(SV *target) const {
apply_default(fields[map_value_index], target);
}
MapperField::MapperField(pTHX_ const Mapper *_mapper, const Mapper::Field *_field) :
field(_field),
mapper(_mapper) {
SET_THX_MEMBER;
mapper->ref();
}
MapperField::~MapperField() {
mapper->unref();
}
MapperField *MapperField::find_extension(pTHX_ CV *cv, SV *extension) {
const Mapper *mapper = (const Mapper *) CvXSUBANY(cv).any_ptr;
STRLEN len;
const char *buffer = SvPV(extension, len);
// ignore square brackets at beginning and end
if (len > 2 && buffer[0] == '[' && buffer[len - 1] == ']') {
len -= 2;
buffer += 1;
}
string extension_name(buffer, len);
MapperField *mapper_field = mapper->find_extension(extension_name);
if (!mapper_field)
croak("Unknown extension field '%s' for message '%s'", extension_name.c_str(), mapper->full_name());
return mapper_field;
}
MapperField *MapperField::find_scalar_extension(pTHX_ CV *cv, SV *extension) {
MapperField *mf = find_extension(aTHX_ cv, extension);
if (mf && mf->is_repeated())
croak("Extension field '%s' is a repeated field", mf->field->full_name().c_str());
return mf;
}
MapperField *MapperField::find_repeated_extension(pTHX_ CV *cv, SV *extension) {
MapperField *mf = find_extension(aTHX_ cv, extension);
if (mf && !mf->is_repeated())
croak("Extension field '%s' is a non-repeated field", mf->field->full_name().c_str());
return mf;
}
SV *MapperField::get_read_field(HV *self) {
HE *ent = hv_fetch_ent(self, field->name, 0, field->name_hash);
return ent ? HeVAL(ent) : NULL;
}
SV *MapperField::get_write_field(HV *self) {
HE *ent = hv_fetch_ent(self, field->name, 1, field->name_hash);
return HeVAL(ent);
}
SV *MapperField::get_read_array_ref(HV *self) {
HE *ent = hv_fetch_ent(self, field->name, 0, field->name_hash);
if (!ent)
return NULL;
SV *ref = HeVAL(ent);
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVAV)
croak("Value of field '%s' is not an array reference", field->full_name().c_str());
return ref;
}
AV *MapperField::get_read_array(HV *self) {
SV *ref = get_read_array_ref(self);
return ref ? (AV *) SvRV(ref) : NULL;
}
AV *MapperField::get_write_array(HV *self) {
HE *ent = hv_fetch_ent(self, field->name, 1, field->name_hash);
SV *ref = HeVAL(ent);
if (!SvOK(ref)) {
AV *av = newAV();
SvUPGRADE(ref, SVt_RV);
SvROK_on(ref);
SvRV_set(ref, (SV *) av);
return av;
} else {
SV *ref = HeVAL(ent);
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVAV)
croak("Value of field '%s' is not an array reference", field->full_name().c_str());
return (AV *) SvRV(ref);
}
}
SV *MapperField::get_read_hash_ref(HV *self) {
HE *ent = hv_fetch_ent(self, field->name, 0, field->name_hash);
if (!ent)
return NULL;
SV *ref = HeVAL(ent);
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Value of field '%s' is not a hash reference", field->full_name().c_str());
return ref;
}
HV *MapperField::get_read_hash(HV *self) {
SV *ref = get_read_hash_ref(self);
return ref ? (HV *) SvRV(ref) : NULL;
}
HV *MapperField::get_write_hash(HV *self) {
HE *ent = hv_fetch_ent(self, field->name, 1, field->name_hash);
SV *ref = HeVAL(ent);
if (!SvOK(ref)) {
HV *hv = newHV();
SvUPGRADE(ref, SVt_RV);
SvROK_on(ref);
SvRV_set(ref, (SV *) hv);
return hv;
} else {
SV *ref = HeVAL(ent);
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Value of field '%s' is not a hash reference", field->full_name().c_str());
return (HV *) SvRV(ref);
}
}
const char *MapperField::name() {
return field->field_def->name();
}
bool MapperField::is_repeated() {
return field->field_def->label() == UPB_LABEL_REPEATED;
}
bool MapperField::is_extension() {
return field->field_def->is_extension();
}
bool MapperField::is_map() {
return field->is_map;
}
bool MapperField::has_field(HV *self) {
return hv_fetch_ent(self, field->name, 0, field->name_hash);
}
void MapperField::clear_field(HV *self) {
hv_delete_ent(self, field->name, G_DISCARD, field->name_hash);
}
SV *MapperField::get_scalar(HV *self, SV *target) {
SV *value = get_read_field(self);
if (value) {
return value;
} else {
copy_default(target);
return target;
}
}
void MapperField::copy_default(SV *target) {
const FieldDef *field_def = field->field_def;
switch (field_def->type()) {
case UPB_TYPE_FLOAT:
sv_setnv(target, field_def->default_float());
break;
case UPB_TYPE_DOUBLE:
sv_setnv(target, field_def->default_double());
break;
case UPB_TYPE_BOOL:
mapper->set_bool(target, field_def->default_bool());
break;
case UPB_TYPE_STRING: {
size_t len;
const char *str = field_def->default_string(&len);
sv_setpvn(target, str, len);
SvUTF8_on(target);
}
break;
case UPB_TYPE_BYTES: {
size_t len;
const char *str = field_def->default_string(&len);
sv_setpvn(target, str, len);
}
break;
case UPB_TYPE_MESSAGE:
sv_setsv(target, &PL_sv_undef);
break;
case UPB_TYPE_ENUM: {
sv_setiv(target, field_def->default_int32());
}
break;
case UPB_TYPE_INT32:
sv_setiv(target, field_def->default_int32());
break;
case UPB_TYPE_UINT32:
sv_setuv(target, field_def->default_uint32());
break;
case UPB_TYPE_INT64: {
if (sizeof(IV) >= sizeof(int64_t))
sv_setiv(target, field_def->default_int64());
else {
int64_t i64 = field_def->default_int64();
set_bigint(aTHX_ target, (uint64_t) i64, i64 < 0);
}
}
break;
case UPB_TYPE_UINT64: {
if (sizeof(IV) >= sizeof(uint64_t))
sv_setuv(target, field_def->default_uint64());
else {
int64_t u64 = field_def->default_uint64();
set_bigint(aTHX_ target, u64, false);
}
}
break;
default:
croak("Unhandled field type %d for field '%s'", field->field_def->type(), field->full_name().c_str());
}
}
void MapperField::clear_oneof(HV *self) {
for (int i = 0, max = mapper->field_count(); i < max; ++i) {
const Mapper::Field *other = mapper->get_field(i);
if (other->oneof_index != field->oneof_index || other == field)
continue;
hv_delete_ent(self, other->name, G_DISCARD, other->name_hash);
}
}
void MapperField::set_scalar(HV *self, SV *value) {
if (field->oneof_index != -1)
clear_oneof(self);
SV *target = get_write_field(self);
copy_value(target, value);
}
void MapperField::copy_value(SV *target, SV *value) {
const FieldDef *field_def = field->field_def;
switch (field->is_map ? field->map_value_type() : field_def->type()) {
case UPB_TYPE_FLOAT:
sv_setnv(target, SvNV(value));
break;
case UPB_TYPE_DOUBLE:
sv_setnv(target, SvNV(value));
break;
case UPB_TYPE_BOOL:
mapper->set_bool(target, SvTRUE(value));
break;
case UPB_TYPE_STRING: {
STRLEN len;
const char *str = SvPVutf8(value, len);
sv_setpvn(target, str, len);
SvUTF8_on(target);
}
break;
case UPB_TYPE_BYTES: {
STRLEN len;
const char *str = SvPV(value, len);
sv_setpvn(target, str, len);
}
break;
case UPB_TYPE_MESSAGE:
if (SvOK(value) && (!SvROK(value) || SvTYPE(SvRV(value)) != SVt_PVHV))
croak("Value for message field '%s' is not a hash reference", field->full_name().c_str());
sv_setsv(target, value);
break;
case UPB_TYPE_ENUM: {
I32 i32 = SvIV(value);
const unordered_set<int32_t> &enum_values = field->is_map ?
field->map_enum_values() :
field->enum_values;
if (enum_values.size() &&
enum_values.find(i32) == field->enum_values.end())
croak("Invalid value %d for enumeration field '%s'", i32, field->full_name().c_str());
sv_setiv(target, i32);
}
break;
case UPB_TYPE_INT32:
sv_setiv(target, SvIV(value));
break;
case UPB_TYPE_UINT32:
sv_setuv(target, SvUV(value));
break;
case UPB_TYPE_INT64: {
if (sizeof(IV) >= sizeof(int64_t))
sv_setiv(target, SvIV(value));
else {
int64_t i64 = SvIV64(value);
set_bigint(aTHX_ target, (uint64_t) i64, i64 < 0);
}
}
break;
case UPB_TYPE_UINT64: {
if (sizeof(IV) >= sizeof(uint64_t))
sv_setuv(target, SvUV(value));
else {
int64_t u64 = SvUV64(value);
set_bigint(aTHX_ target, u64, false);
}
}
break;
default:
croak("Unhandled field type %d for field '%s'", field->field_def->type(), field->full_name().c_str());
}
}
SV *MapperField::get_item(HV *self, int index, SV *target) {
AV *array = get_read_array(self);
if (!array)
croak("Accessing unset array field '%s'", field->full_name().c_str());
int max = av_top_index(array);
if (max == -1)
croak("Accessing empty array field '%s'", field->full_name().c_str());
if (index > max || index < -(max + 1))
croak("Accessing out-of-bounds index %d for field '%s'", index, field->full_name().c_str());
SV **value = av_fetch(array, index, 0);
if (value) {
return *value;
} else {
copy_default(target);
return target;
}
}
SV *MapperField::get_item(HV *self, SV *key, SV *target) {
HV *hash = get_read_hash(self);
if (!hash)
croak("Accessing unset map field '%s'", field->full_name().c_str());
if (HvTOTALKEYS(hash) == 0)
croak("Accessing empty map field '%s'", field->full_name().c_str());
HE *value = hv_fetch_ent(hash, key, 0, 0);
if (value) {
return HeVAL(value);
} else {
croak("Accessing non-existing key '%s' for field '%s'", SvPV_nolen(key), field->full_name().c_str());
}
}
void MapperField::set_item(HV *self, int index, SV *value) {
AV *array = get_write_array(self);
SV **target = av_fetch(array, index, 1);
copy_value(*target, value ? value : NULL);
}
void MapperField::set_item(HV *self, SV *key, SV *value) {
HV *hash = get_write_hash(self);
HE *target = hv_fetch_ent(hash, key, 1, 0);
copy_value(HeVAL(target), value ? value : NULL);
}
void MapperField::add_item(HV *self, SV *value) {
AV *array = get_write_array(self);
SV **target = av_fetch(array, av_top_index(array) + 1, 1);
copy_value(*target, value ? value : NULL);
}
int MapperField::list_size(HV *self) {
AV *array = get_read_array(self);
if (!array)
return 0;
return av_top_index(array) + 1;
}
SV *MapperField::get_list(HV *self) {
SV *array_ref = get_read_array_ref(self);
return array_ref ? array_ref : &PL_sv_undef;
}
void MapperField::set_list(HV *self, SV *ref) {
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVAV)
croak("Value for field '%s' is not an array reference", field->full_name().c_str());
SV *field_ref = get_write_field(self);
if (!SvOK(field_ref)) {
SvUPGRADE(field_ref, SVt_RV);
SvROK_on(field_ref);
} else {
if (!SvROK(field_ref))
croak("Value of field '%s' is not a reference", field->full_name().c_str());
SvREFCNT_dec(SvRV(field_ref));
}
SvRV_set(field_ref, SvRV(ref));
SvREFCNT_inc(SvRV(field_ref));
}
SV *MapperField::get_map(HV *self) {
SV *hash_ref = get_read_hash_ref(self);
return hash_ref ? hash_ref : &PL_sv_undef;
}
void MapperField::set_map(HV *self, SV *ref) {
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Value for field '%s' is not a hash reference", field->full_name().c_str());
SV *field_ref = get_write_field(self);
if (!SvOK(field_ref)) {
SvUPGRADE(field_ref, SVt_RV);
SvROK_on(field_ref);
} else {
if (!SvROK(field_ref))
croak("Value of field '%s' is not a reference", field->full_name().c_str());
SvREFCNT_dec(SvRV(field_ref));
}
SvRV_set(field_ref, SvRV(ref));
SvREFCNT_inc(SvRV(field_ref));
}
MethodMapper::MethodMapper(pTHX_ Dynamic *_registry, const string &method, const MessageDef *_input_def, const MessageDef *_output_def, bool client_streaming, bool server_streaming) :
registry(_registry),
input_def(_input_def),
output_def(_output_def) {
SET_THX_MEMBER;
registry->ref();
method_name_key_sv = newSVpvs_share("method");
serialize_key_sv = newSVpvs_share("serialize");
deserialize_key_sv = newSVpvs_share("deserialize");
method_name_sv = newSVpvn_share(method.data(), method.length(), 0);
serialize_sv = NULL;
deserialize_sv = NULL;
const char *grpc_name;
if (client_streaming) {
if (server_streaming) {
grpc_name = "Grpc::Client::BaseStub::_bidiRequest";
} else {
grpc_name = "Grpc::Client::BaseStub::_clientStreamRequest";
}
} else {
if (server_streaming) {
grpc_name = "Grpc::Client::BaseStub::_serverStreamRequest";
} else {
grpc_name = "Grpc::Client::BaseStub::_simpleRequest";
}
}
grpc_call_sv = get_cv(grpc_name, 0);
if (grpc_call_sv == NULL)
croak("Unable to resolve function '%s'", grpc_name);
}
MethodMapper::~MethodMapper() {
SvREFCNT_dec(method_name_key_sv);
SvREFCNT_dec(serialize_key_sv);
SvREFCNT_dec(deserialize_key_sv);
SvREFCNT_dec(method_name_sv);
SvREFCNT_dec(serialize_sv);
SvREFCNT_dec(deserialize_sv);
SvREFCNT_dec(grpc_call_sv);
// make sure this only goes away after inner destructors have completed
refcounted_mortalize(aTHX_ registry);
}
void MethodMapper::resolve_input_output() {
const Mapper *request_mapper = registry->find_mapper(input_def);
const Mapper *response_mapper = registry->find_mapper(output_def);
string request_encoder_name =
request_mapper->package_name()
+ string("::_static_encode");
SV *encode = (SV *) get_cv(request_encoder_name.c_str(), 0);
if (encode == NULL)
croak("Unable to find GRPC encoder in package '%s' for message '%s'",
request_mapper->package_name(), request_mapper->full_name());
serialize_sv = newRV_inc(encode);
string response_decoder_name =
response_mapper->package_name()
+ string("::_static_decode");
SV *decode = (SV *) get_cv(response_decoder_name.c_str(), 0);
if (decode == NULL)
croak("Unable to find GRPC decoder in package '%s' for message '%s'",
response_mapper->package_name(), response_mapper->full_name());
deserialize_sv = newRV_inc(decode);
}
WarnContext::WarnContext(pTHX) : chained_handler(NULL) {
CV *handler = get_cv("Google::ProtocolBuffers::Dynamic::Mapper::handle_warning", 0);
warn_handler = (SV*) handler;
}
void WarnContext::setup(pTHX) {
CV *handler = get_cv("Google::ProtocolBuffers::Dynamic::Mapper::handle_warning", 0);
CvXSUBANY(handler).any_ptr = new WarnContext(aTHX);
}
WarnContext *WarnContext::get(pTHX) {
CV *handler = get_cv("Google::ProtocolBuffers::Dynamic::Mapper::handle_warning", 0);
return (WarnContext *) CvXSUBANY(handler).any_ptr;
}
void WarnContext::localize_warning_handler(pTHX) {
chained_handler = PL_warnhook;
SAVEGENERICSV(PL_warnhook);
PL_warnhook = SvREFCNT_inc_simple_NN(warn_handler);
}
namespace {
void concat_key(pTHX_ SV *sv, const MapperContext::ExternalItem *key, bool is_hash) {
if (is_hash)
sv_catpvs(sv, "{");
if (key->hash_item.svkey) {
sv_catsv(sv, key->hash_item.svkey);
} else if (key->hash_item.keybuf) {
sv_catpvn(sv, key->hash_item.keybuf, key->hash_item.keylen);
} else {
sv_catpvs(sv, "<message>.");
}
if (is_hash)
sv_catpvs(sv, "}.");
else
sv_catpvs(sv, ".");
}
}
void WarnContext::warn_with_context(pTHX_ SV *warning) const {
SV *cxt = sv_2mortal(newSVpvs("While encoding field '"));
int mapper_context_size = 0;
const gpd::MapperContext::ExternalItem *const *mapper_context = NULL;
context->fill_context(&mapper_context, &mapper_context_size);
for (int i = 0; i < mapper_context_size; ++i) {
const gpd::MapperContext::ExternalItem *item = mapper_context[i];
switch (item->kind) {
case MapperContext::Array:
sv_catpvf(cxt, "[%d].", item->array_item.index);
break;
case MapperContext::Hash:
concat_key(aTHX_ cxt, item, true);
break;
case MapperContext::Message:
concat_key(aTHX_ cxt, item, false);
break;
}
}
SvCUR_set(cxt, SvCUR(cxt) - 1); // chop last '.'
sv_catpvs(cxt, "': ");
sv_catsv(cxt, warning);
if (chained_handler) {
dSP;
PUSHMARK(SP);
XPUSHs(cxt);
PUTBACK;
call_sv(chained_handler, G_DISCARD|G_VOID);
} else
warn_sv(cxt);
}