oj 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of oj might be problematic. Click here for more details.

data/README.md CHANGED
@@ -16,19 +16,19 @@ A fast JSON parser and Object marshaller as a Ruby gem.
16
16
 
17
17
  ## <a name="links">Links of Interest</a>
18
18
 
19
- ## <a name="release">Release Notes</a>
19
+ *Fast XML parser and marshaller on RubyGems*: https://rubygems.org/gems/ox
20
20
 
21
- ### Release 0.6.0
21
+ *Fast XML parser and marshaller on GitHub*: https://rubygems.org/gems/ox
22
22
 
23
- - supports arbitrary Object dumping/serialization
23
+ ## <a name="release">Release Notes</a>
24
24
 
25
- - to_hash() method called if the Object responds to to_hash and the result is converted to JSON
25
+ ### Release 0.7.0
26
26
 
27
- - to_json() method called if the Object responds to to_json
27
+ - changed the object JSON format
28
28
 
29
- - almost any Object can be dumped, including Exceptions (not including Thread, Mutex and Objects that only make sense within a process)
29
+ - serialized Ruby Objects can now be deserialized
30
30
 
31
- - default options have been added
31
+ - improved performance testing
32
32
 
33
33
  ## <a name="description">Description</a>
34
34
 
@@ -39,19 +39,23 @@ time faster than Yajl for parsing and 3 or more times faster writing JSON.
39
39
 
40
40
  Oj has several dump or serialization modes which control how Objects are
41
41
  converted to JSON. These modes are set with the :effort option in either the
42
- dafault options or as one of the options to the dump() method. The :strict
43
- mode will only allow the 7 basic JSON types to be serialized. Any other Object
44
- will raise and Exception. In the :lazy mode any Object that is not one of the
45
- JSON types is replaced by a JSON null. In :interal mode any Object will be
46
- dumped as a JSON Object with keys that match the Ruby Object's variable names
47
- without the '@' character. This is the highest performance mode. The :internal
48
- mode is not found in other JSON gems. The last mode, the :tolerant mode is the
49
- most tolerant. It will serialize any Object but will check to see if the
50
- Object implements a to_hash() or to_json() method. If either exists that
51
- method is used for serializing the Object. The to_hash() is more flexible and
52
- produces more consistent output so it has a preference over the to_json()
53
- method. If neither the to_json() or to_hash() methods exist then the Oj
54
- internal Object variable encoding is used.
42
+ default options or as one of the options to the dump() method.
43
+
44
+ - :strict mode will only allow the 7 basic JSON types to be serialized. Any other Object
45
+ will raise and Exception.
46
+
47
+ - :null mode replaces any Object that is not one of the JSON types is replaced by a JSON null.
48
+
49
+ - :object mode will dump any Object as a JSON Object with keys that match the
50
+ Ruby Object's variable names without the '@' character. This is the highest
51
+ performance mode.
52
+
53
+ - :compat mode is is the compatible with other systems. It will serialize any
54
+ Object but will check to see if the Object implements a to_hash() or to_json()
55
+ method. If either exists that method is used for serializing the Object. The
56
+ to_hash() is more flexible and produces more consistent output so it has a
57
+ preference over the to_json() method. If neither the to_json() or to_hash()
58
+ methods exist then the Oj internal Object variable encoding is used.
55
59
 
56
60
  Coming soon: As an Object marshaller with support for circular references.
57
61
 
@@ -63,17 +67,16 @@ Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, JRuby, and RBX.
63
67
 
64
68
  The following table shows the difference is speeds between several
65
69
  serialization packages. The tests had to be scaled back due to limitation of
66
- some of the gems. I finally gave up trying to get JSON to serialize without
67
- errors with Ruby 1.9.3. It had internal errors on anything other than a simple
68
- JSON structure. The errors encountered were:
70
+ some of the gems. I finally gave up trying to get JSON Pure to serialize
71
+ without errors with Ruby 1.9.3. It had internal errors on anything other than
72
+ a simple JSON structure. The errors encountered were:
69
73
 
70
74
  - MessagePack fails to convert Bignum to JSON
71
75
 
72
- - JSON Pure and Ext fails to serialize any numbers or Objects with the to_json() method
76
+ - JSON Pure fails to serialize any numbers or Objects with the to_json() method
73
77
 
