Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / data / UuidGenerator.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @extend Ext.data.IdGenerator
17  * @author Don Griffin
18  *
19  * This class generates UUID's according to RFC 4122. This class has a default id property.
20  * This means that a single instance is shared unless the id property is overridden. Thus,
21  * two {@link Ext.data.Model} instances configured like the following share one generator:
22  *
23  *     Ext.define('MyApp.data.MyModelX', {
24  *         extend: 'Ext.data.Model',
25  *         idgen: 'uuid'
26  *     });
27  *
28  *     Ext.define('MyApp.data.MyModelY', {
29  *         extend: 'Ext.data.Model',
30  *         idgen: 'uuid'
31  *     });
32  *
33  * This allows all models using this class to share a commonly configured instance.
34  *
35  * # Using Version 1 ("Sequential") UUID's
36  *
37  * If a server can provide a proper timestamp and a "cryptographic quality random number"
38  * (as described in RFC 4122), the shared instance can be configured as follows:
39  *
40  *     Ext.data.IdGenerator.get('uuid').reconfigure({
41  *         version: 1,
42  *         clockSeq: clock, // 14 random bits
43  *         salt: salt,      // 48 secure random bits (the Node field)
44  *         timestamp: ts    // timestamp per Section 4.1.4
45  *     });
46  *
47  *     // or these values can be split into 32-bit chunks:
48  *
49  *     Ext.data.IdGenerator.get('uuid').reconfigure({
50  *         version: 1,
51  *         clockSeq: clock,
52  *         salt: { lo: saltLow32, hi: saltHigh32 },
53  *         timestamp: { lo: timestampLow32, hi: timestamptHigh32 }
54  *     });
55  *
56  * This approach improves the generator's uniqueness by providing a valid timestamp and
57  * higher quality random data. Version 1 UUID's should not be used unless this information
58  * can be provided by a server and care should be taken to avoid caching of this data.
59  *
60  * See http://www.ietf.org/rfc/rfc4122.txt for details.
61  */
62 Ext.define('Ext.data.UuidGenerator', function () {
63     var twoPow14 = Math.pow(2, 14),
64         twoPow16 = Math.pow(2, 16),
65         twoPow28 = Math.pow(2, 28),
66         twoPow32 = Math.pow(2, 32);
67
68     function toHex (value, length) {
69         var ret = value.toString(16);
70         if (ret.length > length) {
71             ret = ret.substring(ret.length - length); // right-most digits
72         } else if (ret.length < length) {
73             ret = Ext.String.leftPad(ret, length, '0');
74         }
75         return ret;
76     }
77
78     function rand (lo, hi) {
79         var v = Math.random() * (hi - lo + 1);
80         return Math.floor(v) + lo;
81     }
82
83     function split (bignum) {
84         if (typeof(bignum) == 'number') {
85             var hi = Math.floor(bignum / twoPow32);
86             return {
87                 lo: Math.floor(bignum - hi * twoPow32),
88                 hi: hi
89             };
90         }
91         return bignum;
92     }
93
94     return {
95         extend: 'Ext.data.IdGenerator',
96
97         alias: 'idgen.uuid',
98
99         id: 'uuid', // shared by default
100
101         /**
102          * @property {Number/Object} salt
103          * When created, this value is a 48-bit number. For computation, this value is split
104          * into 32-bit parts and stored in an object with `hi` and `lo` properties.
105          */
106
107         /**
108          * @property {Number/Object} timestamp
109          * When created, this value is a 60-bit number. For computation, this value is split
110          * into 32-bit parts and stored in an object with `hi` and `lo` properties.
111          */
112
113         /**
114          * @cfg {Number} version
115          * The Version of UUID. Supported values are:
116          *
117          *  * 1 : Time-based, "sequential" UUID.
118          *  * 4 : Pseudo-random UUID.
119          *
120          * The default is 4.
121          */
122         version: 4,
123
124         constructor: function() {
125             var me = this;
126
127             me.callParent(arguments);
128
129             me.parts = [];
130             me.init();
131         },
132
133         generate: function () {
134             var me = this,
135                 parts = me.parts,
136                 ts = me.timestamp;
137
138             /*
139                The magic decoder ring (derived from RFC 4122 Section 4.2.2):
140
141                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142                |                          time_low                             |
143                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
144                |           time_mid            |  ver  |        time_hi        |
145                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146                |res|  clock_hi |   clock_low   |    salt 0   |M|     salt 1    |
147                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148                |                         salt (2-5)                            |
149                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
150
151                          time_mid      clock_hi (low 6 bits)
152                 time_low     | time_hi |clock_lo
153                     |        |     |   || salt[0]
154                     |        |     |   ||   | salt[1..5]
155                     v        v     v   vv   v v
156                     0badf00d-aced-1def-b123-dfad0badbeef
157                                   ^    ^     ^
158                             version    |     multicast (low bit)
159                                        |
160                                     reserved (upper 2 bits)
161             */
162             parts[0] = toHex(ts.lo, 8);
163             parts[1] = toHex(ts.hi & 0xFFFF, 4);
164             parts[2] = toHex(((ts.hi >>> 16) & 0xFFF) | (me.version << 12), 4);
165             parts[3] = toHex(0x80 | ((me.clockSeq >>> 8) & 0x3F), 2) +
166                        toHex(me.clockSeq & 0xFF, 2);
167             parts[4] = toHex(me.salt.hi, 4) + toHex(me.salt.lo, 8);
168
169             if (me.version == 4) {
170                 me.init(); // just regenerate all the random values...
171             } else {
172                 // sequentially increment the timestamp...
173                 ++ts.lo;
174                 if (ts.lo >= twoPow32) { // if (overflow)
175                     ts.lo = 0;
176                     ++ts.hi;
177                 }
178             }
179
180             return parts.join('-').toLowerCase();
181         },
182
183         getRecId: function (rec) {
184             return rec.getId();
185         },
186
187         /**
188          * @private
189          */
190         init: function () {
191             var me = this,
192                 salt, time;
193
194             if (me.version == 4) {
195                 // See RFC 4122 (Secion 4.4)
196                 //   o  If the state was unavailable (e.g., non-existent or corrupted),
197                 //      or the saved node ID is different than the current node ID,
198                 //      generate a random clock sequence value.
199                 me.clockSeq = rand(0, twoPow14-1);
200
201                 // we run this on every id generation...
202                 salt = me.salt || (me.salt = {});
203                 time = me.timestamp || (me.timestamp = {});
204
205                 // See RFC 4122 (Secion 4.4)
206                 salt.lo = rand(0, twoPow32-1);
207                 salt.hi = rand(0, twoPow16-1);
208                 time.lo = rand(0, twoPow32-1);
209                 time.hi = rand(0, twoPow28-1);
210             } else {
211                 // this is run only once per-instance
212                 me.salt = split(me.salt);
213                 me.timestamp = split(me.timestamp);
214
215                 // Set multicast bit: "the least significant bit of the first octet of the
216                 // node ID" (nodeId = salt for this implementation):
217                 me.salt.hi |= 0x100;
218             }
219         },
220
221         /**
222          * Reconfigures this generator given new config properties.
223          */
224         reconfigure: function (config) {
225             Ext.apply(this, config);
226             this.init();
227         }
228     };
229 }());
230