#include "mapper.h"
#include "dynamic.h"
#undef New
#include <upb/pb/encoder.h>
#include <upb/pb/decoder.h>
using namespace gpd;
using namespace std;
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
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) {
SET_THX_MEMBER;
mappers.push_back(mapper);
}
void Mapper::DecoderHandlers::prepare(HV *target) {
mappers.resize(1);
seen_fields.resize(1);
seen_fields.back().clear();
seen_fields.back().resize(mappers.back()->fields.size());
if (int oneof_count = mappers.back()->message_def->oneof_count()) {
seen_oneof.resize(1);
seen_oneof.back().clear();
seen_oneof.back().resize(oneof_count, -1);
}
items.resize(1);
error.clear();
items[0] = (SV *) target;
string = NULL;
}
SV *Mapper::DecoderHandlers::get_target() {
return items[0];
}
void Mapper::DecoderHandlers::clear() {
SvREFCNT_dec(items[0]);
}
namespace {
#if PERL_VERSION < 18
inline SSize_t GPD_av_top_index(pTHX_ AV *av) {
return AvFILL(av);
}
#define av_top_index(av) GPD_av_top_index(aTHX_ (av))
#define READ_XDIGIT(s) ((0xf & (isDIGIT(*(s)) \
? (*(s)++) \
: (*(s)++ + 9))))
#endif
#if PERL_VERSION < 14
inline void GPD_warn_sv(pTHX_ SV *mess) {
warn("%" SVf, mess);
}
#define warn_sv(sv) GPD_warn_sv(aTHX_ (sv))
#endif
inline void set_bool(pTHX_ SV *target, bool value) {
if (value)
sv_setiv(target, 1);
else
sv_setpvn(target, "", 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 {
const vector<Field> &map_fields = mapper->fields;
return map_fields[1].is_value ?
map_fields[1].field_def->type() :
map_fields[0].field_def->type();
}
const STD_TR1::unordered_set<int32_t> &Mapper::Field::map_enum_values() const {
const vector<Field> &map_fields = mapper->fields;
return map_fields[1].is_value ?
map_fields[1].enum_values :
map_fields[0].enum_values;
}
bool Mapper::DecoderHandlers::apply_defaults_and_check() {
const vector<bool> &seen = seen_fields.back();
const Mapper *mapper = mappers.back();
const vector<Mapper::Field> &fields = mapper->fields;
bool decode_explict_defaults = mapper->decode_explicit_defaults;
bool check_required_fields = mapper->check_required_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_explict_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;
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(aTHX_ 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;
}
} 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 (!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);
// if length of the string is zero initialize it with empty string
if (size_hint == 0)
sv_setpvn(cxt->string, "", 0);
return cxt;
}
size_t Mapper::DecoderHandlers::on_string(DecoderHandlers *cxt, const int *field_index, const char *buf, size_t len) {
THX_DECLARE_AND_GET;
if (!SvOK(cxt->string))
sv_setpvn(cxt->string, buf, len);
else
sv_catpvn(cxt->string, buf, 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;
}
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_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_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->mappers.push_back(mapper->fields[*field_index].mapper);
cxt->items.push_back((SV *) hv);
cxt->items.push_back(sv_newmortal());
cxt->items.push_back(NULL);
return cxt;
}
bool Mapper::DecoderHandlers::on_end_map(DecoderHandlers *cxt, const int *field_index) {
cxt->mappers.pop_back();
cxt->items.pop_back();
cxt->items.pop_back();
cxt->items.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();
SV *target = cxt->get_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->items.push_back((SV *) hv);
cxt->mappers.push_back(mapper->fields[*field_index].mapper);
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()->message_def->oneof_count()) {
cxt->seen_oneof.resize(cxt->seen_oneof.size() + 1);
cxt->seen_oneof.back().resize(oneof_count, -1);
}
sv_bless(target, cxt->mappers.back()->stash);
return cxt;
}
bool Mapper::DecoderHandlers::on_end_sub_message(DecoderHandlers *cxt, const int *field_index) {
cxt->items.pop_back();
cxt->mappers.pop_back();
cxt->seen_fields.pop_back();
if (cxt->mappers.back()->message_def->oneof_count())
cxt->seen_oneof.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 - 3];
SV *key = (SV *) cxt->items[size - 2];
SV *value = (SV *) cxt->items[size - 1];
if (SvOK(key) && value) {
SvREFCNT_inc(value);
hv_store_ent(hash, key, value, 0);
if (SvPOK(key))
SvLEN_set(key, 0);
} else {
// having decoding of broken maps succeed is debatable
warn("Incomplete map entry: missing %s",
(!SvOK(key) && !value) ? "both key and value" :
!SvOK(key) ? "key" :
"value");
}
SvOK_off(key);
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;
int count = 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_bool(DecoderHandlers *cxt, const int *field_index, bool val) {
THX_DECLARE_AND_GET;
cxt->mark_seen(field_index);
set_bool(aTHX_ 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];
SV *curr = items.back();
if (field.is_key) {
return items[items.size() - 2];
} else if (field.is_value) {
SV *sv = sv_newmortal();
items[items.size() - 1] = sv;
return sv;
} else if (SvTYPE(curr) == SVt_PVAV) {
AV *av = (AV *) curr;
return *av_fetch(av, av_top_index(av) + 1, 1);
} else {
HV *hv = (HV *) curr;
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));
}
}
void Mapper::DecoderHandlers::mark_seen(const int *field_index) {
seen_fields.back()[*field_index] = true;
}
Mapper::Mapper(pTHX_ Dynamic *_registry, const MessageDef *_message_def, HV *_stash, const MappingOptions &options) :
registry(_registry),
message_def(_message_def),
stash(_stash),
decoder_callbacks(aTHX_ this),
string_sink(&output_buffer) {
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);
decode_explicit_defaults = options.explicit_defaults;
encode_defaults = message_def->syntax() == UPB_SYNTAX_PROTO2 &&
options.encode_defaults;
check_enum_values = options.check_enum_values;
warn_context = WarnContext::get(aTHX);
if (!decoder_handlers->SetEndMessageHandler(UpbMakeHandler(DecoderHandlers::on_end_message)))
croak("Unable to set upb end message handler for %s", message_def->full_name());
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();
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.oneof_index = -1;
if (map_entry) {
field.is_key = field_def->number() == 1;
field.is_value = field_def->number() == 2;
}
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();
break;
case UPB_TYPE_DOUBLE:
GET_SELECTOR(DOUBLE, primitive);
SET_VALUE_HANDLER(double, on_nv<double>);
field.default_nv = field_def->default_double();
break;
case UPB_TYPE_BOOL:
GET_SELECTOR(BOOL, primitive);
SET_VALUE_HANDLER(bool, on_bool);
field.default_bool = field_def->default_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_string);
SET_HANDLER(EndString, on_end_string);
field.default_str = field_def->default_string(&field.default_str_len);
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;
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();
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();
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();
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();
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();
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);
} else {
SET_HANDLER(StartSequence, on_start_sequence);
SET_HANDLER(EndSequence, on_end_sequence);
}
}
#undef GET_SELECTOR
#undef SET_VALUE_HANDLER
#undef SET_HANDLER
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[SvPV_nolen(it->name)] = &*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[field_def->index()];
field.oneof_index = oneof_index;
}
}
check_required_fields = has_required && options.check_required_fields;
}
Mapper::~Mapper() {
for (vector<Field>::iterator it = fields.begin(), en = fields.end(); it != en; ++it)
if (it->mapper)
it->mapper->unref();
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();
// make sure this only goes away after inner destructors have completed
refcounted_mortalize(aTHX_ registry);
SvREFCNT_dec(stash);
}
const char *Mapper::full_name() const {
return message_def->full_name();
}
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 an 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());
}
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);
}
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(), string_sink.input());
status.Clear();
output_buffer.clear();
warn_context->clear();
warn_context->localize_warning_handler(aTHX);
SV *result = NULL;
if (encode(pb_encoder->input(), &status, ref))
result = newSVpvn(output_buffer.data(), output_buffer.size());
output_buffer.clear();
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(), string_sink.input());
status.Clear();
output_buffer.clear();
warn_context->clear();
warn_context->localize_warning_handler(aTHX);
SV *result = NULL;
if (encode(json_encoder->input(), &status, ref))
result = newSVpvn(output_buffer.data(), output_buffer.size());
output_buffer.clear();
return result;
}
SV *Mapper::decode(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 = sv_bless(newRV_inc(decoder_callbacks.get_target()), stash);
decoder_callbacks.clear();
return 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(), &decoder_sink);
status.Clear();
decoder_callbacks.prepare(newHV());
SV *result = NULL;
if (BufferSource::PutBuffer(buffer, bufsize, json_decoder->input()))
result = sv_bless(newRV_inc(decoder_callbacks.get_target()), stash);
decoder_callbacks.clear();
return result;
}
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;
int count = 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;
}
uint64_t get_uint64(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);
}
int64_t get_int64(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);
}
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;
}
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(src); }
};
class IVGetter {
public:
IV operator()(pTHX_ SV *src) const { return SvIV(src); }
};
class UVGetter {
public:
UV operator()(pTHX_ SV *src) const { return SvUV(src); }
};
class I64Getter {
public:
int64_t operator()(pTHX_ SV *src) const { return get_int64(aTHX_ src); }
};
class U64Getter {
public:
uint64_t operator()(pTHX_ SV *src) const { return get_uint64(aTHX_ src); }
};
class StringGetter {
public:
void operator()(pTHX_ SV *src, string *dest) const {
STRLEN len;
const char *buf = SvPVutf8(src, len);
dest->assign(buf, len);
}
};
class BytesGetter {
public:
void operator()(pTHX_ SV *src, string *dest) const {
STRLEN len;
const char *buf = SvPV(src, len);
dest->assign(buf, len);
}
};
class BoolGetter {
public:
bool operator()(pTHX_ SV *src) const { return SvTRUE(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.field_def->type() == UPB_TYPE_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(Sink *sink, Status *status, const Mapper::Field &fd, AV *source) const {
G getter;
S setter(status);
Sink sub;
if (!sink->StartSequence(fd.selector.seq_start, &sub))
return false;
int size = av_top_index(source) + 1;
WarnContext::Item &warn_cxt = warn_context->push_level(WarnContext::Array);
for (int i = 0; i < size; ++i) {
warn_cxt.index = i;
SV **item = av_fetch(source, i, 0);
if (!item)
return false;
if (!setter(aTHX_ &sub, fd, getter(aTHX_ *item)))
return false;
}
warn_context->pop_level();
return sink->EndSequence(fd.selector.seq_end);
}
bool Mapper::encode_from_message_array(Sink *sink, Status *status, const Mapper::Field &fd, AV *source) const {
int size = av_top_index(source) + 1;
Sink sub;
if (!sink->StartSequence(fd.selector.seq_start, &sub))
return false;
WarnContext::Item &warn_cxt = warn_context->push_level(WarnContext::Array);
for (int i = 0; i < size; ++i) {
warn_cxt.index = i;
SV **item = av_fetch(source, i, 0);
if (!item)
return false;
Sink submsg;
SvGETMAGIC(*item);
if (!sub.StartSubMessage(fd.selector.msg_start, &submsg))
return false;
if (!encode(&submsg, status, *item))
return false;
if (!sub.EndSubMessage(fd.selector.msg_end))
return false;
}
warn_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);
}
}
bool Mapper::encode(Sink *sink, Status *status, SV *ref) const {
SvGETMAGIC(ref);
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Not an hash reference when encoding a %s value", message_def->full_name());
HV *hv = (HV *) SvRV(ref);
if (!sink->StartMessage())
return false;
bool tied = SvTIED_mg((SV *) hv, PERL_MAGIC_tied);
bool ok = true;
WarnContext::Item &warn_cxt = warn_context->push_level(WarnContext::Message);
vector<bool> seen_oneof;
seen_oneof.resize(message_def->oneof_count());
for (vector<Field>::const_iterator it = fields.begin(), en = fields.end(); it != en; ++it) {
warn_cxt.field = &*it;
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);
if (!he) {
if (it->field_def->label() == UPB_LABEL_REQUIRED) {
status->SetFormattedErrorMessage(
"Missing required field '%s'",
it->full_name().c_str());
return false;
} else
continue;
} else if (it->oneof_index != -1) {
if (seen_oneof[it->oneof_index])
continue;
seen_oneof[it->oneof_index] = true;
}
if (it->is_map)
ok = ok && encode_from_perl_hash(sink, status, *it, HeVAL(he));
else if (it->field_def->label() == UPB_LABEL_REPEATED)
ok = ok && encode_from_perl_array(sink, status, *it, HeVAL(he));
else if (encode_defaults || !it->has_default)
ok = ok && encode(sink, status, *it, HeVAL(he));
else
ok = ok && encode_nodefaults(sink, status, *it, HeVAL(he));
}
warn_context->pop_level();
if (!sink->EndMessage(status))
return false;
return ok;
}
bool Mapper::encode(Sink *sink, Status *status, const Field &fd, SV *ref) const {
switch (fd.field_def->type()) {
case UPB_TYPE_FLOAT:
return sink->PutFloat(fd.selector.primitive, SvNV(ref));
case UPB_TYPE_DOUBLE:
return sink->PutDouble(fd.selector.primitive, SvNV(ref));
case UPB_TYPE_BOOL:
return sink->PutBool(fd.selector.primitive, SvTRUE(ref));
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
STRLEN len;
const char *str = fd.field_def->type() == UPB_TYPE_STRING ? SvPVutf8(ref, len) : SvPV(ref, 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);
}
case UPB_TYPE_MESSAGE: {
Sink sub;
if (!sink->StartSubMessage(fd.selector.msg_start, &sub))
return false;
if (!fd.mapper->encode(&sub, status, ref))
return false;
return sink->EndSubMessage(fd.selector.msg_end);
}
case UPB_TYPE_ENUM: {
IV value = SvIV(ref);
if (check_enum_values &&
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);
}
case UPB_TYPE_INT32:
return sink->PutInt32(fd.selector.primitive, SvIV(ref));
case UPB_TYPE_UINT32:
return sink->PutUInt32(fd.selector.primitive, SvUV(ref));
case UPB_TYPE_INT64:
if (sizeof(IV) >= sizeof(int64_t))
return sink->PutInt64(fd.selector.primitive, SvIV(ref));
else
return sink->PutInt64(fd.selector.primitive, get_int64(aTHX_ ref));
case UPB_TYPE_UINT64:
if (sizeof(UV) >= sizeof(int64_t))
return sink->PutInt64(fd.selector.primitive, SvUV(ref));
else
return sink->PutUInt64(fd.selector.primitive, get_uint64(aTHX_ ref));
default:
return false; // just in case
}
}
bool Mapper::encode_nodefaults(Sink *sink, Status *status, const Field &fd, SV *ref) const {
switch (fd.field_def->type()) {
case UPB_TYPE_FLOAT: {
NV value = SvNV(ref);
if (value == fd.default_nv)
return true;
return sink->PutFloat(fd.selector.primitive, value);
}
case UPB_TYPE_DOUBLE: {
NV value = SvNV(ref);
if (value == fd.default_nv)
return true;
return sink->PutDouble(fd.selector.primitive, value);
}
case UPB_TYPE_BOOL: {
bool value = SvTRUE(ref);
if (value == fd.default_bool)
return true;
return sink->PutBool(fd.selector.primitive, value);
}
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
STRLEN len;
const char *str = fd.field_def->type() == UPB_TYPE_STRING ? SvPVutf8(ref, len) : SvPV(ref, len);
if (len == fd.default_str_len &&
(len == 0 || memcmp(str, fd.default_str, len) == 0))
return true;
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);
}
case UPB_TYPE_ENUM: {
IV value = SvIV(ref);
if (value == fd.default_iv)
return true;
if (check_enum_values &&
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);
}
case UPB_TYPE_INT32: {
IV value = SvIV(ref);
if (value == fd.default_iv)
return true;
return sink->PutInt32(fd.selector.primitive, value);
}
case UPB_TYPE_UINT32: {
UV value = SvUV(ref);
if (value == fd.default_uv)
return true;
return sink->PutUInt32(fd.selector.primitive, value);
}
case UPB_TYPE_INT64: {
int64_t value = sizeof(IV) >= sizeof(int64_t) ? SvIV(ref) : get_int64(aTHX_ ref);
if (value == fd.default_i64)
return true;
return sink->PutInt64(fd.selector.primitive, value);
}
case UPB_TYPE_UINT64: {
uint64_t value = sizeof(UV) >= sizeof(int64_t) ? SvUV(ref) : get_uint64(aTHX_ ref);
if (value == fd.default_u64)
return true;
return sink->PutUInt64(fd.selector.primitive, value);
}
default:
return false; // just in case
}
}
bool Mapper::encode_key(Sink *sink, Status *status, const Field &fd, const char *key, I32 keylen) const {
switch (fd.field_def->type()) {
case UPB_TYPE_BOOL: {
// follows what SvTRUE() does for strings
bool bval = keylen > 1 || keylen == 1 && key[0] != '0';
return sink->PutBool(fd.selector.primitive, bval);
}
case UPB_TYPE_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 UPB_TYPE_INT32:
return sink->PutInt32(fd.selector.primitive, key_iv(aTHX_ key, keylen));
case UPB_TYPE_UINT32:
return sink->PutUInt32(fd.selector.primitive, key_uv(aTHX_ key, keylen));
case UPB_TYPE_INT64:
return sink->PutInt64(fd.selector.primitive, key_iv(aTHX_ key, keylen));
case UPB_TYPE_UINT64:
return sink->PutInt64(fd.selector.primitive, key_uv(aTHX_ key, keylen));
default:
return false; // just in case
}
}
bool Mapper::encode_hash_kv(Sink *sink, Status *status, const char *key, STRLEN keylen, SV *value) const {
if (!sink->StartMessage())
return false;
if (fields[0].is_key) {
if (!encode_key(sink, status, fields[0], key, keylen))
return false;
if (!encode(sink, status, fields[1], value))
return false;
} else {
if (!encode_key(sink, status, fields[1], key, keylen))
return false;
if (!encode(sink, status, fields[0], value))
return false;
}
if (!sink->EndMessage(status))
return false;
return true;
}
bool Mapper::encode_from_perl_hash(Sink *sink, Status *status, const Field &fd, SV *ref) const {
SvGETMAGIC(ref);
if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVHV)
croak("Not an hash reference when encoding field '%s'", fd.full_name().c_str());
HV *hash = (HV *) SvRV(ref);
Sink repeated;
if (!sink->StartSequence(fd.selector.seq_start, &repeated))
return false;
hv_iterinit(hash);
WarnContext::Item &warn_cxt = warn_context->push_level(WarnContext::Hash);
while (HE *entry = hv_iternext(hash)) {
Sink key_value;
SV *value = HeVAL(entry);
const char *key;
STRLEN keylen;
bool needs_free = false;
if (HeKLEN(entry) == HEf_SVKEY) {
key = SvPVutf8(HeKEY_sv(entry), keylen);
} else {
if (HeKUTF8(entry)) {
key = HeKEY(entry);
keylen = HeKLEN(entry);
} else {
keylen = HeKLEN(entry);
key = (const char *) bytes_to_utf8((U8*) HeKEY(entry), &keylen);
SAVEFREEPV(key);
}
}
warn_cxt.key = key;
warn_cxt.keylen = keylen;
if (!repeated.StartSubMessage(fd.selector.msg_start, &key_value))
return false;
if (!fd.mapper->encode_hash_kv(&key_value, status, key, keylen, value))
return false;
if (!repeated.EndSubMessage(fd.selector.msg_end))
return false;
}
warn_context->pop_level();
return sink->EndSequence(fd.selector.seq_end);
}
bool Mapper::encode_from_perl_array(Sink *sink, 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.field_def->type()) {
case UPB_TYPE_FLOAT:
return encode_from_array<NVGetter, FloatEmitter>(sink, fd, array);
case UPB_TYPE_DOUBLE:
return encode_from_array<NVGetter, DoubleEmitter>(sink, fd, array);
case UPB_TYPE_BOOL:
return encode_from_array<BoolGetter, BoolEmitter>(sink, fd, array);
case UPB_TYPE_STRING:
return encode_from_array<SVGetter, StringEmitter>(sink, fd, array);
case UPB_TYPE_BYTES:
return encode_from_array<SVGetter, StringEmitter>(sink, fd, array);
case UPB_TYPE_MESSAGE:
return fd.mapper->encode_from_message_array(sink, status, fd, array);
case UPB_TYPE_ENUM:
if (check_enum_values)
return encode_from_array<IVGetter, EnumEmitter>(sink, status, fd, array);
else
return encode_from_array<IVGetter, Int32Emitter>(sink, fd, array);
case UPB_TYPE_INT32:
return encode_from_array<IVGetter, Int32Emitter>(sink, fd, array);
case UPB_TYPE_UINT32:
return encode_from_array<UVGetter, UInt32Emitter>(sink, fd, array);
case UPB_TYPE_INT64:
if (sizeof(IV) >= sizeof(int64_t))
return encode_from_array<IVGetter, Int64Emitter>(sink, fd, array);
else
return encode_from_array<I64Getter, Int64Emitter>(sink, fd, array);
case UPB_TYPE_UINT64:
if (sizeof(IV) >= sizeof(int64_t))
return encode_from_array<UVGetter, UInt64Emitter>(sink, fd, array);
else
return encode_from_array<U64Getter, UInt64Emitter>(sink, 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 an 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);
// if the key is marked as UTF-8 and contains non-ASCII characters,
// it will not be there anyway in the lookup
string name(key, keylen < 0 ? -keylen : keylen);
STD_TR1::unordered_map<string, Field *>::const_iterator it = field_map.find(name);
if (it == field_map.end()) {
status->SetFormattedErrorMessage(
"Unknown field '%s' during check",
name.c_str());
return false;
}
Field *field = it->second;
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.field_def->type()) {
case UPB_TYPE_MESSAGE:
return fd.mapper->check(status, ref);
case UPB_TYPE_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.field_def->type()) {
case UPB_TYPE_MESSAGE:
return fd.mapper->check_from_message_array(status, fd, array);
case UPB_TYPE_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;
}
}
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 an 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 an 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:
set_bool(aTHX_ 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 == 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:
set_bool(aTHX_ 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 an hash reference", field->full_name().c_str());
sv_setsv(target, value);
break;
case UPB_TYPE_ENUM: {
I32 i32 = SvIV(value);
const STD_TR1::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 = get_int64(aTHX_ 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 = get_uint64(aTHX_ 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 an 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));
}
EnumMapper::EnumMapper(pTHX_ Dynamic *_registry, const upb::EnumDef *_enum_def) :
registry(_registry),
enum_def(_enum_def) {
SET_THX_MEMBER;
registry->ref();
}
EnumMapper::~EnumMapper() {
// make sure this only goes away after inner destructors have completed
refcounted_mortalize(aTHX_ registry);
}
SV *EnumMapper::enum_descriptor() const {
SV *ref = newSV(0);
sv_setref_iv(ref, "Google::ProtocolBuffers::Dynamic::EnumDef", (IV) enum_def);
return ref;
}
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);
}
void WarnContext::warn_with_context(pTHX_ SV *warning) const {
SV *cxt = sv_2mortal(newSVpvs("While encoding field '"));
for (Levels::const_iterator it = levels.begin(), en = levels.end(); it != en; ++it) {
switch (it->kind) {
case Array:
sv_catpvf(cxt, "[%d].", it->index);
break;
case Hash:
sv_catpvs(cxt, "{");
sv_catpvn(cxt, it->key, it->keylen);
sv_catpvs(cxt, "}.");
break;
case Message:
sv_catpvf(cxt, "%" SVf ".", it->field->name);
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);
}