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 ====

Reply via email to