Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moo like #4

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
1 change: 0 additions & 1 deletion Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ WriteMakefile(
LICENSE => 'perl',
MIN_PERL_VERSION=> 5.010001,
CCFLAGS => $CCFLAGS,
LDFLAGS => '-g',
OBJECT => 'XS.o meta.o',
#LIBS => ['-lstdc++'],
);
Expand Down
47 changes: 23 additions & 24 deletions XS.xs
Original file line number Diff line number Diff line change
Expand Up @@ -18,50 +18,50 @@ static int unstolen = 0;

static void
CAIXS_install_inherited_accessor(pTHX_ SV* full_name, SV* hash_key, SV* pkg_key, SV* read_cb, SV* write_cb, int opts) {
shared_keys* payload;
install_info info;
bool need_cb = read_cb && write_cb;

if (need_cb) {
assert(pkg_key != NULL);

if (opts & IsNamed) {
payload = CAIXS_install_accessor<InheritedCbNamed>(aTHX_ full_name, (AccessorOpts)(opts & ~IsNamed));
info = CAIXS_install_accessor<InheritedCbNamed>(aTHX_ full_name, (AccessorOpts)(opts & ~IsNamed));
} else {
payload = CAIXS_install_accessor<InheritedCb>(aTHX_ full_name, (AccessorOpts)opts);
info = CAIXS_install_accessor<InheritedCb>(aTHX_ full_name, (AccessorOpts)opts);
}

} else if (pkg_key != NULL) {
payload = CAIXS_install_accessor<Inherited>(aTHX_ full_name, (AccessorOpts)opts);
info = CAIXS_install_accessor<Inherited>(aTHX_ full_name, (AccessorOpts)opts);

} else {
payload = CAIXS_install_accessor<ObjectOnly>(aTHX_ full_name, (AccessorOpts)opts);
bool required = true;
SV* default_val = NULL;
caixs::meta::install(full_name, hash_key, required, default_val);
info = CAIXS_install_accessor<ObjectOnly>(aTHX_ full_name, (AccessorOpts)opts);
SV* required = &PL_sv_yes;
SV* default_val = &PL_sv_undef;
caixs::meta::install(info.cv, hash_key, required, default_val);
}

STRLEN len;
const char* hash_key_buf = SvPV_const(hash_key, len);
SV* s_hash_key = newSVpvn_share(hash_key_buf, SvUTF8(hash_key) ? -(I32)len : (I32)len, 0);
payload->hash_key = s_hash_key;
info.payload->hash_key = s_hash_key;

if (pkg_key != NULL) {
const char* pkg_key_buf = SvPV_const(pkg_key, len);
SV* s_pkg_key = newSVpvn_share(pkg_key_buf, SvUTF8(pkg_key) ? -(I32)len : (I32)len, 0);
payload->pkg_key = s_pkg_key;
info.payload->pkg_key = s_pkg_key;
}

if (need_cb) {
if (SvROK(read_cb) && SvTYPE(SvRV(read_cb)) == SVt_PVCV) {
payload->read_cb = SvREFCNT_inc_NN(SvRV(read_cb));
info.payload->read_cb = SvREFCNT_inc_NN(SvRV(read_cb));
} else {
payload->read_cb = NULL;
info.payload->read_cb = NULL;
}

if (SvROK(write_cb) && SvTYPE(SvRV(write_cb)) == SVt_PVCV) {
payload->write_cb = SvREFCNT_inc_NN(SvRV(write_cb));
info.payload->write_cb = SvREFCNT_inc_NN(SvRV(write_cb));
} else {
payload->write_cb = NULL;
info.payload->write_cb = NULL;
}
}
}
Expand All @@ -70,34 +70,34 @@ static void
CAIXS_install_class_accessor(pTHX_ SV* full_name, SV* default_sv, bool is_varclass, int opts) {
bool is_lazy = SvROK(default_sv) && SvTYPE(SvRV(default_sv)) == SVt_PVCV;

shared_keys* payload;
install_info info;
if (is_lazy) {
payload = CAIXS_install_accessor<LazyClass>(aTHX_ full_name, (AccessorOpts)opts);
info = CAIXS_install_accessor<LazyClass>(aTHX_ full_name, (AccessorOpts)opts);

} else {
payload = CAIXS_install_accessor<PrivateClass>(aTHX_ full_name, (AccessorOpts)opts);
info = CAIXS_install_accessor<PrivateClass>(aTHX_ full_name, (AccessorOpts)opts);
}

if (is_varclass) {
GV* gv = gv_fetchsv(full_name, GV_ADD, SVt_PV);
assert(gv);

payload->storage = GvSV(gv);
assert(payload->storage);
info.payload->storage = GvSV(gv);
assert(info.payload->storage);

/* We take ownership of this glob slot, so if someone changes the glob - they're in trouble */
SvREFCNT_inc_simple_void_NN(payload->storage);
SvREFCNT_inc_simple_void_NN(info.payload->storage);

} else {
payload->storage = newSV(0);
info.payload->storage = newSV(0);
}

if (SvOK(default_sv)) {
if (is_lazy) {
payload->lazy_cb = SvREFCNT_inc_NN(SvRV(default_sv));
info.payload->lazy_cb = SvREFCNT_inc_NN(SvRV(default_sv));

} else {
sv_setsv(payload->storage, default_sv);
sv_setsv(info.payload->storage, default_sv);
}
}
}
Expand All @@ -117,7 +117,6 @@ BOOT:
HV* stash = gv_stashpv("Class::Accessor::Inherited::XS", 0);
newCONSTSUB(stash, "BINARY_UNSAFE", CAIX_BINARY_UNSAFE_RESULT);
newCONSTSUB(stash, "OPTIMIZED_OPMETHOD", CAIX_OPTIMIZE_OPMETHOD_RESULT);
caixs::meta::init_meta();
}

