#include "transform.h"
#include "unordered_map.h"
using namespace gpd::transform;
DecoderTransform::DecoderTransform(CDecoderTransform _c_transform) :
c_transform(_c_transform),
c_transform_fieldtable(NULL),
perl_transform(NULL)
{}
DecoderTransform::DecoderTransform(CDecoderTransformFieldtable _c_transform_fieldtable) :
c_transform(NULL),
c_transform_fieldtable(_c_transform_fieldtable),
perl_transform(NULL)
{}
DecoderTransform::DecoderTransform(SV *_perl_transform) :
c_transform(NULL),
c_transform_fieldtable(NULL),
perl_transform(_perl_transform)
{}
void DecoderTransform::destroy(pTHX) {
SvREFCNT_dec(perl_transform);
delete this;
}
void DecoderTransform::transform(pTHX_ SV *target) const {
if (c_transform) {
c_transform(aTHX_ target);
} else if (perl_transform) {
dSP;
PUSHMARK(SP);
XPUSHs(target);
PUTBACK;
// return value is always 1 because of G_SCALAR
call_sv(perl_transform, G_VOID|G_DISCARD);
} else {
croak("Internal error: transform function not provided");
}
}
void DecoderTransform::transform_fieldtable(pTHX_ SV *target, DecoderFieldtable *fieldtable) const {
if (c_transform_fieldtable) {
c_transform_fieldtable(aTHX_ target, fieldtable);
} else {
croak("Internal error: fieldtable transform function not provided");
}
}
EncoderTransform::EncoderTransform(CEncoderTransform _c_transform) :
c_transform(_c_transform),
c_transform_fieldtable(NULL),
perl_transform(NULL) {
}
EncoderTransform::EncoderTransform(CEncoderTransformFieldtable _c_transform_fieldtable) :
c_transform(NULL),
c_transform_fieldtable(_c_transform_fieldtable),
perl_transform(NULL) {
}
EncoderTransform::EncoderTransform(SV *_perl_transform) :
c_transform(NULL),
c_transform_fieldtable(NULL),
perl_transform(_perl_transform) {
}
void EncoderTransform::EncoderTransform::destroy(pTHX) {
SvREFCNT_dec(perl_transform);
delete this;
}
void EncoderTransform::transform(pTHX_ SV *target, SV *value) const {
if (c_transform) {
c_transform(aTHX_ target, value);
} else if (perl_transform) {
dSP;
PUSHMARK(SP);
XPUSHs(target);
XPUSHs(value);
PUTBACK;
// return value is always 1 because of G_SCALAR
call_sv(perl_transform, G_VOID|G_DISCARD);
} else {
croak("Internal error: transform function not provided");
}
}
void EncoderTransform::transform_fieldtable(pTHX_ EncoderFieldtable **target, SV *value) const {
if (c_transform_fieldtable) {
c_transform_fieldtable(aTHX_ target, value);
} else {
croak("Internal error: fieldtable transform function not provided");
}
}
UnknownFieldTransform::UnknownFieldTransform(CUnknownFieldTransform _c_transform) :
c_transform(_c_transform) {
}
void UnknownFieldTransform::destroy(pTHX) {
delete this;
}
DecoderTransformQueue::DecoderTransformQueue(pTHX) {
SET_THX_MEMBER;
}
DecoderTransformQueue::~DecoderTransformQueue() {
clear();
}
void DecoderTransformQueue::static_clear(DecoderTransformQueue *queue) {
queue->clear();
}
void DecoderTransformQueue::clear() {
for (std::vector<PendingTransform>::iterator it = pending_transforms.begin(), en = pending_transforms.end(); it != en; ++it) {
SvREFCNT_dec(it->target);
}
pending_transforms.clear();
for (std::vector<DecoderFieldtable::Entry>::iterator it = fieldtable.begin(), en = fieldtable.end(); it != en; ++it) {
SvREFCNT_dec(it->value);
}
fieldtable.clear();
}
size_t DecoderTransformQueue::add_transform(SV *target, const DecoderTransform *message_transform, const DecoderTransform *field_transform) {
if (field_transform != NULL) {
pending_transforms.push_back(PendingTransform(SvREFCNT_inc(target), field_transform));
} else if (message_transform != NULL) {
pending_transforms.push_back(PendingTransform(SvREFCNT_inc(target), message_transform));
}
return pending_transforms.size() - 1;
}
void DecoderTransformQueue::finish_add_transform(size_t index, int size, DecoderFieldtable::Entry *entries) {
int fieldtable_offset = fieldtable.size();
fieldtable.insert(fieldtable.end(), entries, entries + size);
pending_transforms[index].fieldtable_offset = fieldtable_offset;
pending_transforms[index].fieldtable_size = size;
}
void DecoderTransformQueue::apply_transforms() {
UMS_NS::unordered_set<SV *> already_mapped;
DecoderFieldtable table;
for (std::vector<PendingTransform>::reverse_iterator it = pending_transforms.rbegin(), en = pending_transforms.rend(); it != en; ++it) {
SV *target = it->target;
// this block should only be entered when decoding the concatenation
// of two protocol buffer messahes
//
// if an SV has refcount 2 (1 in the decoded struct, 1 in the
// transform list) it can't be in the transform list multiple times
if (SvREFCNT(target) > 2) {
if (already_mapped.find(target) != already_mapped.end())
continue;
already_mapped.insert(target);
}
if (it->fieldtable_offset == -1) {
it->transform->transform(aTHX_ target);
} else {
table.size = it->fieldtable_size;
table.entries = &fieldtable[it->fieldtable_offset];
it->transform->transform_fieldtable(aTHX_ target, &table);
}
}
}
// the only use for this transform is to be able to test fieldtable transformations
void gpd::transform::fieldtable_debug_decoder_transform(pTHX_ SV *target, DecoderFieldtable *fieldtable) {
AV *res = newAV();
SvUPGRADE(target, SVt_RV);
SvROK_on(target);
SvRV_set(target, (SV *) res);
for (int i = 0; i < fieldtable->size; ++i) {
AV *item = newAV();
av_push(item, newSViv(fieldtable->entries[i].field));
av_push(item, fieldtable->entries[i].value);
fieldtable->entries[i].value = NULL;
av_push(res, newRV_noinc((SV *) item));
}
}
// the only use for this transform is to be able to benchmark fieldtable trnasformations overhead
void gpd::transform::fieldtable_profile_decoder_transform(pTHX_ SV *target, DecoderFieldtable *fieldtable) {
DecoderFieldtable::Entry *entry = fieldtable->entries;
sv_setsv(target, entry->value);
SvREFCNT_dec(entry->value);
entry->value = NULL;
}
// the only use for this transform is to be able to test fieldtable trnasformations
namespace {
EncoderFieldtable::Entry debug_fieldentries[5];
EncoderFieldtable debug_fieldtable;
}
void gpd::transform::fieldtable_debug_encoder_transform(pTHX_ EncoderFieldtable **fieldtable, SV *value) {
AV *av = (AV *) SvRV(value);
debug_fieldtable.size = av_top_index(av) + 1;
debug_fieldtable.entries = debug_fieldentries;
for (int i = 0; i <= av_top_index(av); ++i) {
AV *item = (AV *) SvRV(*av_fetch(av, i, 0));
debug_fieldentries[i].field = SvIV(*av_fetch(item, 0, 0));
debug_fieldentries[i].name = SvPV_nolen(*av_fetch(item, 1, 0));
debug_fieldentries[i].value = SvREFCNT_inc(*av_fetch(item, 2, 0));
}
*fieldtable = &debug_fieldtable;
}
// the only use for this transform is to be able to test unknown field callbacks
namespace {
AV *value_dump;
}
AV *gpd::transform::fieldtable_debug_encoder_unknown_fields_get() {
AV *current = value_dump;
value_dump = NULL;
return current;
}
void gpd::transform::fieldtable_debug_encoder_unknown_fields(pTHX_ UnknownFieldContext *field_context, SV *value) {
if (value_dump == NULL)
value_dump = newAV();
AV *dump = newAV();
for (int i = 0; i < field_context->size; ++i) {
const MapperContext::ExternalItem *item = field_context->mapper_context[i];
switch (item->kind) {
case MapperContext::Hash:
case MapperContext::Message:
if (item->hash_item.svkey) {
av_push(dump, SvREFCNT_inc(item->hash_item.svkey));
} else {
av_push(dump, newSVpvn(item->hash_item.keybuf, item->hash_item.keylen));
}
break;
case MapperContext::Array:
av_push(dump, newSVuv(item->array_item.index));
break;
}
}
av_push(dump, SvREFCNT_inc(value));
av_push(value_dump, newRV_noinc((SV *) dump));
}