The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

BEGIN { $^W=1; }
use strict;
use Fatal qw(open);
use lib './';
BEGIN {
require "./HashRecord.pm";
'ObjStore::REP::HashRecord'->import(qw(c_types $VERSION $Fspec));
}
use vars qw(%T @T $Indent $Base);
$Base = "OSPV_HashRecord";
@T = c_types;
for (my $x=0; $x < @T; $x++) { $T{ $T[$x] } = $x }
$Indent = 0;
sub indent {
my ($x) = @_;
$Indent += 2;
$x->();
$Indent -= 2;
}
sub out(@) { print(' 'x$Indent.join('', @_)."\n") }
sub preamble {
out "// Yucky -*-C++-*- generated at ".localtime()." by HashRecord $VERSION";
out;
}
sub print_defines {
for (sort keys %T) {
print "#define FT_$_".' 'x(25 - length)."$T{$_}\n";
}
}
sub print_fetch {
out "void $Base\::FETCH(SV *key)";
out "{";
indent sub {
out "STRLEN klen;";
out "char *kstr = SvPV(key, klen);";
out "int fi = HR_key_2field(kstr, klen);";
out "if (fi < 0) {";
indent sub {
out "OSPV_Generic *fb = (OSPV_Generic *) HR_get_fallback();";
out "if (!fb) return;";
out "fb->FETCH(key);";
out "return;";
};
out "}";
out "$Fspec *spec = HR_get_field_spec(fi);";
out "switch (spec->type) {";
for my $t (@T) {
out "case FT_$t:{";
indent sub {
out "$t *fp = ($t *) (((char*)this)+spec->offset);";
if ($t eq 'OSPVptr') {
out "SV *ret = osp_thr::ospv_2sv(*fp);";
out "dSP;";
out "XPUSHs(ret);";
} elsif ($t =~ m/^os_reference/) {
out "SV *ret = osp_thr::ospv_2sv((OSSVPV*) fp->resolve());";
out "dSP;";
out "XPUSHs(ret);";
} elsif ($t eq 'OSSV') {
out "SV *ret = osp_thr::ossv_2sv(fp);";
out "dSP;";
out "XPUSHs(ret);";
} elsif ($t =~ m/^ os_int(\d+) $/x) {
out "dSP;";
out "XPUSHs(sv_2mortal(newSViv(*fp)));";
} elsif ($t =~ m/^osp_str(\d+)$/) {
out "dSP;";
out "if (fp->is_undef())";
indent sub { out "XPUSHs(&PL_sv_undef);" };
out "else {";
indent sub {
out "STRLEN len; char *str = fp->get(&len);";
out "XPUSHs(len? sv_2mortal(newSVpvn(str,len)) : &PL_sv_no);";
};
out "}";
} elsif ($t =~ m/^double|float$/) {
out "dSP;";
out "XPUSHs(sv_2mortal(newSVnv(*fp)));";
} elsif ($t =~ m/^char$/) {
out "dSP;";
out "XPUSHs(sv_2mortal(newSVpvn(fp, 1)));";
} else {
warn "*** unknown type '$t'";
}
out "PUTBACK;";
out "break;}"
};
}
out "default:";
indent sub {
out qq[croak("%s(0x%p)->FETCH: $Fspec type %d unknown", os_class(&PL_na), this, spec->type);];
};
out "}";
};
out "}";
}
sub print_store {
out "void $Base\::STORE(SV *key, SV *nval)";
out "{";
indent sub {
out "STRLEN klen;";
out "char *kstr = SvPV(key, klen);";
out "int fi = HR_key_2field(kstr, klen);";
out "if (fi < 0) {";
indent sub {
out "OSPV_Generic *fb = (OSPV_Generic *) HR_get_fallback();";
out q[if (!fb) croak("Cannot create key '%s' in %s(0x%p)", kstr, os_class(&PL_na), this);];
out "fb->STORE(key,nval);";
out "return;";
};
out "}";
out "$Fspec *spec = HR_get_field_spec(fi);";
out qq[if (!HR_mod_field(fi)) croak("%s(0x%x): attempt to modify READONLY %s", os_class(&PL_na), this, spec->key);];
out "switch (spec->type) {";
for my $t (@T) {
out "case FT_$t:{";
indent sub {
out "$t *fp = ($t *) (((char*)this)+spec->offset);";
if ($t eq 'OSPVptr' or $t =~ m/^os_reference/) {
out "ospv_bridge *br = osp_thr::sv_2bridge(nval, 0, os_segment::of(this));";
out "*fp = br? br->ospv() : 0;";
} elsif ($t eq 'OSSV') {
out "*fp = nval;";
} elsif ($t =~ m/^ os_int(\d+) $/x) {
out "*fp = SvIV(nval);";
} elsif ($t =~ m/^osp_str(\d+)$/) {
out "if (!SvOK(nval))";
indent sub { out "fp->set_undef();" };
out "else {";
indent sub {
out "STRLEN len; char *pv = SvPV(nval, len);";
out qq[if (len > $1) warn("Truncating string from length %d to %d", len, $1);];
out "fp->set(pv,len);";
};
out "}";
} elsif ($t =~ m/^double|float$/) {
out "*fp = SvNV(nval);";
} elsif ($t =~ m/^char$/) {
out "*fp = *SvPV(nval, PL_na);";
} else {
warn "*** unknown type '$t'";
}
out "break;}"
}
}
out "default:";
indent sub {
out qq[croak("%s(0x%p)->STORE: $Fspec type %d unknown", os_class(&PL_na), this, spec->type);];
};
out "}";
};
out "}";
}
sub print_traverse1 {
out "OSSVPV *$Base\::traverse1(osp_pathexam &exam)";
out "{";
indent sub {
out "int fi = HR_key_2field(exam.get_thru(), exam.get_thru_len());";
out "if (fi < 0) {";
indent sub {
out "OSPV_Generic *fb = (OSPV_Generic *) HR_get_fallback();";
out "if (!fb) return 0;";
out "return fb->traverse1(exam);";
};
out "}";
out "$Fspec *spec = HR_get_field_spec(fi);";
out "switch (spec->type) {";
for my $t (@T) {
next unless $t eq 'OSPVptr' || $t =~ m/^os_reference/;
out "case FT_$t:{";
indent sub {
out "$t *fp = ($t *) (((char*)this)+spec->offset);";
if ($t eq 'OSPVptr') {
out "OSSVPV *pv = *fp;";
} elsif ($t =~ m/^os_reference/) {
out "OSSVPV *pv = (OSSVPV*) fp->resolve();";
} else {
warn "*** unknown type '$t'";
}
out "if (!pv) return 0;";
out "HR_mod_field(exam, fi);";
out "return pv;}";
};
}
out "default: return 0;";
out "}";
};
out "}";
}
sub print_traverse2 {
out "OSSV *$Base\::traverse2(osp_pathexam &exam)";
out "{";
indent sub {
out "int fi = HR_key_2field(exam.get_thru(), exam.get_thru_len());";
out "if (fi < 0) {";
indent sub {
out "OSPV_Generic *fb = (OSPV_Generic *) HR_get_fallback();";
out "if (!fb) return 0;";
out "return traverse2(exam);";
};
out "}";
out "osp_hashrec_field_spec *spec = HR_get_field_spec(fi);";
out "OSSV *ret = exam.get_tmpkey();";
out "switch (spec->type) {";
for my $t (@T) {
out "case FT_$t:{";
indent sub {
out "$t *fp = ($t *) (((char*)this)+spec->offset);";
if ($t eq 'OSPVptr' or $t =~ m/^os_reference/) {
out q[croak("path resolves to a ref (at %s)", exam.get_thru());];
} elsif ($t eq 'OSSV') {
out "if (fp->is_set()) HR_mod_field(exam, fi);";
out "return fp;";
} elsif ($t =~ m/^os_int(\d+)$/) {
out "HR_mod_field(exam, fi);";
out "ret->s((os_int32) *fp);";
} elsif ($t =~ m/^osp_str(\d+)$/) {
out "if (fp->is_undef())";
indent sub { out "ret->set_undef();"; };
out "else {";
indent sub {
out "HR_mod_field(exam, fi);";
out "STRLEN len; char *str = fp->get(&len);";
out "ret->s(str, len);";
};
out "}";
} elsif ($t =~ m/^double|float$/) {
out "HR_mod_field(exam, fi);";
out "ret->s((double) *fp);";
} elsif ($t =~ m/^char$/) {
out "HR_mod_field(exam, fi);";
out "ret->s(fp, 1);";
} else {
warn "*** unknown type '$t'";
}
out "break;}"
};
}
out "default:";
indent sub {
out qq[croak("%s(0x%p)->traverse2: $Fspec type %d unknown", os_class(&PL_na), this, spec->type);];
};
out "}";
out "return ret;";
};
out "}";
}
open OUT, ">osp_hashrecord.h";
select OUT;
&preamble;
out "#ifndef _osp_hashrecord_h_";
out "#define _osp_hashrecord_h_";
out;
out q[#include <osperl.h>];
&print_defines;
out <<END;
#define HASHRECORD_API_VERSION 3
#define OSPV_phrREADONLY 0x0100 /* readonly flag */
struct $Fspec {
int id;
char *key;
STRLEN keylen;
int offset;
int type;
char *alias_to;
};
struct $Base : OSPV_Generic {
static void use(const char *YourName, int ver);
virtual char *rep_class(STRLEN *len);
virtual int get_perl_type();
virtual void FETCH(SV *key);
virtual void STORE(SV *key, SV *value);
virtual int EXISTS(SV *key);
virtual void DELETE(SV *key);
virtual void FIRST(osp_smart_object **);
virtual void NEXT(osp_smart_object **);
virtual OSSVPV *traverse1(osp_pathexam &exam);
virtual OSSV *traverse2(osp_pathexam &exam);
// YOU MUST IMPLEMENT:
virtual char *os_class(STRLEN *len)=0;
// & your XS 'new' method
// TO BE GENERATED PER-SUBCLASS...
virtual void make_constant()=0;
virtual int HR_get_num_fields()=0;
virtual int HR_key_2field(char *key, int klen)=0;
virtual OSSVPV *HR_get_fallback();
virtual $Fspec *HR_get_field_spec(int xx)=0;
virtual int HR_mod_field(int xx)=0;
virtual void HR_mod_field(osp_pathexam &exam, int xx)=0;
};
#endif
END
close OUT;
open OUT, ">librecord.c";
select OUT;
&preamble;
out q[extern "C" {];
out q[#include "EXTERN.h"];
out q[#include "perl.h"];
out q[#include "XSUB.h"];
out "}";
out q[#include "osp_hashrecord.h"];
out <<END;
void $Base\::use(const char *YourName, int ver)
{
osp_thr::use("ObjStore::REP::HashRecord", OSPERL_API_VERSION);
if (ver != HASHRECORD_API_VERSION)
croak("ObjStore::HashRecord API mismatch (%d != %d); please recompile '%s'",
HASHRECORD_API_VERSION, ver, YourName);
}
char *$Base\::rep_class(STRLEN *len)
{ *len = 20; return "ObjStore::REP::HashRecord"; }
int $Base\::get_perl_type()
{ return SVt_PVHV; }
OSSVPV *$Base\::HR_get_fallback() { return 0; }
int $Base\::EXISTS(SV *key)
{
STRLEN klen;
char *kstr = SvPV(key, klen);
if (HR_key_2field(kstr, klen) >= 0) return 1;
OSPV_Generic *fb = (OSPV_Generic *) HR_get_fallback();
if (!fb) return 0;
return fb->EXISTS(key);
}
void $Base\::DELETE(SV *key)
{
STRLEN klen;
char *kstr = SvPV(key, klen);
if (HR_key_2field(kstr, klen) >= 0) {
warn("Attempt to delete key '%s' from %s(0x%p) ignored",
kstr, os_class(&PL_na), this);
return;
}
OSPV_Generic *fb = (OSPV_Generic *) HR_get_fallback();
if (!fb) return;
fb->DELETE(key);
}
struct HR_bridge : osp_smart_object {
int cursor;
osp_smart_object *fb_cursor; //for fallback!
HR_bridge() : cursor(0), fb_cursor(0) {}
void reset() {
cursor = 1;
if (fb_cursor) { fb_cursor->freelist(); fb_cursor = 0; }
}
virtual ~HR_bridge() { reset(); }
};
void $Base\::FIRST(osp_smart_object **info)
{
assert(info);
if (! *info) *info = new HR_bridge;
HR_bridge *br = (HR_bridge*) *info;
br->reset();
$Fspec *spec = HR_get_field_spec(0);
dSP;
XPUSHs(sv_2mortal(newSVpvn(spec->key, spec->keylen)));
PUTBACK;
}
void $Base\::NEXT(osp_smart_object **info)
{
assert(info);
assert(*info);
HR_bridge *br = (HR_bridge*) *info;
if (br->cursor < HR_get_num_fields()) {
$Fspec *spec = HR_get_field_spec(br->cursor++);
dSP;
XPUSHs(sv_2mortal(newSVpvn(spec->key, spec->keylen)));
PUTBACK;
} else {
OSPV_Generic *fb = (OSPV_Generic*) HR_get_fallback();
if (!fb) return;
if (!br->fb_cursor) fb->FIRST(&br->fb_cursor);
else fb->NEXT(&br->fb_cursor);
}
}
END
&print_traverse1;
out;
&print_traverse2;
out;
&print_fetch;
out;
&print_store;
close OUT;