source: trunk/prototype/library/uuid/class.uuid.php @ 7655

Revision 7655, 10.6 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Melhorias de performance no codigo do Expresso.

Line 
1<?php
2/*-
3 * Copyright (c) 2011 Fredrik Lindberg - http://www.shapeshifter.se
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *        notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *        notice, this list of conditions and the following disclaimer in the
13 *        documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * Alternative this software might be licensed under the following license
27 *
28 *  Copyright 2011 Fredrik Lindberg
29 *
30 *  Licensed under the Apache License, Version 2.0 (the "License");
31 *  you may not use this file except in compliance with the License.
32 *  You may obtain a copy of the License at
33 *
34 *      http://www.apache.org/licenses/LICENSE-2.0
35 *
36 *  Unless required by applicable law or agreed to in writing, software
37 *  distributed under the License is distributed on an "AS IS" BASIS,
38 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39 *  See the License for the specific language governing permissions and
40 *  limitations under the License.
41 *
42 */
43
44/*
45 * UUID (RFC4122) Generator
46 * http://tools.ietf.org/html/rfc4122
47 *
48 * Implements version 1, 3, 4 and 5
49 */
50class UUID {
51        /* UUID versions */
52        const UUID_TIME  = 1;   /* Time based UUID */
53        const UUID_NAME_MD5      = 3;   /* Name based (MD5) UUID */
54        const UUID_RANDOM        = 4;   /* Random UUID */
55        const UUID_NAME_SHA1     = 5;   /* Name based (SHA1) UUID */
56
57        /* UUID formats */
58        const FMT_FIELD  = 100;
59        const FMT_STRING         = 101;
60        const FMT_BINARY         = 102;
61        const FMT_QWORD  = 1;   /* Quad-word, 128-bit (not impl.) */
62        const FMT_DWORD  = 2;   /* Double-word, 64-bit (not impl.) */
63        const FMT_WORD           = 4;   /* Word, 32-bit (not impl.) */
64        const FMT_SHORT         = 8;    /* Short (not impl.) */
65        const FMT_BYTE          = 16;   /* Byte */
66        const FMT_DEFAULT        = 16;
67
68        /* Field UUID representation */
69        static private $m_uuid_field = array(
70                'time_low' => 0,                /* 32-bit */
71                'time_mid' => 0,                /* 16-bit */
72                'time_hi' => 0,                 /* 16-bit */
73                'clock_seq_hi' => 0,            /*  8-bit */
74                'clock_seq_low' => 0,           /*  8-bit */
75                'node' => array()               /* 48-bit */
76        );
77
78        static private $m_generate = array(
79                self::UUID_TIME => "generateTime",
80                self::UUID_RANDOM => "generateRandom",
81                self::UUID_NAME_MD5 => "generateNameMD5",
82                self::UUID_NAME_SHA1 => "generateNameSHA1"
83        );
84
85        static private $m_convert = array(
86                self::FMT_FIELD => array(
87                        self::FMT_BYTE => "conv_field2byte",
88                        self::FMT_STRING => "conv_field2string",
89                        self::FMT_BINARY => "conv_field2binary"
90                ),
91                self::FMT_BYTE => array(
92                        self::FMT_FIELD => "conv_byte2field",
93                        self::FMT_STRING => "conv_byte2string",
94                        self::FMT_BINARY => "conv_byte2binary"
95                ),
96                self::FMT_STRING => array(
97                        self::FMT_BYTE => "conv_string2byte",
98                        self::FMT_FIELD => "conv_string2field",
99                        self::FMT_BINARY => "conv_string2binary"
100                ),
101        );
102
103        /* Swap byte order of a 32-bit number */
104        static private function swap32($x) {
105                return (($x & 0x000000ff) << 24) | (($x & 0x0000ff00) << 8) |
106                        (($x & 0x00ff0000) >> 8) | (($x & 0xff000000) >> 24);
107        }
108
109        /* Swap byte order of a 16-bit number */
110        static private function swap16($x) {
111                return (($x & 0x00ff) << 8) | (($x & 0xff00) >> 8);
112        }
113
114        /* Auto-detect UUID format */
115        static private function detectFormat($src) {
116                if (is_string($src))
117                        return self::FMT_STRING;
118                else if (is_array($src)) {
119                        $len = count($src);
120                        if ($len == 1 || ($len % 2) == 0)
121                                return $len;
122                        else
123                                return (-1);
124                }
125                else
126                        return self::FMT_BINARY;
127        }
128
129        /*
130         * Public API, generate a UUID of 'type' in format 'fmt' for
131         * the given namespace 'ns' and node 'node'
132         */
133        static public function generate($type, $fmt = self::FMT_BYTE,
134                $node = "", $ns = "") {
135                $func = self::$m_generate[$type];
136                if (!isset($func))
137                        return null;
138                $conv = self::$m_convert[self::FMT_FIELD][$fmt];
139
140                $uuid = self::$func($ns, $node);
141                return self::$conv($uuid);
142        }
143
144        /*
145         * Public API, convert a UUID from one format to another
146         */
147        static public function convert($uuid, $from, $to) {
148                $conv = self::$m_convert[$from][$to];
149                if (!isset($conv))
150                        return ($uuid);
151
152                return (self::$conv($uuid));
153        }
154
155        /*
156         * Generate an UUID version 4 (pseudo random)
157         */
158        static private function generateRandom($ns, $node) {
159                $uuid = self::$m_uuid_field;
160
161                $uuid['time_hi'] = (4 << 12) | (mt_rand(0, 0x1000));
162                $uuid['clock_seq_hi'] = (1 << 7) | mt_rand(0, 128);
163                $uuid['time_low'] = mt_rand(0, 0xffff) + (mt_rand(0, 0xffff) << 16);
164                $uuid['time_mid'] = mt_rand(0, 0xffff);
165                $uuid['clock_seq_low'] = mt_rand(0, 255);
166                for ($i = 0; $i < 6; ++$i)
167                        $uuid['node'][$i] = mt_rand(0, 255);
168                return ($uuid);
169        }
170
171        /*
172         * Generate UUID version 3 and 5 (name based)
173         */
174        static private function generateName($ns, $node, $hash, $version) {
175                $ns_fmt = self::detectFormat($ns);
176                $field = self::convert($ns, $ns_fmt, self::FMT_FIELD);
177
178                /* Swap byte order to keep it in big endian on all platforms */
179                $field['time_low'] = self::swap32($field['time_low']);
180                $field['time_mid'] = self::swap16($field['time_mid']);
181                $field['time_hi'] = self::swap16($field['time_hi']);
182
183                /* Convert the namespace to binary and concatenate node */
184                $raw = self::convert($field, self::FMT_FIELD, self::FMT_BINARY);
185                $raw .= $node;
186
187                /* Hash the namespace and node and convert to a byte array */
188                $val = $hash($raw, true);       
189                $tmp = unpack('C16', $val);
190                foreach (array_keys($tmp) as $key)
191                        $byte[$key - 1] = $tmp[$key];
192
193                /* Convert byte array to a field array */
194                $field = self::conv_byte2field($byte);
195
196                $field['time_low'] = self::swap32($field['time_low']);
197                $field['time_mid'] = self::swap16($field['time_mid']);
198                $field['time_hi'] = self::swap16($field['time_hi']);
199
200                /* Apply version and constants */
201                $field['clock_seq_hi'] &= 0x3f;
202                $field['clock_seq_hi'] |= (1 << 7);
203                $field['time_hi'] &= 0x0fff;
204                $field['time_hi'] |= ($version << 12);
205
206                return ($field);       
207        }
208        static private function generateNameMD5($ns, $node) {
209                return self::generateName($ns, $node, "md5",
210                        self::UUID_NAME_MD5);
211        }
212        static private function generateNameSHA1($ns, $node) {
213                return self::generateName($ns, $node, "sha1",
214                        self::UUID_NAME_SHA1);
215        }
216
217        /*
218         * Generate UUID version 1 (time based)
219         */
220        static private function generateTime($ns, $node) {
221                $uuid = self::$m_uuid_field;
222
223                /*
224                 * Get current time in 100 ns intervals. The magic value
225                 * is the offset between UNIX epoch and the UUID UTC
226                 * time base October 15, 1582.
227                 */
228                $tp = gettimeofday();
229                $time = ($tp['sec'] * 10000000) + ($tp['usec'] * 10) +
230                        0x01B21DD213814000;
231
232                $uuid['time_low'] = $time & 0xffffffff;
233                /* Work around PHP 32-bit bit-operation limits */
234                $high = intval($time / 0xffffffff);
235                $uuid['time_mid'] = $high & 0xffff;
236                $uuid['time_hi'] = (($high >> 16) & 0xfff) | (self::UUID_TIME << 12);
237               
238                /*
239                 * We don't support saved state information and generate
240                 * a random clock sequence each time.
241                 */
242                $uuid['clock_seq_hi'] = 0x80 | mt_rand(0, 64);
243                $uuid['clock_seq_low'] = mt_rand(0, 255);
244
245                /*
246                 * Node should be set to the 48-bit IEEE node identifier, but
247                 * we leave it for the user to supply the node.
248                 */
249                for ($i = 0; $i < 6; ++$i)
250                        $uuid['node'][$i] = ord(substr($node, $i, 1));
251
252                return ($uuid);
253        }
254
255        /* Assumes correct byte order */
256        static private function conv_field2byte($src) {
257                $uuid[0] = ($src['time_low'] & 0xff000000) >> 24;
258                $uuid[1] = ($src['time_low'] & 0x00ff0000) >> 16;
259                $uuid[2] = ($src['time_low'] & 0x0000ff00) >> 8;
260                $uuid[3] = ($src['time_low'] & 0x000000ff);
261                $uuid[4] = ($src['time_mid'] & 0xff00) >> 8;
262                $uuid[5] = ($src['time_mid'] & 0x00ff);
263                $uuid[6] = ($src['time_hi'] & 0xff00) >> 8;
264                $uuid[7] = ($src['time_hi'] & 0x00ff);
265                $uuid[8] = $src['clock_seq_hi'];
266                $uuid[9] = $src['clock_seq_low'];
267
268                for ($i = 0; $i < 6; ++$i)
269                        $uuid[10+$i] = $src['node'][$i];
270
271                return ($uuid);
272        }
273
274        static private function conv_field2string($src) {
275                $str = sprintf(
276                        '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x',
277                        ($src['time_low']), ($src['time_mid']), ($src['time_hi']),
278                        $src['clock_seq_hi'], $src['clock_seq_low'],
279                        $src['node'][0], $src['node'][1], $src['node'][2],
280                        $src['node'][3], $src['node'][4], $src['node'][5]);
281                return ($str);
282        }
283
284        static private function conv_field2binary($src) {
285                $byte = self::conv_field2byte($src);
286                return self::conv_byte2binary($byte);
287        }
288
289        static private function conv_byte2field($uuid) {
290                $field = self::$m_uuid_field;
291                $field['time_low'] = ($uuid[0] << 24) | ($uuid[1] << 16) |
292                        ($uuid[2] << 8) | $uuid[3];
293                $field['time_mid'] = ($uuid[4] << 8) | $uuid[5];
294                $field['time_hi'] = ($uuid[6] << 8) | $uuid[7];
295                $field['clock_seq_hi'] = $uuid[8];
296                $field['clock_seq_low'] = $uuid[9];
297
298                for ($i = 0; $i < 6; ++$i)
299                        $field['node'][$i] = $uuid[10+$i];
300                return ($field);
301        }
302
303        static public function conv_byte2string($src) {
304                $field = self::conv_byte2field($src);
305                return self::conv_field2string($field);
306        }
307
308        static private function conv_byte2binary($src) {
309                $raw = pack('C16', $src[0], $src[1], $src[2], $src[3],
310                        $src[4], $src[5], $src[6], $src[7], $src[8], $src[9],
311                        $src[10], $src[11], $src[12], $src[13], $src[14], $src[15]);
312                return ($raw);
313        }
314
315        static private function conv_string2field($src) {
316                $parts = sscanf($src, '%x-%x-%x-%x-%02x%02x%02x%02x%02x%02x');
317                $field = self::$m_uuid_field;
318                $field['time_low'] = ($parts[0]);
319                $field['time_mid'] = ($parts[1]);
320                $field['time_hi'] = ($parts[2]);
321                $field['clock_seq_hi'] = ($parts[3] & 0xff00) >> 8;
322                $field['clock_seq_low'] = $parts[3] & 0x00ff;
323                for ($i = 0; $i < 6; ++$i)
324                        $field['node'][$i] = $parts[4+$i];
325
326                return ($field);
327        }
328
329        static private function conv_string2byte($src) {
330                $field = self::conv_string2field($src);
331                return self::conv_field2byte($field);
332        }
333
334        static private function conv_string2binary($src) {
335                $byte = self::conv_string2byte($src);
336                return self::conv_byte2binary($byte);
337        }
338}
339
340?>
Note: See TracBrowser for help on using the repository browser.