File Coverage

blib/lib/Data/UUID/MT.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1 1     1   49239 use 5.006;
  1         5  
  1         78  
2 1     1   9 use strict;
  1         2  
  1         45  
3 1     1   6 use warnings;
  1         2  
  1         94  
4              
5             package Data::UUID::MT;
6             our $VERSION = '1.001'; # VERSION
7              
8 1     1   4 use Config;
  1         2  
  1         61  
9 1     1   3015 use Math::Random::MT::Auto;
  0            
  0            
10             use Scalar::Util 1.10 ();
11             use Time::HiRes ();
12              
13             # track objects across threads for reseeding
14             my ($can_weaken, @objects);
15             $can_weaken = Scalar::Util->can('weaken');
16             sub CLONE { defined($_) && $_->reseed for @objects }
17              
18             # HoH: $builders{$Config{uvsize}}{$version}
19             my %builders = (
20             '8' => {
21             '1' => ($] ge 5.010 ? '_build_64bit_v1' : '_build_64bit_v1_old' ),
22             '4' => ($] ge 5.010 ? '_build_64bit_v4' : '_build_64bit_v4_old' ),
23             '4s' => ($] ge 5.010 ? '_build_64bit_v4s' : '_build_64bit_v4s_old'),
24             },
25             '4' => {
26             '1' => '_build_32bit_v1',
27             '4' => '_build_32bit_v4',
28             '4s' => '_build_32bit_v4s',
29             }
30             );
31              
32             sub new {
33             my ($class, %args) = @_;
34             $args{version} = 4 unless defined $args{version};
35             Carp::croak "Unsupported UUID version '$args{version}'"
36             unless $args{version} =~ /^(?:1|4|4s)$/;
37             my $int_size = $Config{uvsize};
38             Carp::croak "Unsupported integer size '$int_size'"
39             unless $int_size == 4 || $int_size == 8;
40              
41             my $prng = Math::Random::MT::Auto->new;
42              
43             my $self = {
44             _prng => $prng,
45             _version => $args{version},
46             };
47              
48             bless $self, $class;
49              
50             $self->{_iterator} = $self->_build_iterator;
51              
52             if ($can_weaken) {
53             push @objects, $self;
54             Scalar::Util::weaken($objects[-1]);
55             }
56              
57             return $self;
58             }
59              
60             sub _build_iterator {
61             my $self = shift;
62             # get the iterator based on int size and UUID version
63             my $int_size = $Config{uvsize};
64             my $builder = $builders{$int_size}{$self->{_version}};
65             return $self->$builder;
66             }
67              
68             sub create {
69             return shift->{_iterator}->();
70             }
71              
72             sub create_hex {
73             return "0x" . unpack("H*", shift->{_iterator}->() );
74             }
75              
76             sub create_string {
77             return join "-", unpack("H8H4H4H4H12", shift->{_iterator}->());
78             }
79              
80             sub iterator {
81             return shift->{_iterator};
82             }
83              
84             sub reseed {
85             my $self = shift;
86             $self->{_prng}->srand(@_ ? @_ : ());
87             }
88              
89             #--------------------------------------------------------------------------#
90             # UUID algorithm closure generators
91             #--------------------------------------------------------------------------#
92              
93             sub _build_64bit_v1 {
94             my $self = shift;
95             my $gregorian_offset = 12219292800 * 10_000_000;
96             my $prng = $self->{_prng};
97             my $pid = $$;
98              
99             return sub {
100             if ($$ != $pid) {
101             $prng->srand();
102             $pid = $$;
103             }
104             my ($sec,$usec) = Time::HiRes::gettimeofday();
105             my $raw_time = pack("Q>", $sec*10_000_000 + $usec*10 + $gregorian_offset);
106             # UUID v1 shuffles the time bits around
107             my $uuid = substr($raw_time,4,4)
108             . substr($raw_time,2,2)
109             . substr($raw_time,0,2)
110             . pack("Q>", $prng->irand);
111             vec($uuid, 87, 1) = 0x1; # force MAC multicast bit on per RFC
112             vec($uuid, 13, 4) = 0x1; # set UUID version
113             vec($uuid, 35, 2) = 0x2; # set UUID variant
114             return $uuid;
115             }
116             }
117              
118             # For Perl < v5.10, can't use "Q>" in pack
119             sub _build_64bit_v1_old {
120             my $self = shift;
121             my $gregorian_offset = 12219292800 * 10_000_000;
122             my $prng = $self->{_prng};
123             my $pid = $$;
124              
125             return sub {
126             if ($$ != $pid) {
127             $prng->srand();
128             $pid = $$;
129             }
130             my ($sec,$usec) = Time::HiRes::gettimeofday();
131             my $time_sum = $sec*10_000_000 + $usec*10 + $gregorian_offset;
132             my $raw_time = pack("N2", $time_sum >> 32, $time_sum );
133             # UUID v1 shuffles the time bits around
134             my $irand = $prng->irand;
135             my $uuid = substr($raw_time,4,4)
136             . substr($raw_time,2,2)
137             . substr($raw_time,0,2)
138             . pack("N2", $irand >> 32, $irand);
139             vec($uuid, 87, 1) = 0x1; # force MAC multicast bit on per RFC
140             vec($uuid, 13, 4) = 0x1; # set UUID version
141             vec($uuid, 35, 2) = 0x2; # set UUID variant
142             return $uuid;
143             }
144             }
145              
146             sub _build_32bit_v1 {
147             my $self = shift;
148             my $prng = $self->{_prng};
149             my $pid = $$;
150              
151             return sub {
152             if ($$ != $pid) {
153             $prng->srand();
154             $pid = $$;
155             }
156             # Adapted from UUID::Tiny
157             my $timestamp = Time::HiRes::time();
158              
159             # hi = time mod (1000000 / 0x100000000)
160             my $hi = int( $timestamp / 65536.0 / 512 * 78125 );
161             $timestamp -= $hi * 512.0 * 65536 / 78125;
162             my $low = int( $timestamp * 10000000.0 + 0.5 );
163              
164             # MAGIC offset: 01B2-1DD2-13814000
165             if ( $low < 0xec7ec000 ) {
166             $low += 0x13814000;
167             }
168             else {
169             $low -= 0xec7ec000;
170             $hi++;
171             }
172              
173             if ( $hi < 0x0e4de22e ) {
174             $hi += 0x01b21dd2;
175             }
176             else {
177             $hi -= 0x0e4de22e; # wrap around
178             }
179              
180             # UUID v1 shuffles the time bits around
181             my $uuid = pack( 'NnnNN',
182             $low, $hi & 0xffff, ( $hi >> 16 ) & 0x0fff, $prng->irand, $prng->irand
183             );
184             vec($uuid, 87, 1) = 0x1; # force MAC multicast bit on per RFC
185             vec($uuid, 13, 4) = 0x1; # set UUID version
186             vec($uuid, 35, 2) = 0x2; # set UUID variant
187             return $uuid;
188             }
189             }
190              
191             sub _build_64bit_v4 {
192             my $self = shift;
193             my $prng = $self->{_prng};
194             my $pid = $$;
195              
196             return sub {
197             if ($$ != $pid) {
198             $prng->srand();
199             $pid = $$;
200             }
201             my $uuid = pack("Q>2", $prng->irand, $prng->irand);
202             vec($uuid, 13, 4) = 0x4; # set UUID version
203             vec($uuid, 35, 2) = 0x2; # set UUID variant
204             return $uuid;
205             }
206             }
207              
208             # For Perl < v5.10, can't use "Q>" in pack
209             sub _build_64bit_v4_old {
210             my $self = shift;
211             my $prng = $self->{_prng};
212             my $pid = $$;
213              
214             return sub {
215             if ($$ != $pid) {
216             $prng->srand();
217             $pid = $$;
218             }
219             my @irand = ($prng->irand, $prng->irand);
220             my $uuid = pack("N4",
221             $irand[0] >> 32, $irand[0], $irand[1] >> 32, $irand[1]
222             );
223             vec($uuid, 13, 4) = 0x4; # set UUID version
224             vec($uuid, 35, 2) = 0x2; # set UUID variant
225             return $uuid;
226             }
227             }
228              
229             sub _build_32bit_v4 {
230             my $self = shift;
231             my $prng = $self->{_prng};
232             my $pid = $$;
233              
234             return sub {
235             if ($$ != $pid) {
236             $prng->srand();
237             $pid = $$;
238             }
239             my $uuid = pack("N4",
240             $prng->irand, $prng->irand, $prng->irand, $prng->irand
241             );
242             vec($uuid, 13, 4) = 0x4; # set UUID version
243             vec($uuid, 35, 2) = 0x2; # set UUID variant
244             return $uuid;
245             }
246             }
247              
248             # "4s" is custom "random" with sequential override based on
249             # 100 nanosecond intervals since epoch
250             sub _build_64bit_v4s {
251             my $self = shift;
252             my $prng = $self->{_prng};
253             my $pid = $$;
254              
255             return sub {
256             if ($$ != $pid) {
257             $prng->srand();
258             $pid = $$;
259             }
260             my ($sec,$usec) = Time::HiRes::gettimeofday();
261             my $uuid = pack("Q>2",
262             $sec*10_000_000 + $usec*10, $prng->irand
263             );
264             # rotate last timestamp bits to make room for version field
265             vec($uuid, 14, 4) = vec($uuid, 15, 4);
266             vec($uuid, 15, 4) = vec($uuid, 12, 4);
267             vec($uuid, 12, 4) = vec($uuid, 13, 4);
268             vec($uuid, 13, 4) = 0x4; # set UUID version
269             vec($uuid, 35, 2) = 0x2; # set UUID variant
270             return $uuid;
271             }
272             }
273              
274             # "4s" is custom "random" with sequential override based on
275             # 100 nanosecond intervals since epoch
276             # For Perl < v5.10, can't use "Q>" in pack
277             sub _build_64bit_v4s_old {
278             my $self = shift;
279             my $prng = $self->{_prng};
280             my $pid = $$;
281              
282             return sub {
283             if ($$ != $pid) {
284             $prng->srand();
285             $pid = $$;
286             }
287             my ($sec,$usec) = Time::HiRes::gettimeofday();
288             my @parts = ($sec*10_000_000 + $usec*10, $prng->irand);
289             my $uuid = pack("N4",
290             $parts[0] >> 32, $parts[0], $parts[1] >> 32, $parts[1]
291             );
292             # rotate last timestamp bits to make room for version field
293             vec($uuid, 14, 4) = vec($uuid, 15, 4);
294             vec($uuid, 15, 4) = vec($uuid, 12, 4);
295             vec($uuid, 12, 4) = vec($uuid, 13, 4);
296             vec($uuid, 13, 4) = 0x4; # set UUID version
297             vec($uuid, 35, 2) = 0x2; # set UUID variant
298             return $uuid;
299             }
300             }
301              
302              
303             # "4s" is custom "random" with sequential override based on
304             # 100 nanosecond intervals since epoch
305             sub _build_32bit_v4s {
306             my $self = shift;
307             my $prng = $self->{_prng};
308             my $pid = $$;
309              
310             return sub {
311             if ($$ != $pid) {
312             $prng->srand();
313             $pid = $$;
314             }
315             # Adapted from UUID::Tiny
316             my $timestamp = Time::HiRes::time();
317              
318             # hi = time mod (1000000 / 0x100000000)
319             my $hi = int( $timestamp / 65536.0 / 512 * 78125 );
320             $timestamp -= $hi * 512.0 * 65536 / 78125;
321             my $low = int( $timestamp * 10000000.0 + 0.5 );
322              
323             # MAGIC offset: 01B2-1DD2-13814000
324             if ( $low < 0xec7ec000 ) {
325             $low += 0x13814000;
326             }
327             else {
328             $low -= 0xec7ec000;
329             $hi++;
330             }
331              
332             if ( $hi < 0x0e4de22e ) {
333             $hi += 0x01b21dd2;
334             }
335             else {
336             $hi -= 0x0e4de22e; # wrap around
337             }
338              
339             my $uuid = pack("N4", $hi, $low, $prng->irand, $prng->irand);
340             # rotate last timestamp bits to make room for version field
341             vec($uuid, 14, 4) = vec($uuid, 15, 4);
342             vec($uuid, 15, 4) = vec($uuid, 12, 4);
343             vec($uuid, 12, 4) = vec($uuid, 13, 4);
344             vec($uuid, 13, 4) = 0x4; # set UUID version
345             vec($uuid, 35, 2) = 0x2; # set UUID variant
346             return $uuid;
347             }
348             }
349              
350             1;
351              
352             # ABSTRACT: Fast random UUID generator using the Mersenne Twister algorithm
353              
354              
355             # vim: ts=2 sts=2 sw=2 et:
356              
357             __END__
358              
359             =pod
360              
361             =encoding utf-8
362              
363             =head1 NAME
364              
365             Data::UUID::MT - Fast random UUID generator using the Mersenne Twister algorithm
366              
367             =head1 VERSION
368              
369             version 1.001
370              
371             =head1 SYNOPSIS
372              
373             use Data::UUID::MT;
374             my $ug1 = Data::UUID::MT->new( version => 4 ); # "1", "4" or "4s"
375             my $ug2 = Data::UUID::MT->new(); # default is "4"
376              
377             # method interface
378             my $uuid1 = $ug->create(); # 16 byte binary string
379             my $uuid2 = $ug->create_hex();
380             my $uuid3 = $ug->create_string();
381              
382             # iterator -- avoids some method call overhead
383             my $next = $ug->iterator;
384             my $uuid4 = $next->();
385              
386             =head1 DESCRIPTION
387              
388             This UUID generator uses the excellent L<Math::Random::MT::Auto> module
389             as a source of fast, high-quality (pseudo) random numbers.
390              
391             Three different types of UUIDs are supported. Two are consistent with
392             RFC 4122 and one is a custom variant that provides a 'sequential UUID'
393             that can be advantageous when used as a primary database key.
394              
395             B<Note>: The Mersenne Twister pseudo-random number generator has excellent
396             statistical properties, but it is not considered cryptographically secure.
397             Pseudo-random UUIDs are not recommended for use as security authentication
398             tokens in cookies or other user-visible session identifiers.
399              
400             =head2 Version 1 UUIDs
401              
402             The UUID generally follows the "version 1" spec from the RFC, however the clock
403             sequence and MAC address are randomly generated each time. (This is
404             permissible within the spec of the RFC.) The generated MAC address has the
405             the multicast bit set as mandated by the RFC to ensure it does not
406             conflict with real MAC addresses. This UUID has 60 bits of timestamp data,
407             61 bits of pseudo-random data and 7 mandated bits (multicast bit, "variant"
408             field and "version" field).
409              
410             =head2 Version 4 UUIDs
411              
412             The UUID follows the "version 4" spec, with 122 pseudo-random bits and
413             6 mandated bits ("variant" field and "version" field).
414              
415             =head2 Version 4s UUIDs
416              
417             This is a custom UUID form that resembles "version 4" form, but that overlays
418             the first 60 bits with a timestamp akin to "version 1", Unlike "version 1",
419             this custom version preserves the ordering of bits from high to low, whereas
420             "version 1" puts the low 32 bits of the timestamp first, then the middle 16
421             bits, then multiplexes the high bits with version field. This "4s" variant
422             provides a "sequential UUID" with the timestamp providing order and the
423             remaining random bits making collision with other UUIDs created at the exact
424             same microsecond highly unlikely. This UUID has 60 timestamp bits, 62
425             pseudo-random bits and 6 mandated bits ("variant" field and "version" field).
426              
427             =head2 Unsupported: Versions 2, 3 and 5
428              
429             This module focuses on generation of UUIDs with random elements and does not
430             support UUID versions 2, 3 and 5.
431              
432             =head1 METHODS
433              
434             =head2 new
435              
436             my $ug = Data::UUID::MT->new( version => 4 );
437              
438             Creates a UUID generator object. The only allowed versions are
439             "1", "4" and "4s". If no version is specified, it defaults to "4".
440              
441             =head2 create
442              
443             my $uuid = $ug->create;
444              
445             Returns a UUID packed into a 16 byte string.
446              
447             =head2 create_hex
448              
449             my $uuid = $ug->create_hex();
450              
451             Returns a UUID as a lowercase hex string, prefixed with "0x", e.g.
452             C<0xb0470602a64b11da863293ebf1c0e05a>
453              
454             =head2 create_string
455              
456             my $uuid = $ug->create_string(); #
457              
458             Returns UUID as a lowercase string in "standard" format, e.g.
459             C<b0470602-a64b-11da-8632-93ebf1c0e05a>
460              
461             =head2 iterator
462              
463             my $next = $ug->iterator;
464             my $uuid = $next->();
465              
466             Returns a reference to the internal UUID generator function. Because this
467             avoids method call overhead, it is slightly faster than calling C<create>.
468              
469             =head2 reseed
470              
471             $ug->reseed;
472              
473             Reseeds the internal pseudo-random number generator. This happens
474             automatically after a fork or thread creation (assuming Scalar::Util::weaken),
475             but may be called manually if desired for some reason.
476              
477             Any arguments provided are passed to Math::Random::MT::Auto::srand() for
478             custom seeding.
479              
480             $ug->reseed('hotbits' => 250, '/dev/random');
481              
482             =for Pod::Coverage method_names_here
483              
484             =head1 UUID STRING REPRESENTATIONS
485              
486             A UUID contains 16 bytes. A hex string representation looks like
487             C<0xb0470602a64b11da863293ebf1c0e05a>. A "standard" representation
488             looks like C<b0470602-a64b-11da-8632-93ebf1c0e05a>. Sometimes
489             these are seen in upper case and on Windows the standard format is
490             often seen wrapped in parentheses.
491              
492             Converting back and forth is easy with C<pack> and C<unpack>.
493              
494             # string to 16 bytes
495             $string =~ s/^0x//i; # remove leading "0x"
496             $string =~ tr/()-//d; # strip '-' and parentheses
497             $binary = pack("H*", $string);
498              
499             # 16 bytes to uppercase string formats
500             $hex = "0x" . uc unpack("H*", $binary);
501             $std = uc join "-", unpack("H8H4H4H4H12", $binary);
502              
503             If you need a module that provides these conversions for you, consider
504             L<UUID::Tiny>.
505              
506             =head1 COMPARISON TO OTHER UUID MODULES
507              
508             At the time of writing, there are five other general purpose UUID generators on
509             CPAN that I consider potential alternatives. Data::UUID::MT is included in
510             the discussion below for comparison.
511              
512             =over 4
513              
514             =item *
515              
516             L<Data::GUID> - version 1 UUIDs (wrapper around Data::UUID)
517              
518             =item *
519              
520             L<Data::UUID> - version 1 or 3 UUIDs (derived from RFC 4122 code)
521              
522             =item *
523              
524             L<Data::UUID::LibUUID> - version 1 or 4 UUIDs (libuuid)
525              
526             =item *
527              
528             L<UUID> - version 1 or 4 UUIDs (libuuid)
529              
530             =item *
531              
532             L<UUID::Tiny> - versions 1, 3, 4, or 5 (pure perl)
533              
534             =item *
535              
536             L<Data::UUID::MT> - version 1 or 4 (or custom sequential "4s")
537              
538             =back
539              
540             C<libuuid> based UUIDs may generally be either version 4 (preferred) or version
541             1 (fallback), depending on the availability of a good random bit source (e.g.
542             /dev/random). C<libuuid> version 1 UUIDs could also be provided by the
543             C<uuidd> daemon if available.
544              
545             UUID.pm leaves the choice of version up to C<libuuid>. Data::UUID::LibUUID
546             does so by default, but also allows specifying a specific version. Note that
547             Data::UUID::LibUUID incorrectly refers to version 1 UUIDs as version 2 UUIDs.
548             For example, to get a version 1 binary UUID explicitly, you would call
549             C<Data::UUID::LibUUID::new_uuid_binary(2)>.
550              
551             In addition to differences mentioned below, there are additional slight
552             difference in how the modules (or C<libuuid>) treat the "clock sequence" field
553             and otherwise attempt to keep state between calls, but this is generally
554             immaterial.
555              
556             =head2 Use of Ethernet MAC addresses
557              
558             Version 1 UUID generators differ in whether they include the Ethernet MAC
559             address as a "node identifier" as specified in RFC 4122. Including the MAC
560             has security implications as Version 1 UUIDs can then be traced to a
561             particular machine at a particular time.
562              
563             For C<libuuid> based modules, Version 1 UUIDs will include the actual MAC
564             address, if available, or will substitute a random MAC (with multicast bit
565             set).
566              
567             Data::UUID version 1 UUIDs do not contain the MAC address, but replace
568             it with an MD5 hash of data including the hostname and host id (possibly
569             just the IP address), modified with the multicast bit.
570              
571             Both UUID::Tiny and Data::UUID::MT version 1 UUIDs do not contain the actual
572             MAC address, but replace it with a random multicast MAC address.
573              
574             =head2 Source of random bits
575              
576             All the modules differ in the source of random bits.
577              
578             C<libuuid> based modules get random bits from C</dev/random> or C</dev/urandom>
579             or fall back to a pseudo-random number generator.
580              
581             Data::UUID only uses random data to see the clock sequence and gets bits from
582             the C C<rand()> function.
583              
584             UUID::Tiny uses Perl's C<rand()> function.
585              
586             Data::UUID::MT gets random bits from L<Math::Random::MT::Auto>, which uses the
587             Mersenne Twister algorithm. Math::Random::MT::Auto seeds from system sources
588             (including Win32 specific ones on that platform) if available and falls back to
589             other less ideal sources if not.
590              
591             =head2 Fork and thread safety
592              
593             Pseudo-random number generators used in generating UUIDs should be reseeded if
594             the process forks or if threads are created.
595              
596             Data::UUID::MT checks if the process ID has changed before generating a UUID
597             and reseeds if necessary. If L<Scalar::Util> is installed and provides
598             C<weaken()>, Data::UUID::MT will also reseed its objects on thread creation.
599              
600             Data::UUID::LibUUID will reseed on fork on Mac OSX.
601              
602             I have not explored further whether other UUID generators are fork/thread safe.
603              
604             =head2 Benchmarks
605              
606             The F<examples/bench.pl> program included with this module does some simple
607             benchmarking of UUID generation speeds. Here is the output from my desktop
608             system (AMD Phenom II X6 1045T CPU). Note that "v?" is used where the choice
609             is left to C<libuuid> -- which will result in version 4 UUIDs on my system.
610              
611             Benchmark on Perl v5.14.0 for x86_64-linux with 8 byte integers.
612              
613             Key:
614             U => UUID 0.02
615             UT => UUID::Tiny 1.03
616             DG => Data::GUID 0.046
617             DU => Data::UUID 1.217
618             DULU => Data::UUID::LibUUID 0.05
619             DUMT => Data::UUID::MT 0.001
620              
621             Benchmarks are marked as to which UUID version is generated.
622             Some modules offer method ('meth') and function ('func') interfaces.
623              
624             UT|v1 85229/s
625             UT|v4 110652/s
626             DULU|v1 177495/s
627             DULU|v? 178629/s
628             DUMT|v4s|meth 274905/s
629             DUMT|v1|meth 281942/s
630             U|v? 288136/s
631             DULU|v4 295107/s
632             DUMT|v4s|func 307575/s
633             DUMT|v1|func 313538/s
634             DG|v1|func 335333/s
635             DG|v1|meth 373515/s
636             DUMT|v4|meth 450845/s
637             DUMT|v4|func 588573/s
638             DU|v1 1312946/s
639              
640             =head1 SEE ALSO
641              
642             =over 4
643              
644             =item *
645              
646             L<RFC 4122 A Universally Unique IDentifier (UUID) URN Namespace|http://www.apps.ietf.org/rfc/rfc4122.html>
647              
648             =back
649              
650             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
651              
652             =head1 SUPPORT
653              
654             =head2 Bugs / Feature Requests
655              
656             Please report any bugs or feature requests through the issue tracker
657             at L<https://github.com/dagolden/data-uuid-mt/issues>.
658             You will be notified automatically of any progress on your issue.
659              
660             =head2 Source Code
661              
662             This is open source software. The code repository is available for
663             public review and contribution under the terms of the license.
664              
665             L<https://github.com/dagolden/data-uuid-mt>
666              
667             git clone git://github.com/dagolden/data-uuid-mt.git
668              
669             =head1 AUTHOR
670              
671             David Golden <dagolden@cpan.org>
672              
673             =head1 CONTRIBUTOR
674              
675             Matt Koscica <matt.koscica@gmail.com>
676              
677             =head1 COPYRIGHT AND LICENSE
678              
679             This software is Copyright (c) 2011 by David Golden.
680              
681             This is free software, licensed under:
682              
683             The Apache License, Version 2.0, January 2004
684              
685             =cut