void
Expand Down
199 changes: 66 additions & 133 deletions meta.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,190 +3,123 @@

namespace caixs { namespace meta {

typedef AV* PackageMeta;

static MGVTBL package_marker;
static MGVTBL field_marker;

static int package_meta_free(pTHX_ SV*, MAGIC* mg);
static int field_meta_free(pTHX_ SV*, MAGIC* mg);

void init_meta() {
package_marker.svt_free = &package_meta_free;
field_marker.svt_free = &field_meta_free;
}
struct FieldMeta {
SV* name;
SV* required;
SV* default_value;
};

static void attach_magic(SV* sv, MGVTBL* marker, void* value) {
MAGIC* mg;
Newx(mg, 1, MAGIC);
mg->mg_moremagic = SvMAGIC(sv);
mg->mg_virtual = marker;
mg->mg_type = PERL_MAGIC_ext;
mg->mg_len = 0;
mg->mg_ptr = (char*)value;
mg->mg_private = 0;
mg->mg_obj = NULL;
mg->mg_flags = 0;

SvMAGIC_set(sv, mg);
//SvRMAGICAL_off(sv); ???
}

static int package_meta_free(pTHX_ SV*, MAGIC* mg) {
if (mg->mg_virtual == &package_marker) {
PackageMeta* meta = (PackageMeta*)mg->mg_ptr;
meta->~PackageMeta();
}
return 0;
}

static int field_meta_free(pTHX_ SV*, MAGIC* mg) {
if (mg->mg_virtual == &field_marker) {
FieldMeta* meta = (FieldMeta*)mg->mg_ptr;
meta->~FieldMeta();
}
return 0;
}
#define FIELDS_PREALLOCADED 5
#define FIELD_SV_COUNT 3

static void* find_magic(SV* sv, MGVTBL* marker) {
if (SvTYPE(sv) >= SVt_PVMG) {
for (MAGIC* mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
if (mg->mg_virtual == marker) {
return mg->mg_ptr;
PackageMeta find_package(HV* stash) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Используй все-таки CAIXS_mg_findext.

if (SvTYPE(stash) >= SVt_PVMG) {
for (MAGIC* mg = SvMAGIC(stash); mg; mg = mg->mg_moremagic) {
if (mg->mg_virtual == &package_marker) {
return (PackageMeta)mg->mg_obj;
}
}
}
return NULL;
}

PackageMeta* find_package(HV* stash) { return (PackageMeta*)find_magic((SV*)stash, &package_marker); }
FieldMeta* find_field(SV* field) { return (FieldMeta*) find_magic(field, &field_marker); }

static PackageMeta* create_package(HV* stash) {
if (SvREADONLY(stash)) return NULL;
if ((SvTYPE(stash) > SVt_PVMG) && SvOK(stash)) return NULL;
static PackageMeta create_package(HV* stash) {
AV* meta = newAV();
av_extend(meta, FIELDS_PREALLOCADED * FIELD_SV_COUNT);

char* storage = (char*) malloc(sizeof(PackageMeta));
PackageMeta* meta = new(storage) PackageMeta();
sv_magicext((SV*)stash, (SV*)meta, PERL_MAGIC_ext, &package_marker, NULL, 0);
SvREFCNT_dec_NN((SV*)meta);
SvRMAGICAL_off((SV*)stash);

SvUPGRADE((SV*)stash, SVt_PVMG);
attach_magic((SV*)stash, &package_marker, meta);
return meta;
}

// field meta
FieldMeta::FieldMeta(): name(NULL), required{false}, default_value{NULL} { }
inline size_t size(PackageMeta meta) { return (AvFILLp(meta) + 1) / FIELD_SV_COUNT;}

FieldMeta::FieldMeta(SV* name_, bool required_, SV* default_value_) {
name = SvREFCNT_inc_simple(name_);
required = required_;
if (default_value_ && SvOK(default_value_)) {
default_value = SvREFCNT_inc_simple(default_value_);
}
}
void record(PackageMeta meta, SV* hash_key, SV* required, SV* default_value) {
FieldMeta* fields = (FieldMeta*)AvARRAY(meta);

FieldMeta::~FieldMeta() {
if (name) { SvREFCNT_dec(name); }
if (default_value) { SvREFCNT_dec(default_value); }
}

void FieldMeta::activate(HV* obj) {
STRLEN f_len;
const char* f_name = SvPV(name, f_len);
STRLEN name_len;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не utf8-безопасно.

char* name = SvPV(hash_key, name_len);

if (required) {
SV** ref = hv_fetch(obj, f_name, f_len, 0);
if (!ref) croak("key '%s' is required", f_name);
return;
}
/* check that there might be already field meta is defined*/
size_t fields_sz = size(meta);
for(size_t i = 0; i < fields_sz; ++i) {
STRLEN field_len;
char* field_name = SvPV(fields[i].name, field_len);
if (field_len != name_len) continue;

if (default_value && !hv_exists(obj, f_name, f_len)) {
SV* val = SvREFCNT_inc(default_value);
SV** ret = hv_store(obj, f_name, f_len, val, 0);
if (!ret) SvREFCNT_dec(val);
if (strcmp(name, field_name) == 0) {
croak("object key '%' is already defined", name);
}
}
}

// package meta
PackageMeta::PackageMeta() : fields(newHV()) {}

PackageMeta::~PackageMeta() {
SvREFCNT_dec(fields);
}

void PackageMeta::record(SV* hash_key, bool required, SV* default_value) {
if (!hash_key) return;
if (!(SvTYPE(hash_key) <= SVt_PVMG) && SvROK(hash_key)) return;

STRLEN k_len;
const char* k_name = SvPV(hash_key, k_len);

SV** ref = hv_fetch(fields, k_name, k_len, 0);
if (ref) return;
if (SvOK(default_value) && (!SvROK(default_value) || SvTYPE(SvRV(default_value)) != SVt_PVCV))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a simple scalar is provided, it will be inlined as a string.
Давай для совместимости сделаем так же.

croak("'default' should be a code reference");

SV* field_sv = newSV(0);
SvUPGRADE(field_sv, SVt_PVMG);
size_t new_sz = AvFILLp(meta) + FIELD_SV_COUNT;
av_fill(meta, new_sz);

char* storage = (char*) malloc(sizeof(FieldMeta));
FieldMeta* field = new(storage) FieldMeta(hash_key, required, default_value);
attach_magic(field_sv, &field_marker, field);

SV** ret = hv_store(fields, k_name, k_len, field_sv, 0);
if (!ret) SvREFCNT_dec_NN(field_sv);
FieldMeta& field = fields[fields_sz];
field.name = SvREFCNT_inc(hash_key);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно брать _NN версии.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

И даже _simple_NN.

field.required = SvREFCNT_inc(required);
field.default_value = SvREFCNT_inc(default_value);
}

void PackageMeta::activate(SV *sv) {
if (!(sv && SvROK(sv))) return;
void activate(PackageMeta meta, SV *sv) {
if (!SvROK(sv)) croak("cannot activate non-reference");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут же всегда корректный объект из конструктора, и проверки не нужны?


SV* obj = SvRV(sv);
if (SvTYPE(obj) != SVt_PVHV) return;
if (!SvOBJECT(obj)) return;

HV* hv = (HV*)obj;
HE** arr = HvARRAY(fields);
HE** end = arr + HvMAX(fields) + 1;
HE* cur = nullptr;
if (SvTYPE(obj) != SVt_PVHV) croak("cannot activate non-hash reference");
HV* hv = (HV*)obj;

while(true) {
if (cur) { cur = HeNEXT(cur); }
else { while (!cur && arr != end) cur = *arr++; }

if (!cur) break;
FieldMeta* fields = (FieldMeta*)AvARRAY(meta);
size_t fields_sz = size(meta);
for(size_t i = 0; i < fields_sz; ++i) {
FieldMeta& field = fields[i];

SV* field_sv = HeVAL(cur);
FieldMeta* field = find_field(field_sv);
if (!field) continue;
STRLEN field_len;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аналогично комменту про utf8 выше - зачем использовать строковый апи без предпосчитанного хэша?

char* field_name = SvPV(field.name, field_len);

field->activate(hv);
if (SvTRUE(field.required)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно SvTRUE проверку вынести на этап install, и ставить оттуда жестко PL_sv_yes/PL_sv_no - и эта строчка станет сравнением с указателем.

SV** ref = hv_fetch(hv, field_name, field_len, 0);
if (!ref) croak("key '%s' is required", field_name);
return;
} else if (SvOK(field.default_value)) {
// ...
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DO THIS TO

}
}
}

// API-helpers

void install (SV* full_name, SV* hash_key, bool required, SV *default_value) {
STRLEN len;

const char* name = SvPV_const(full_name, len);

CV* cv = get_cvn_flags(name, len, 0);
if (!cv) return;

void install (CV *cv, SV* hash_key, SV* required, SV *default_value) {
GV* gv = CvGV(cv);
if (!gv) return;
if (!gv) croak("cant get CV's glob");

HV* stash = GvSTASH(gv);
if (!stash) return;
if (!stash) croak("can't get stash");

PackageMeta* meta = find_package(stash);
PackageMeta meta = find_package(stash);
if (!meta) meta = create_package(stash);
if (!meta) return;

meta->record(hash_key, required, default_value);
record(meta, hash_key, required, default_value);
}

void activate(HV* stash, SV* object) {
PackageMeta* meta = find_package(stash);
PackageMeta meta = find_package(stash);
if (!meta) return;

meta->activate(object);
activate(meta, object);
}


Expand Down
2 changes: 1 addition & 1 deletion xs/fimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ template <AccessorType type, AccessorOpts opts>
struct CImpl;

template <AccessorType type> inline
shared_keys*
install_info
CAIXS_install_accessor(pTHX_ SV* full_name, AccessorOpts val) {
return CImpl<type, AccessorOptsBF>::CAIXS_install_accessor(aTHX_ val, full_name);
}
Expand Down
Loading