json 1.8.6 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +5 -5
  2. data/{CHANGES → CHANGES.md} +292 -96
  3. data/LICENSE +56 -0
  4. data/README.md +185 -114
  5. data/ext/json/ext/fbuffer/fbuffer.h +0 -3
  6. data/ext/json/ext/generator/generator.c +328 -117
  7. data/ext/json/ext/generator/generator.h +8 -8
  8. data/ext/json/ext/parser/extconf.rb +29 -0
  9. data/ext/json/ext/parser/parser.c +540 -569
  10. data/ext/json/ext/parser/parser.h +10 -6
  11. data/ext/json/ext/parser/parser.rl +269 -261
  12. data/ext/json/extconf.rb +1 -1
  13. data/json.gemspec +0 -0
  14. data/lib/json/add/bigdecimal.rb +40 -10
  15. data/lib/json/add/complex.rb +32 -9
  16. data/lib/json/add/core.rb +1 -0
  17. data/lib/json/add/date.rb +27 -7
  18. data/lib/json/add/date_time.rb +26 -9
  19. data/lib/json/add/exception.rb +25 -7
  20. data/lib/json/add/ostruct.rb +32 -9
  21. data/lib/json/add/range.rb +33 -8
  22. data/lib/json/add/rational.rb +30 -8
  23. data/lib/json/add/regexp.rb +28 -10
  24. data/lib/json/add/set.rb +48 -0
  25. data/lib/json/add/struct.rb +29 -7
  26. data/lib/json/add/symbol.rb +28 -5
  27. data/lib/json/add/time.rb +27 -6
  28. data/lib/json/common.rb +402 -188
  29. data/lib/json/ext.rb +0 -6
  30. data/lib/json/generic_object.rb +11 -6
  31. data/lib/json/pure/generator.rb +120 -137
  32. data/lib/json/pure/parser.rb +64 -86
  33. data/lib/json/pure.rb +2 -8
  34. data/lib/json/version.rb +2 -1
  35. data/lib/json.rb +559 -29
  36. metadata +18 -129
  37. data/.gitignore +0 -17
  38. data/.travis.yml +0 -18
  39. data/Gemfile +0 -7
  40. data/README-json-jruby.markdown +0 -33
  41. data/Rakefile +0 -402
  42. data/TODO +0 -1
  43. data/VERSION +0 -1
  44. data/data/example.json +0 -1
  45. data/data/index.html +0 -38
  46. data/data/prototype.js +0 -4184
  47. data/diagrams/.keep +0 -0
  48. data/install.rb +0 -23
  49. data/java/src/json/ext/ByteListTranscoder.java +0 -166
  50. data/java/src/json/ext/Generator.java +0 -446
  51. data/java/src/json/ext/GeneratorMethods.java +0 -231
  52. data/java/src/json/ext/GeneratorService.java +0 -42
  53. data/java/src/json/ext/GeneratorState.java +0 -542
  54. data/java/src/json/ext/OptionsReader.java +0 -113
  55. data/java/src/json/ext/Parser.java +0 -2644
  56. data/java/src/json/ext/Parser.rl +0 -968
  57. data/java/src/json/ext/ParserService.java +0 -34
  58. data/java/src/json/ext/RuntimeInfo.java +0 -120
  59. data/java/src/json/ext/StringDecoder.java +0 -166
  60. data/java/src/json/ext/StringEncoder.java +0 -111
  61. data/java/src/json/ext/Utils.java +0 -88
  62. data/json-java.gemspec +0 -38
  63. data/json_pure.gemspec +0 -37
  64. data/lib/json/ext/.keep +0 -0
  65. data/tests/fixtures/fail1.json +0 -1
  66. data/tests/fixtures/fail10.json +0 -1
  67. data/tests/fixtures/fail11.json +0 -1
  68. data/tests/fixtures/fail12.json +0 -1
  69. data/tests/fixtures/fail13.json +0 -1
  70. data/tests/fixtures/fail14.json +0 -1
  71. data/tests/fixtures/fail18.json +0 -1
  72. data/tests/fixtures/fail19.json +0 -1
  73. data/tests/fixtures/fail2.json +0 -1
  74. data/tests/fixtures/fail20.json +0 -1
  75. data/tests/fixtures/fail21.json +0 -1
  76. data/tests/fixtures/fail22.json +0 -1
  77. data/tests/fixtures/fail23.json +0 -1
  78. data/tests/fixtures/fail24.json +0 -1
  79. data/tests/fixtures/fail25.json +0 -1
  80. data/tests/fixtures/fail27.json +0 -2
  81. data/tests/fixtures/fail28.json +0 -2
  82. data/tests/fixtures/fail3.json +0 -1
  83. data/tests/fixtures/fail4.json +0 -1
  84. data/tests/fixtures/fail5.json +0 -1
  85. data/tests/fixtures/fail6.json +0 -1
  86. data/tests/fixtures/fail7.json +0 -1
  87. data/tests/fixtures/fail8.json +0 -1
  88. data/tests/fixtures/fail9.json +0 -1
  89. data/tests/fixtures/pass1.json +0 -56
  90. data/tests/fixtures/pass15.json +0 -1
  91. data/tests/fixtures/pass16.json +0 -1
  92. data/tests/fixtures/pass17.json +0 -1
  93. data/tests/fixtures/pass2.json +0 -1
  94. data/tests/fixtures/pass26.json +0 -1
  95. data/tests/fixtures/pass3.json +0 -6
  96. data/tests/setup_variant.rb +0 -11
  97. data/tests/test_json.rb +0 -519
  98. data/tests/test_json_addition.rb +0 -196
  99. data/tests/test_json_encoding.rb +0 -65
  100. data/tests/test_json_fixtures.rb +0 -35
  101. data/tests/test_json_generate.rb +0 -348
  102. data/tests/test_json_generic_object.rb +0 -75
  103. data/tests/test_json_string_matching.rb +0 -39
  104. data/tests/test_json_unicode.rb +0 -72
  105. data/tools/diff.sh +0 -18
  106. data/tools/fuzz.rb +0 -139
  107. data/tools/server.rb +0 -62
