diff --git a/perl/lib/Data/MessagePack.pm b/perl/lib/Data/MessagePack.pm index afe37af3..4a1da179 100644 --- a/perl/lib/Data/MessagePack.pm +++ b/perl/lib/Data/MessagePack.pm @@ -5,6 +5,7 @@ use 5.008001; our $VERSION = '0.34'; our $PreferInteger = 0; +our $Canonical = 0; sub true () { require Data::MessagePack::Boolean; diff --git a/perl/lib/Data/MessagePack/PP.pm b/perl/lib/Data/MessagePack/PP.pm index f179ad74..65ce24b4 100644 --- a/perl/lib/Data/MessagePack/PP.pm +++ b/perl/lib/Data/MessagePack/PP.pm @@ -182,7 +182,12 @@ sub _pack { : $num < 2 ** 32 - 1 ? CORE::pack( 'CN', 0xdf, $num ) : _unexpected("number %d", $num) ; - return join( '', $header, map { _pack( $_ ) } %$value ); + + if ($Data::MessagePack::Canonical) { + return join( '', $header, map { _pack( $_ ), _pack($value->{$_}) } sort { $a cmp $b } keys %$value ); + } else { + return join( '', $header, map { _pack( $_ ) } %$value ); + } } elsif ( ref( $value ) eq 'Data::MessagePack::Boolean' ) { diff --git a/perl/t/17_canonical.t b/perl/t/17_canonical.t new file mode 100644 index 00000000..5389e2f9 --- /dev/null +++ b/perl/t/17_canonical.t @@ -0,0 +1,32 @@ + +use strict; +use warnings; +use Test::More; +use Data::MessagePack; + +$Data::MessagePack::Canonical = 1; + +my $data = { + 'foo' => { + 'a' => '', + 'b' => '', + 'c' => '', + 'd' => '', + 'e' => '', + 'f' => '', + 'g' => '', + } +}; + +my $packed1 = +Data::MessagePack->pack($data); +my $packed2 = +Data::MessagePack->pack(Data::MessagePack->unpack($packed1)); +my $packed3 = +Data::MessagePack->pack(Data::MessagePack->unpack($packed2)); +my $packed4 = +Data::MessagePack->pack(Data::MessagePack->unpack($packed3)); +my $packed5 = +Data::MessagePack->pack(Data::MessagePack->unpack($packed4)); + +is $packed1, $packed2; +is $packed1, $packed3; +is $packed1, $packed4; +is $packed1, $packed5; + +done_testing; diff --git a/perl/xs-src/pack.c b/perl/xs-src/pack.c index 862808eb..582b1202 100644 --- a/perl/xs-src/pack.c +++ b/perl/xs-src/pack.c @@ -211,9 +211,32 @@ STATIC_INLINE void _msgpack_pack_rv(enc_t *enc, SV* sv, int depth) { msgpack_pack_map(enc, count); - while ((he = hv_iternext(hval))) { - _msgpack_pack_sv(enc, hv_iterkeysv(he), depth); - _msgpack_pack_sv(enc, HeVAL(he), depth); + if (SvTRUE(get_sv("Data::MessagePack::Canonical", 0))) { + AV* keys = newAV(); + av_extend(keys, count); + + while ((he = hv_iternext(hval))) { + av_push(keys, SvREFCNT_inc(hv_iterkeysv(he))); + } + + int len = av_len(keys) + 1; + sortsv(AvARRAY(keys), len, Perl_sv_cmp); + + int i; + for (i=0; i