74
- Options were added to the test/perf_simple.rb test to run the test without
75
- Object encoding and without Bignums. There is also an option for shallow JSON
76
- so that JSON Pure and Ext can be compared.
78
+ Options were added to the test/perf_strict.rb test to run the test without
79
+ Object encoding and without Bignums.
77
80
 
78
81
  None of the packages except Oj were able to serialize Ruby Objects that did
79
82
  not have a to_json() method or were of the 7 native JSON types.
@@ -86,68 +89,125 @@ The results:
86
89
 
87
90
  with Object and Bignum encoding:
88
91
 
89
- 100000 Oj.load()s in 1.456 seconds or 68.7 loads/msec
90
- 100000 Yajl::Parser.parse()s in 2.681 seconds or 37.3 parses/msec
91
- 100000 JSON::Ext::Parser parse()s in 2.804 seconds or 35.7 parses/msec
92
- 100000 JSON::Pure::Parser parse()s in 27.494 seconds or 3.6 parses/msec
93
- MessagePack failed: RangeError: bignum too big to convert into `unsigned long long'
94
- 100000 Ox.load()s in 3.165 seconds or 31.6 loads/msec
95
- Parser results:
96
- gem seconds parses/msec X faster than JSON::Pure (higher is better)
97
- oj 1.456 68.7 18.9
98
- yajl 2.681 37.3 10.3
99
- msgpack failed to generate JSON
100
- pure 27.494 3.6 1.0
101
- ext 2.804 35.7 9.8
102
- ox 3.165 31.6 8.7
103
-
104
- 100000 Oj.dump()s in 0.484 seconds or 206.7 dumps/msec
105
- 100000 Yajl::Encoder.encode()s in 2.167 seconds or 46.2 encodes/msec
106
- JSON::Ext failed: TypeError: wrong argument type JSON::Pure::Generator::State (expected Data)
107
- JSON::Pure failed: TypeError: wrong argument type JSON::Pure::Generator::State (expected Data)
108
- MessagePack failed: RangeError: bignum too big to convert into `unsigned long long'
109
- 100000 Ox.dump()s in 0.554 seconds or 180.4 dumps/msec
110
- Parser results:
111
- gem seconds dumps/msec X faster than Yajl (higher is better)
112
- oj 0.484 206.7 4.5
113
- yajl 2.167 46.2 1.0
114
- msgpack failed to generate JSON
115
- pure failed to generate JSON
116
- ext failed to generate JSON
117
- ox 0.554 180.4 3.9
92
+ MessagePack failed to pack! RangeError: bignum too big to convert into `unsigned long long'.
93
+ Skipping.
94
+ --------------------------------------------------------------------------------
95
+ Load/Parse Performance
96
+ Oj.load 100000 times in 1.384 seconds or 72230.276 load/sec.
97
+ Yajl.parse 100000 times in 2.475 seconds or 40401.331 parse/sec.
98
+ JSON::Ext.parse 100000 times in 2.562 seconds or 39037.263 parse/sec.
99
+ JSON::Pure.parse 100000 times in 20.914 seconds or 4781.518 parse/sec.
100
+ Ox.load 100000 times in 1.517 seconds or 65923.576 load/sec.
101
+
102
+ Summary:
103
+ System time (secs) rate (ops/sec)
104
+ ---------- ----------- --------------
105
+ Oj 1.384 72230.276
106
+ Ox 1.517 65923.576
107
+ Yajl 2.475 40401.331
108
+ JSON::Ext 2.562 39037.263
109
+ JSON::Pure 20.914 4781.518
110
+
111
+ Comparison Matrix
112
+ (performance factor, 2.0 row is means twice as fast as column)
113
+ Oj Ox Yajl JSON::Ext JSON::Pure
114
+ ---------- ---------- ---------- ---------- ---------- ----------
115
+ Oj 1.00 1.10 1.79 1.85 15.11
116
+ Ox 0.91 1.00 1.63 1.69 13.79
117
+ Yajl 0.56 0.61 1.00 1.03 8.45
118
+ JSON::Ext 0.54 0.59 0.97 1.00 8.16
119
+ JSON::Pure 0.07 0.07 0.12 0.12 1.00
120
+
121
+
122
+ --------------------------------------------------------------------------------
123
+ Dump/Encode/Generate Performance
124
+ Oj.dump 100000 times in 0.819 seconds or 122096.842 dump/sec.
125
+ Yajl.encode 100000 times in 2.221 seconds or 45014.913 encode/sec.
126
+ JSON::Ext.generate 100000 times in 5.082 seconds or 19678.462 generate/sec.
127
+ ***** JSON::Pure.generate failed! TypeError: wrong argument type JSON::Pure::Generator::State (expected Data)
128
+ Ox.dump 100000 times in 0.532 seconds or 188014.455 dump/sec.
129
+
130
+ Summary:
131
+ System time (secs) rate (ops/sec)
132
+ --------- ----------- --------------
133
+ Ox 0.532 188014.455
134
+ Oj 0.819 122096.842
135
+ Yajl 2.221 45014.913
136
+ JSON::Ext 5.082 19678.462
137
+
138
+ Comparison Matrix
139
+ (performance factor, 2.0 row is means twice as fast as column)
140
+ Ox Oj Yajl JSON::Ext
141
+ --------- --------- --------- --------- ---------
142
+ Ox 1.00 1.54 4.18 9.55
143
+ Oj 0.65 1.00 2.71 6.20
144
+ Yajl 0.24 0.37 1.00 2.29
145
+ JSON::Ext 0.10 0.16 0.44 1.00
118
146
 
119
147
  without Objects or numbers (for JSON Pure) JSON:
120
148
 
121
- 100000 Oj.load()s in 0.739 seconds or 135.3 loads/msec
122
- 100000 Yajl::Parser.parse()s in 1.421 seconds or 70.4 parses/msec
123
- 100000 JSON::Ext::Parser parse()s in 1.512 seconds or 66.2 parses/msec
124
- 100000 JSON::Pure::Parser parse()s in 16.953 seconds or 5.9 parses/msec
125
- 100000 MessagePack.unpack()s in 0.635 seconds or 157.6 packs/msec
126
- 100000 Ox.load()s in 0.971 seconds or 103.0 loads/msec
127
- Parser results:
128
- gem seconds parses/msec X faster than JSON::Pure (higher is better)
129
- oj 0.739 135.3 22.9
130
- yajl 1.421 70.4 11.9
131
- msgpack 0.635 157.6 26.7
132
- pure 16.953 5.9 1.0
133
- ext 1.512 66.2 11.2
134
- ox 0.971 103.0 17.5
135
-
136
- 100000 Oj.dump()s in 0.174 seconds or 575.1 dumps/msec
137
- 100000 Yajl::Encoder.encode()s in 0.729 seconds or 137.2 encodes/msec
138
- 100000 JSON::Ext generate()s in 7.171 seconds or 13.9 generates/msec
139
- 100000 JSON::Pure generate()s in 7.219 seconds or 13.9 generates/msec
140
- 100000 Msgpack()s in 0.299 seconds or 334.8 unpacks/msec
141
- 100000 Ox.dump()s in 0.210 seconds or 475.8 dumps/msec
142
- Parser results:
143
- gem seconds dumps/msec X faster than JSON::Pure (higher is better)
144
- oj 0.174 575.1 41.5
145
- yajl 0.729 137.2 9.9
146
- msgpack 0.299 334.8 24.2
147
- pure 7.219 13.9 1.0
148
- ext 1.512 66.2 4.8
149
- ox 0.210 475.8 34.3
150
-
149
+ --------------------------------------------------------------------------------
150
+ Load/Parse Performance
151
+ Oj.load 100000 times in 0.737 seconds or 135683.185 load/sec.
152
+ Yajl.parse 100000 times in 1.352 seconds or 73978.778 parse/sec.
153
+ JSON::Ext.parse 100000 times in 1.433 seconds or 69780.554 parse/sec.
154
+ JSON::Pure.parse 100000 times in 12.974 seconds or 7707.624 parse/sec.
155
+ Ox.load 100000 times in 0.904 seconds or 110596.591 load/sec.
156
+ MessagePack.unpack 100000 times in 0.644 seconds or 155281.191 unpack/sec.
157
+
158
+ Summary:
159
+ System time (secs) rate (ops/sec)
160
+ ----------- ----------- --------------
161
+ MessagePack 0.644 155281.191
162
+ Oj 0.737 135683.185
163
+ Ox 0.904 110596.591
164
+ Yajl 1.352 73978.778
165
+ JSON::Ext 1.433 69780.554
166
+ JSON::Pure 12.974 7707.624
167
+
168
+ Comparison Matrix
169
+ (performance factor, 2.0 row is means twice as fast as column)
170
+ MessagePack Oj Ox Yajl JSON::Ext JSON::Pure
171
+ ----------- ----------- ----------- ----------- ----------- ----------- -----------
172
+ MessagePack 1.00 1.14 1.40 2.10 2.23 20.15
173
+ Oj 0.87 1.00 1.23 1.83 1.94 17.60
174
+ Ox 0.71 0.82 1.00 1.49 1.58 14.35
175
+ Yajl 0.48 0.55 0.67 1.00 1.06 9.60
176
+ JSON::Ext 0.45 0.51 0.63 0.94 1.00 9.05
177
+ JSON::Pure 0.05 0.06 0.07 0.10 0.11 1.00
178
+
179
+
180
+ --------------------------------------------------------------------------------
181
+ Dump/Encode/Generate Performance
182
+ Oj.dump 100000 times in 0.161 seconds or 620058.906 dump/sec.
183
+ Yajl.encode 100000 times in 0.765 seconds or 130637.498 encode/sec.
184
+ JSON::Ext.generate 100000 times in 3.306 seconds or 30250.212 generate/sec.
185
+ JSON::Pure.generate 100000 times in 7.067 seconds or 14150.026 generate/sec.
186
+ Ox.dump 100000 times in 0.178 seconds or 561312.123 dump/sec.
187
+ MessagePack.pack 100000 times in 0.306 seconds or 326301.535 pack/sec.
188
+
189
+ Summary:
190
+ System time (secs) rate (ops/sec)
191
+ ----------- ----------- --------------
192
+ Oj 0.161 620058.906
193
+ Ox 0.178 561312.123
194
+ MessagePack 0.306 326301.535
195
+ Yajl 0.765 130637.498
196
+ JSON::Ext 3.306 30250.212
197
+ JSON::Pure 7.067 14150.026
198
+
199
+ Comparison Matrix
200
+ (performance factor, 2.0 row is means twice as fast as column)
201
+ Oj Ox MessagePack Yajl JSON::Ext JSON::Pure
202
+ ----------- ----------- ----------- ----------- ----------- ----------- -----------
203
+ Oj 1.00 1.10 1.90 4.75 20.50 43.82
204
+ Ox 0.91 1.00 1.72 4.30 18.56 39.67
205
+ MessagePack 0.53 0.58 1.00 2.50 10.79 23.06
206
+ Yajl 0.21 0.23 0.40 1.00 4.32 9.23
207
+ JSON::Ext 0.05 0.05 0.09 0.23 1.00 2.14
208
+ JSON::Pure 0.02 0.03 0.04 0.11 0.47 1.00
209
+
210
+
151
211
  ### Simple JSON Writing and Parsing:
152
212
 
153
213
  require 'oj'
@@ -0,0 +1,148 @@
1
+ /* cache.c
2
+ * Copyright (c) 2011, Peter Ohler
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * - Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * - Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * - Neither the name of Peter Ohler nor the names of its contributors may be
16
+ * used to endorse or promote products derived from this software without
17
+ * specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #include <stdlib.h>
32
+ #include <errno.h>
33
+ #include <stdio.h>
34
+ #include <string.h>
35
+ #include <stdarg.h>
36
+
37
+ #include "cache.h"
38
+
39
+ struct _Cache {
40
+ char *key; // only set if the node has a value, and it is not an exact match
41
+ VALUE value;
42
+ struct _Cache *slots[16];
43
+ };
44
+
45
+ static void slot_print(Cache cache, unsigned int depth);
46
+
47
+ void
48
+ oj_cache_new(Cache *cache) {
49
+ if (0 == (*cache = (Cache)malloc(sizeof(struct _Cache)))) {
50
+ rb_raise(rb_eNoMemError, "not enough memory\n");
51
+ }
52
+ (*cache)->key = 0;
53
+ (*cache)->value = Qundef;
54
+ bzero((*cache)->slots, sizeof((*cache)->slots));
55
+ }
56
+
57
+ VALUE
58
+ oj_cache_get(Cache cache, const char *key, VALUE **slot) {
59
+ unsigned char *k = (unsigned char*)key;
60
+ Cache *cp;
61
+
62
+ for (; '\0' != *k; k++) {
63
+ cp = cache->slots + (unsigned int)(*k >> 4); // upper 4 bits
64
+ if (0 == *cp) {
65
+ oj_cache_new(cp);
66
+ }
67
+ cache = *cp;
68
+ cp = cache->slots + (unsigned int)(*k & 0x0F); // lower 4 bits
69
+ if (0 == *cp) {
70
+ oj_cache_new(cp);
71
+ cache = *cp;
72
+ cache->key = ('\0' == *(k + 1)) ? 0 : strdup(key);
73
+ break;
74
+ } else {
75
+ cache = *cp;
76
+ if (Qundef != cache->value && 0 != cache->key) {
77
+ unsigned char *ck = (unsigned char*)(cache->key + (unsigned int)(k - (unsigned char*)key + 1));
78
+
79
+ if (0 == strcmp((char*)ck, (char*)(k + 1))) {
80
+ break;
81
+ } else {
82
+ Cache *cp2 = cp;
83
+
84
+ // if value was set along with the key then there are no slots filled yet
85
+ cp2 = (*cp2)->slots + (*ck >> 4);
86
+ oj_cache_new(cp2);
87
+ cp2 = (*cp2)->slots + (*ck & 0x0F);
88
+ oj_cache_new(cp2);
89
+ if ('\0' == *(ck + 1)) {
90
+ free(cache->key);
91
+ } else {
92
+ (*cp2)->key = cache->key;
93
+ }
94
+ (*cp2)->value = cache->value;
95
+ cache->key = 0;
96
+ cache->value = Qundef;
97
+ }
98
+ }
99
+ }
100
+ }
101
+ *slot = &cache->value;
102
+
103
+ return cache->value;
104
+ }
105
+
106
+ void
107
+ oj_cache_print(Cache cache) {
108
+ //printf("-------------------------------------------\n");
109
+ slot_print(cache, 0);
110
+ }
111
+
112
+ static void
113
+ slot_print(Cache c, unsigned int depth) {
114
+ char indent[256];
115
+ Cache *cp;
116
+ unsigned int i;
117
+
118
+ if (sizeof(indent) - 1 < depth) {
119
+ depth = ((int)sizeof(indent) - 1);
120
+ }
121
+ memset(indent, ' ', depth);
122
+ indent[depth] = '\0';
123
+ for (i = 0, cp = c->slots; i < 16; i++, cp++) {
124
+ if (0 == *cp) {
125
+ //printf("%s%02u:\n", indent, i);
126
+ } else {
127
+ if (0 == (*cp)->key && Qundef == (*cp)->value) {
128
+ printf("%s%02u:\n", indent, i);
129
+ } else {
130
+ const char *key = (0 == (*cp)->key) ? "*" : (*cp)->key;
131
+ const char *vs;
132
+ const char *clas;
133
+
134
+ if (Qundef == (*cp)->value) {
135
+ vs = "undefined";
136
+ clas = "";
137
+ } else {
138
+ VALUE rs = rb_funcall2((*cp)->value, rb_intern("to_s"), 0, 0);
139
+
140
+ vs = StringValuePtr(rs);
141
+ clas = rb_class2name(rb_obj_class((*cp)->value));
142
+ }
143
+ printf("%s%02u: %s = %s (%s)\n", indent, i, key, vs, clas);
144
+ }
145
+ slot_print(*cp, depth + 2);
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,44 @@
1
+ /* cache.h
2
+ * Copyright (c) 2011, Peter Ohler
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * - Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * - Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * - Neither the name of Peter Ohler nor the names of its contributors may be
16
+ * used to endorse or promote products derived from this software without
17
+ * specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #ifndef __OJ_CACHE_H__
32
+ #define __OJ_CACHE_H__
33
+
34
+ #include "ruby.h"
35
+
36
+ typedef struct _Cache *Cache;
37
+
38
+ extern void oj_cache_new(Cache *cache);
39
+
40
+ extern VALUE oj_cache_get(Cache cache, const char *key, VALUE **slot);
41
+
42
+ extern void oj_cache_print(Cache cache);
43
+
44
+ #endif /* __OJ_CACHE_H__ */