Author: timbo
Date: Tue May 27 04:11:28 2008
New Revision: 11327

Added:
   dbi/trunk/t/ConcatHash.t
Modified:
   dbi/trunk/DBI.xs

Log:
Apply original _concat_hash_sorted implementation patch from Rudolf Lippan.


Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs    (original)
+++ dbi/trunk/DBI.xs    Tue May 27 04:11:28 2008
@@ -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

Added: dbi/trunk/t/ConcatHash.t
==============================================================================
--- (empty file)
+++ dbi/trunk/t/ConcatHash.t    Tue May 27 04:11:28 2008
@@ -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.
+

Reply via email to