data/lib/json/ext.rb CHANGED
@@ -1,9 +1,3 @@
1
- if ENV['SIMPLECOV_COVERAGE'].to_i == 1
2
- require 'simplecov'
3
- SimpleCov.start do
4
- add_filter "/tests/"
5
- end
6
- end
7
1
  require 'json/common'
8
2
 
9
3
  module JSON
@@ -1,4 +1,9 @@
1
- require 'ostruct'
1
+ #frozen_string_literal: false
2
+ begin
3
+ require 'ostruct'
4
+ rescue LoadError
5
+ warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`."
6
+ end
2
7
 
3
8
  module JSON
4
9
  class GenericObject < OpenStruct
@@ -48,12 +53,12 @@ module JSON
48
53
  end
49
54
 
50
55
  def [](name)
51
- table[name.to_sym]
52
- end
56
+ __send__(name)
57
+ end unless method_defined?(:[])
53
58
 
54
59
  def []=(name, value)
55
- __send__ "#{name}=", value
56
- end
60
+ __send__("#{name}=", value)
61
+ end unless method_defined?(:[]=)
57
62
 
58
63
  def |(other)
59
64
  self.class[other.to_hash.merge(to_hash)]
@@ -66,5 +71,5 @@ module JSON
66
71
  def to_json(*a)
67
72
  as_json.to_json(*a)
68
73
  end
69
- end
74
+ end if defined?(::OpenStruct)
70
75
  end
@@ -1,3 +1,4 @@
1
+ #frozen_string_literal: false
1
2
  module JSON
2
3
  MAP = {
3
4
  "\x0" => '\u0000',
@@ -36,86 +37,61 @@ module JSON
36
37
  '\\' => '\\\\',
37
38
  } # :nodoc:
38
39
 
39
- # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
40
- # UTF16 big endian characters as \u????, and return it.
41
- if defined?(::Encoding)
42
- def utf8_to_json(string) # :nodoc:
43
- string = string.dup
44
- string.force_encoding(::Encoding::ASCII_8BIT)
45
- string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
46
- string.force_encoding(::Encoding::UTF_8)
47
- string
48
- end
40
+ ESCAPE_PATTERN = /[\/"\\\x0-\x1f]/n # :nodoc:
49
41
 
50
- def utf8_to_json_ascii(string) # :nodoc:
51
- string = string.dup
52
- string.force_encoding(::Encoding::ASCII_8BIT)
53
- string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] }
54
- string.gsub!(/(
55
- (?:
56
- [\xc2-\xdf][\x80-\xbf] |
57
- [\xe0-\xef][\x80-\xbf]{2} |
58
- [\xf0-\xf4][\x80-\xbf]{3}
59
- )+ |
60
- [\x80-\xc1\xf5-\xff] # invalid
61
- )/nx) { |c|
62
- c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
63
- s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
64
- s.force_encoding(::Encoding::ASCII_8BIT)
65
- s.gsub!(/.{4}/n, '\\\\u\&')
66
- s.force_encoding(::Encoding::UTF_8)
67
- }
68
- string.force_encoding(::Encoding::UTF_8)
69
- string
70
- rescue => e
71
- raise GeneratorError.wrap(e)
72
- end
42
+ SCRIPT_SAFE_MAP = MAP.merge(
43
+ '/' => '\\/',
44
+ "\u2028".b => '\u2028',
45
+ "\u2029".b => '\u2029',
46
+ )
73
47
 
74
- def valid_utf8?(string)
75
- encoding = string.encoding
76
- (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
77
- string.valid_encoding?
78
- end
79
- module_function :valid_utf8?
80
- else
81
- def utf8_to_json(string) # :nodoc:
82
- string.gsub(/["\\\x0-\x1f]/n) { MAP[$&] }
83
- end
48
+ SCRIPT_SAFE_ESCAPE_PATTERN = Regexp.union(ESCAPE_PATTERN, "\u2028".b, "\u2029".b)
84
49
 
85
- def utf8_to_json_ascii(string) # :nodoc:
86
- string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
87
- string.gsub!(/(
88
- (?:
89
- [\xc2-\xdf][\x80-\xbf] |
90
- [\xe0-\xef][\x80-\xbf]{2} |
91
- [\xf0-\xf4][\x80-\xbf]{3}
92
- )+ |
93
- [\x80-\xc1\xf5-\xff] # invalid
94
- )/nx) { |c|
95
- c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
96
- s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
97
- s.gsub!(/.{4}/n, '\\\\u\&')
98
- }
99
- string
100
- rescue => e
101
- raise GeneratorError.wrap(e)
50
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
51
+ # UTF16 big endian characters as \u????, and return it.
52
+ def utf8_to_json(string, script_safe = false) # :nodoc:
53
+ string = string.dup
54
+ string.force_encoding(::Encoding::ASCII_8BIT)
55
+ if script_safe
56
+ string.gsub!(SCRIPT_SAFE_ESCAPE_PATTERN) { SCRIPT_SAFE_MAP[$&] || $& }
57
+ else
58
+ string.gsub!(ESCAPE_PATTERN) { MAP[$&] || $& }
102
59
  end
60
+ string.force_encoding(::Encoding::UTF_8)
61
+ string
62
+ end
103
63
 
104
- def valid_utf8?(string)
105
- string =~
106
- /\A( [\x09\x0a\x0d\x20-\x7e] # ASCII
107
- | [\xc2-\xdf][\x80-\xbf] # non-overlong 2-byte
108
- | \xe0[\xa0-\xbf][\x80-\xbf] # excluding overlongs
109
- | [\xe1-\xec\xee\xef][\x80-\xbf]{2} # straight 3-byte
110
- | \xed[\x80-\x9f][\x80-\xbf] # excluding surrogates
111
- | \xf0[\x90-\xbf][\x80-\xbf]{2} # planes 1-3
112
- | [\xf1-\xf3][\x80-\xbf]{3} # planes 4-15
113
- | \xf4[\x80-\x8f][\x80-\xbf]{2} # plane 16
114
- )*\z/nx
115
- end
64
+ def utf8_to_json_ascii(string, script_safe = false) # :nodoc:
65
+ string = string.dup
66
+ string.force_encoding(::Encoding::ASCII_8BIT)
67
+ map = script_safe ? SCRIPT_SAFE_MAP : MAP
68
+ string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& }
69
+ string.gsub!(/(
70
+ (?:
71
+ [\xc2-\xdf][\x80-\xbf] |
72
+ [\xe0-\xef][\x80-\xbf]{2} |
73
+ [\xf0-\xf4][\x80-\xbf]{3}
74
+ )+ |
75
+ [\x80-\xc1\xf5-\xff] # invalid
76
+ )/nx) { |c|
77
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
78
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
79
+ s.force_encoding(::Encoding::ASCII_8BIT)
80
+ s.gsub!(/.{4}/n, '\\\\u\&')
81
+ s.force_encoding(::Encoding::UTF_8)
82
+ }
83
+ string.force_encoding(::Encoding::UTF_8)
84
+ string
85
+ rescue => e
86
+ raise GeneratorError.wrap(e)
116
87
  end
117
- module_function :utf8_to_json, :utf8_to_json_ascii, :valid_utf8?
118
88
 
89
+ def valid_utf8?(string)
90
+ encoding = string.encoding
91
+ (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
92
+ string.valid_encoding?
93
+ end
94
+ module_function :utf8_to_json, :utf8_to_json_ascii, :valid_utf8?
119
95
 
120
96
  module Pure
121
97
  module Generator
@@ -148,14 +124,14 @@ module JSON
148
124
  # * *space_before*: a string that is put before a : pair delimiter (default: ''),
149
125
  # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
150
126
  # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
127
+ # * *script_safe*: true if U+2028, U+2029 and forward slash (/) should be escaped
128
+ # as to make the JSON object safe to interpolate in a script tag (default: false).
151
129
  # * *check_circular*: is deprecated now, use the :max_nesting option instead,
152
130
  # * *max_nesting*: sets the maximum level of data structure nesting in
153
131
  # the generated JSON, max_nesting = 0 if no maximum should be checked.
154
132
  # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
155
133
  # generated, otherwise an exception is thrown, if these values are
156
134
  # encountered. This options defaults to false.
157
- # * *quirks_mode*: Enables quirks_mode for parser, that is for example
158
- # generating single JSON values instead of documents is possible.
159
135
  def initialize(opts = {})
160
136
  @indent = ''
161
137
  @space = ''
@@ -164,7 +140,8 @@ module JSON
164
140
  @array_nl = ''
165
141
  @allow_nan = false
166
142
  @ascii_only = false
167
- @quirks_mode = false
143
+ @script_safe = false
144
+ @strict = false
168
145
  @buffer_initial_length = 1024
169
146
  configure opts
170
147
  end
@@ -190,9 +167,13 @@ module JSON
190
167
  # the generated JSON, max_nesting = 0 if no maximum is checked.
191
168
  attr_accessor :max_nesting
192
169
 
193
- # If this attribute is set to true, quirks mode is enabled, otherwise
194
- # it's disabled.
195
- attr_accessor :quirks_mode
170
+ # If this attribute is set to true, forward slashes will be escaped in
171
+ # all json strings.
172
+ attr_accessor :script_safe
173
+
174
+ # If this attribute is set to true, attempting to serialize types not
175
+ # supported by the JSON spec will raise a JSON::GeneratorError
176
+ attr_accessor :strict
196
177
 
197
178
  # :stopdoc:
198
179
  attr_reader :buffer_initial_length
@@ -233,9 +214,14 @@ module JSON
233
214
  @ascii_only
234
215
  end
235
216
 
236
- # Returns true, if quirks mode is enabled. Otherwise returns false.
237
- def quirks_mode?
238
- @quirks_mode
217
+ # Returns true, if forward slashes are escaped. Otherwise returns false.
218
+ def script_safe?
219
+ @script_safe
220
+ end
221
+
222
+ # Returns true, if forward slashes are escaped. Otherwise returns false.
223
+ def strict?
224
+ @strict
239
225
  end
240
226
 
241
227
  # Configure this State instance with the Hash _opts_, and return
@@ -248,7 +234,7 @@ module JSON
248
234
  else
249
235
  raise TypeError, "can't convert #{opts.class} into Hash"
250
236
  end
251
- for key, value in opts
237
+ opts.each do |key, value|
252
238
  instance_variable_set "@#{key}", value
253
239
  end
254
240
  @indent = opts[:indent] if opts.key?(:indent)
@@ -259,9 +245,18 @@ module JSON
259
245
  @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
260
246
  @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
261
247
  @depth = opts[:depth] || 0
262
- @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode)
263
248
  @buffer_initial_length ||= opts[:buffer_initial_length]
264
249
 
250
+ @script_safe = if opts.key?(:script_safe)
251
+ !!opts[:script_safe]
252
+ elsif opts.key?(:escape_slash)
253
+ !!opts[:escape_slash]
254
+ else
255
+ false
256
+ end
257
+
258
+ @strict = !!opts[:strict] if opts.key?(:strict)
259
+
265
260
  if !opts.key?(:max_nesting) # defaults to 100
266
261
  @max_nesting = 100
267
262
  elsif opts[:max_nesting]
@@ -277,7 +272,7 @@ module JSON
277
272
  # passed to the configure method.
278
273
  def to_h
279
274
  result = {}
280
- for iv in instance_variables
275
+ instance_variables.each do |iv|
281
276
  iv = iv.to_s[1..-1]
282
277
  result[iv.to_sym] = self[iv]
283
278
  end
@@ -286,20 +281,14 @@ module JSON
286
281
 
287
282
  alias to_hash to_h
288
283
 
289
- # Generates a valid JSON document from object +obj+ and returns the
290
- # result. If no valid JSON document can be created this method raises a
284
+ # Generates a valid JSON document from object +obj+ and
285
+ # returns the result. If no valid JSON document can be
286
+ # created this method raises a
291
287
  # GeneratorError exception.
292
288
  def generate(obj)
293
289
  result = obj.to_json(self)
294
290
  JSON.valid_utf8?(result) or raise GeneratorError,
295
291
  "source sequence #{result.inspect} is illegal/malformed utf-8"
296
- unless @quirks_mode
297
- unless result =~ /\A\s*\[/ && result =~ /\]\s*\Z/ ||
298
- result =~ /\A\s*\{/ && result =~ /\}\s*\Z/
299
- then
300
- raise GeneratorError, "only generation of JSON objects or arrays allowed"
301
- end
302
- end
303
292
  result
304
293
  end
305
294
 
@@ -308,7 +297,8 @@ module JSON
308
297
  if respond_to?(name)
309
298
  __send__(name)
310
299
  else
311
- instance_variable_get("@#{name}")
300
+ instance_variable_get("@#{name}") if
301
+ instance_variables.include?("@#{name}".to_sym) # avoid warning
312
302
  end
313
303
  end
314
304
 
@@ -326,7 +316,13 @@ module JSON
326
316
  # Converts this object to a string (calling #to_s), converts
327
317
  # it to a JSON string, and returns the result. This is a fallback, if no
328
318
  # special method #to_json was defined for some object.
329
- def to_json(*) to_s.to_json end
319
+ def to_json(generator_state)
320
+ if generator_state.strict?
321
+ raise GeneratorError, "#{self.class} not allowed in JSON"
322
+ else
323
+ to_s.to_json
324
+ end
325
+ end
330
326
  end
331
327
 
332
328
  module Hash
@@ -349,21 +345,18 @@ module JSON
349
345
  end
350
346
 
351
347
  def json_transform(state)
352
- delim = ','
353
- delim << state.object_nl
354
- result = '{'
355
- result << state.object_nl
348
+ delim = ",#{state.object_nl}"
349
+ result = "{#{state.object_nl}"
356
350
  depth = state.depth += 1
357
351
  first = true
358
352
  indent = !state.object_nl.empty?
359
- each { |key,value|
353
+ each { |key, value|
360
354
  result << delim unless first
361
355
  result << state.indent * depth if indent
362
- result << key.to_s.to_json(state)
363
- result << state.space_before
364
- result << ':'
365
- result << state.space
366
- if value.respond_to?(:to_json)
356
+ result = "#{result}#{key.to_s.to_json(state)}#{state.space_before}:#{state.space}"
357
+ if state.strict?
358
+ raise GeneratorError, "#{value.class} not allowed in JSON"
359
+ elsif value.respond_to?(:to_json)
367
360
  result << value.to_json(state)
368
361
  else
369
362
  result << %{"#{String(value)}"}
@@ -371,8 +364,10 @@ module JSON
371
364
  first = false
372
365
  }
373
366
  depth = state.depth -= 1
374
- result << state.object_nl
375
- result << state.indent * depth if indent
367
+ unless first
368
+ result << state.object_nl
369
+ result << state.indent * depth if indent
370
+ end
376
371
  result << '}'
377
372
  result
378
373
  end
@@ -402,7 +397,9 @@ module JSON
402
397
  each { |value|
403
398
  result << delim unless first
404
399
  result << state.indent * depth if indent
405
- if value.respond_to?(:to_json)
400
+ if state.strict?
401
+ raise GeneratorError, "#{value.class} not allowed in JSON"
402
+ elsif value.respond_to?(:to_json)
406
403
  result << value.to_json(state)
407
404
  else
408
405
  result << %{"#{String(value)}"}
@@ -445,38 +442,24 @@ module JSON
445
442
  end
446
443
 
447
444
  module String
448
- if defined?(::Encoding)
449
- # This string should be encoded with UTF-8 A call to this method
450
- # returns a JSON string encoded with UTF16 big endian characters as
451
- # \u????.
452
- def to_json(state = nil, *args)
453
- state = State.from_state(state)
454
- if encoding == ::Encoding::UTF_8
455
- string = self
456
- else
457
- string = encode(::Encoding::UTF_8)
458
- end
459
- if state.ascii_only?
460
- '"' << JSON.utf8_to_json_ascii(string) << '"'
461
- else
462
- '"' << JSON.utf8_to_json(string) << '"'
463
- end
445
+ # This string should be encoded with UTF-8 A call to this method
446
+ # returns a JSON string encoded with UTF16 big endian characters as
447
+ # \u????.
448
+ def to_json(state = nil, *args)
449
+ state = State.from_state(state)
450
+ if encoding == ::Encoding::UTF_8
451
+ string = self
452
+ else
453
+ string = encode(::Encoding::UTF_8)
464
454
  end
465
- else
466
- # This string should be encoded with UTF-8 A call to this method
467
- # returns a JSON string encoded with UTF16 big endian characters as
468
- # \u????.
469
- def to_json(state = nil, *args)
470
- state = State.from_state(state)
471
- if state.ascii_only?
472
- '"' << JSON.utf8_to_json_ascii(self) << '"'
473
- else
474
- '"' << JSON.utf8_to_json(self) << '"'
475
- end
455
+ if state.ascii_only?
456
+ '"' << JSON.utf8_to_json_ascii(string, state.script_safe) << '"'
457
+ else
458
+ '"' << JSON.utf8_to_json(string, state.script_safe) << '"'
476
459
  end
477
460
  end
478
461
 
479
- # Module that holds the extinding methods if, the String module is
462
+ # Module that holds the extending methods if, the String module is
480
463
  # included.
481
464
  module Extend
482
465
  # Raw Strings are JSON Objects (the raw bytes are stored in an