-
Notifications
You must be signed in to change notification settings - Fork 3
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
base: master
Are you sure you want to change the base?
Moo like #4
Changes from 1 commit
2a3d1f0
81c96a6
8a669f1
508b637
070dd71
57c0663
4a89cb1
c88643b
667f82d
54a5046
7140352
b2e218f
e5dae46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Можно брать _NN версии. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) { | ||
// ... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Используй все-таки CAIXS_mg_findext.