On Mon, 19 May 2008 10:15:26 +0100, Tim Bunce <[EMAIL PROTECTED]> wrote:
>
> On Fri, May 16, 2008 at 01:45:51AM +0100, Rudolf Lippan wrote:
>> On Thu, 15 May 2008 13:21:34 +0100, Tim Bunce <[EMAIL PROTECTED]>
> wrote:
>> > It would be good to add a clear comment somewhere that the pointers in
>> > the keys array are only valid until perl next free mortal temps.
>> > (There are faster ways of doing that but the use case for the numeric
>> > keys isn't speed critical.)
>> >
>> Suck as, if you don't mind me asking?
>
> ("Such as", I presume :)
>
Yes! Good thing I did not double bounce on the 's' or it could have been
rather confusing.
> Or, more deeply, instead of sorting an array of int's you could sort an
> array of { int key_as_int, char *ptr_to_key } structs. That way you'd
> also avoid the potential risk of problems with keys like "01" or "1.0"
> that change when converted to int and back to a string.
>
That was my first thought, but I gave up on in when I realised that I
would have to figure a place to put the typedef
I changed the code to use a struct (although I just threw the declaration
in ).
It makes it cleaner and gets rid of the sv_2mortal'd copy of the keys.
>> >>
> Whatever seems reasonable (and best for ShowErrorStatement usage).
>
I went and had the wrapper unSVify the params and call _join_hash_sorted().
This
way there is no need to create a bunch of SVs that get thrown away.
-r
==== Patch <concat_hash.patch> level 1
Source: 2032998a-08c0-11dd-b685-1998e50afd6a:/local/dbi:863
Target: 50811bd7-b8ce-0310-adc1-d9db26280581:/dbi:11280
(http://svn.perl.org/modules/dbi)
Log:
[EMAIL PROTECTED]: rlippan | 2008-05-24 11:30:46 -0400
Make local branch of dbi
[EMAIL PROTECTED]: rlippan | 2008-05-24 12:29:52 -0400
before changing hash_sort.
[EMAIL PROTECTED]: rlippan | 2008-05-24 12:30:28 -0400
Add test script
[EMAIL PROTECTED]: rlippan | 2008-05-24 12:49:09 -0400
make join_hash_sorted not take SV* here ints/char* would do.
[EMAIL PROTECTED]: rlippan | 2008-05-24 14:35:23 -0400
use struct when sorting.
[EMAIL PROTECTED]: rlippan | 2008-05-24 14:35:53 -0400
remove warnings. turn off benchmark tests
=== trunk/DBI.xs
==================================================================
--- trunk/DBI.xs (revision 11280)
+++ trunk/DBI.xs (patch concat_hash.patch level 1)
@@ -209,6 +209,141 @@
return buf;
}
+typedef struct num_srt_info {
+ char *key;
+ UV numeric;
+} num_srt_info;
+
+static int
+_cmp_number (val1, val2)
+ const void *val1;
+ const void *val2;
+{
+ int first, second;
+
+ first = ((struct num_srt_info *)val1)->numeric;
+ second = ((struct num_srt_info *)val2)->numeric;
+
+ if (first == second)
+ return 0;
+ else if (first > second)
+ return 1;
+ else
+ return -1;
+}
+
+static int
+_cmp_str (val1, val2)
+ const void *val1;
+ const void *val2;
+{
+ return strcmp( *(char **)val1, *(char **)val2);
+}
+
+static char **
+_sort_hash_keys (hash, sort_order, total_length)
+ HV *hash;
+ int sort_order;
+ STRLEN *total_length;
+{
+ dTHX;
+ I32 hv_len, key_len;
+ HE *entry;
+ char **keys;
+ unsigned int idx = 0;
+ STRLEN tot_len = 0;
+ bool numberish = 1;
+ struct num_srt_info *numbers;
+
+ hv_len = hv_iterinit(hash);
+ if (!hv_len)
+ return 0;
+
+ Newx(keys, hv_len, char *);
+ Newx(numbers, hv_len, struct num_srt_info);
+
+ while ((entry = hv_iternext(hash))) {
+ *(keys+idx) = hv_iterkey(entry, &key_len);
+ tot_len += key_len;
+
+ if (grok_number(*(keys+idx), key_len, &(numbers+idx)->numeric) != IS_NUMBER_IN_UV)
+ numberish = 0;
+
+ (numbers+idx)->key = *(keys+idx);
+ ++idx;
+ }
+
+ if (0 != total_length)
+ *total_length = tot_len;
+
+ if (sort_order <0)
+ sort_order = numberish ? 1 : 0;
+
+ if (0 == sort_order || 0 == numberish ) {
+ qsort(keys, hv_len, sizeof(char*), _cmp_str);
+ } else {
+ qsort(numbers, hv_len, sizeof(struct num_srt_info), _cmp_number);
+ for (idx = 0; idx < hv_len; ++idx)
+ *(keys+idx) = (numbers+idx)->key;
+/* SvPV_nolen(sv_2mortal(newSViv(numbers[idx]))); */
+ }
+
+ Safefree(numbers);
+ return keys;
+}
+
+
+static SV *
+_join_hash_sorted (hash, kv_sep, pair_sep, not_neat, sort)
+ HV *hash;
+ char *kv_sep;
+ char *pair_sep;
+ int not_neat;
+ int sort;
+{
+ dTHX;
+ I32 hv_len;
+ STRLEN kv_sep_len, pair_sep_len, hv_val_len, total_len = 0;
+ char **keys;
+ char *value_string;
+ unsigned int i = 0;
+ SV **hash_svp;
+ SV *return_sv;
+
+ kv_sep_len = strlen(kv_sep);
+ pair_sep_len = strlen(pair_sep);
+
+ keys = _sort_hash_keys(hash, sort, &total_len);
+ if (!keys)
+ return newSVpv("", 0);
+
+ hv_len = hv_iterinit(hash);
+ /* total_len += Separators + quotes + term null */
+ total_len += kv_sep_len*hv_len + pair_sep_len*hv_len+2*hv_len+1;
+ return_sv = newSV(total_len);
+ sv_setpv(return_sv, ""); /* quell undef warnings */
+
+ for (i=0; i<hv_len; ++i) {
+ hash_svp = hv_fetch(hash, keys[i], strlen(keys[i]), 0);
+ if (!hash_svp) {
+ warn("No Hash entry with key: %s", keys[i]);
+ continue;
+ }
+
+ value_string = (not_neat) ?
+ (SvOK(*hash_svp) ? SvPV(*hash_svp, hv_val_len) : "") :
+ neatsvpv(*hash_svp,0);
+ sv_catpvf(return_sv, (not_neat) ? "%s%s'%s'%s" : "%s%s%s%s",
+ keys[i], kv_sep, value_string, (i<hv_len-1) ? pair_sep : "");
+ }
+
+ Safefree(keys);
+
+ return return_sv;
+}
+
+
+
/* handy for embedding into condition expression for debugging */
/*
static int warn1(char *s) { warn(s); return 1; }
@@ -4236,6 +4371,43 @@
RETVAL
+SV *
+_concat_hash_sorted (hash, kv_sep_sv, pair_sep_sv, value_format_sv,sort_type_sv)
+ HV *hash
+ SV *kv_sep_sv
+ SV *pair_sep_sv
+ SV *value_format_sv
+ SV *sort_type_sv
+
+ PREINIT:
+
+ STRLEN kv_sep_len, pair_sep_len;
+ char *kv_sep, *pair_sep;
+ int not_neat, sort;
+
+ CODE:
+
+ kv_sep = SvPV(kv_sep_sv, kv_sep_len);
+ pair_sep = SvPV(pair_sep_sv, pair_sep_len);
+
+ if (SvGMAGICAL(value_format_sv))
+ mg_get(value_format_sv);
+ not_neat = SvTRUE(value_format_sv);
+
+
+ sort = (SvOK(sort_type_sv)) ? SvIV(sort_type_sv) : -1;
+
+
+ RETVAL = _join_hash_sorted(hash,kv_sep, pair_sep, not_neat, sort);
+
+ OUTPUT:
+ RETVAL
+
+
+
+
+
+
MODULE = DBI PACKAGE = DBI::var
void
=== trunk/t/ConcatHash.t
==================================================================
--- trunk/t/ConcatHash.t (revision 11280)
+++ trunk/t/ConcatHash.t (patch concat_hash.patch level 1)
@@ -0,0 +1,191 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl CatHash.t'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use Test::More tests => 36;
+BEGIN { use_ok('DBI') };
+no warnings 'uninitialized';
+
+# null and undefs -- segfaults?;
+is (DBI::_concat_hash_sorted({ }, "=", ":", 0, undef), "");
+eval {DBI::_concat_hash_sorted(undef, "=", ":", 0, undef), undef};
+like ($@ || "", qr/hash is not a hash reference/); #XXX check this
+is (DBI::_concat_hash_sorted({ }, undef, ":", 0, undef), "");
+
+is (DBI::_concat_hash_sorted({ }, "=", undef, 0, undef), "");
+is (DBI::_concat_hash_sorted({ }, "=", ":", undef, undef),"");
+
+# Simple segfault tests?
+ok(DBI::_concat_hash_sorted({bob=>'two', fred=>'one' }, "="x12000, ":", 1, undef));
+ok(DBI::_concat_hash_sorted({bob=>'two', fred=>'one' }, "=", ":"x12000, 1, undef));
+ok(DBI::_concat_hash_sorted({map {$_=>undef} (1..1000)}, "="x12000, ":", 1, undef));
+ok(DBI::_concat_hash_sorted({map {$_=>undef} (1..10000)}, "=", ":"x12000, 1, undef), 'test');
+ok(DBI::_concat_hash_sorted({map {$_=>undef} (1..100)}, "="x12000, ":"x12000, 1, undef), 'test');
+
+my $simple_hash = {
+ bob=>"there",
+ jack=>12,
+ fred=>"there",
+ norman=>"there",
+ # sam =>undef
+};
+
+my $simple_numeric = {
+ 1=>"there",
+ 2=>"there",
+ 3=>"there",
+ 32=>"there",
+ 16 => 'yo',
+ 07 => "buddy",
+ 49 => undef,
+};
+
+my $simple_mixed = {
+ bob=>"there",
+ jack=>12,
+ fred=>"there",
+ norman=>"there",
+ sam =>undef,
+ 1=>"there",
+ 2=>"there",
+ 3=>"there",
+ 32=>"there",
+ 16 => 'yo',
+ 07 => "buddy",
+ 49 => undef,
+};
+
+my $simple_float = {
+ 1.12 =>"there",
+ 3.1415926 =>"there",
+ 2.718281828 =>"there",
+ 32=>"there",
+ 1.6 => 'yo',
+ 0.78 => "buddy",
+ 49 => undef,
+};
+
+#eval {
+# DBI::_concat_hash_sorted($simple_hash, "=",,":",1,12);
+#};
+ok(1," Unknown sort order");
+#like ($@, qr/Unknown sort order/, "Unknown sort order");
+
+
+
+## Loopify and Add Neat
+
+
+my %neats = (
+ "Neat"=>0,
+ "Not Neat"=> 1
+);
+my %sort_types = (
+ guess=>undef,
+ numeric => 1,
+ lexical=> 0
+);
+my %hashes = (
+ Numeric=>$simple_numeric,
+ "Simple Hash" => $simple_hash,
+ "Mixed Hash" => $simple_mixed,
+ "Float Hash" => $simple_float
+);
+
+for $sort_type (keys %sort_types){
+ for $neat (keys %neats) {
+ for $hash(keys %hashes) {
+ test_concat_hash($hash, $neat, $sort_type);
+ }
+ }
+}
+
+sub test_concat_hash {
+ my ($hash, $neat, $sort_type) = @_;
+ is (
+ #DBI::_concat_hash_sorted(
+ _concat_hash_sorted(
+ $hashes{$hash}, "=", ":",$neats{$neat}, $sort_types{$sort_type}
+ ),
+ _concat_hash_sorted(
+ $hashes{$hash} , "=", ":",$neats{$neat}, $sort_types{$sort_type}
+ ),
+ "$hash - $neat $sort_type"
+ );
+}
+
+if (0) {
+ eval {
+ use Benchmark qw(:all);
+ cmpthese(200_000, {
+ Perl => sub {_concat_hash_sorted($simple_hash, "=", ":",0,undef); },
+ C=> sub {DBI::_concat_hash_sorted($simple_hash, "=", ":",0,1);}
+ });
+
+ print "\n";
+ cmpthese(200_000, {
+ NotNeat => sub {DBI::_concat_hash_sorted(
+ $simple_hash, "=", ":",1,undef);
+ },
+ Neat => sub {DBI::_concat_hash_sorted(
+ $simple_hash, "=", ":",0,undef);
+ }
+ });
+ };
+
+}
+#CatHash::_concat_hash_values({ }, ":-",,"::",1,1);
+
+
+sub _concat_hash_sorted {
+ my ( $hash_ref, $kv_separator, $pair_separator, $value_format, $sort_type ) = @_;
+ # $value_format: false=use neat(), true=dumb quotes
+ # $sort_type: 0=lexical, 1=numeric, undef=try to guess
+
+ $keys = _get_sorted_hash_keys($hash_ref, $sort_type);
+ my $string = '';
+ for my $key (@$keys) {
+ $string .= $pair_separator if length $string > 0;
+ my $value = $hash_ref->{$key};
+ if ($value_format) {
+ $value = (defined $value) ? "'$value'" : 'undef';
+ }
+ else {
+ $value = DBI::neat($value,0);
+ }
+ $string .= $key . $kv_separator . $value;
+ }
+ return $string;
+}
+
+use Scalar::Util qw(looks_like_number);
+sub _get_sorted_hash_keys {
+ my ($hash_ref, $sort_type) = @_;
+ my $sort_guess = 1;
+ if (not defined $sort_type) {
+ #my $first_key = (each %$hash_ref)[0];
+ #$sort_type = looks_like_number($first_key);
+
+ $sort_guess =
+ (1!=looks_like_number($_)) ? 0:$sort_guess for keys %$hash_ref;
+ $sort_type = $sort_guess unless (defined $sort_type);
+ }
+
+ my @keys = keys %$hash_ref;
+ no warnings 'numeric';
+ return [ ($sort_type && $sort_guess)
+ ? sort {$a <=> $b} @keys
+ : sort @keys
+ ];
+}
+
+
+
+#########################
+
+# Insert your test code below, the Test::More module is use()ed here so read
+# its man page ( perldoc Test::More ) for help writing this test script.
+
==== BEGIN SVK PATCH BLOCK ====
Version: svk v2.0.2 (linux)
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
==== END SVK PATCH BLOCK ====