]>
git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb
3 # Licensed to the Apache Software Foundation (ASF) under one
4 # or more contributor license agreements. See the NOTICE file
5 # distributed with this work for additional information
6 # regarding copyright ownership. The ASF licenses this file
7 # to you under the Apache License, Version 2.0 (the
8 # "License"); you may not use this file except in compliance
9 # with the License. You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing,
14 # software distributed under the License is distributed on an
15 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 # KIND, either express or implied. See the License for the
17 # specific language governing permissions and limitations
35 @data = @trans.read(1)
43 @data = @trans.read(1)
51 # Class to serve as base JSON context and as base class for other context
55 @
@kJSONElemSeparator = ','
57 # Write context data to the trans. Default is to do nothing.
63 # Read context data from the trans. Default is to do nothing.
69 # Return true if numbers need to be escaped as strings in this context.
70 # Default behavior is to return false.
77 # Context class for object member key-value pairs
78 class JSONPairContext
< JSONContext
79 @
@kJSONPairSeparator = ':'
91 trans
.write(@colon ? @
@kJSONPairSeparator : @
@kJSONElemSeparator)
101 ch
= (@colon ? @
@kJSONPairSeparator : @
@kJSONElemSeparator)
103 JsonProtocol
::read_syntax_char(reader
, ch
)
107 # Numbers must be turned into strings if they are the key part of a pair
113 # Context class for lists
114 class JSONListContext
< JSONContext
124 trans
.write(@
@kJSONElemSeparator)
132 JsonProtocol
::read_syntax_char(reader
, @
@kJSONElemSeparator)
137 class JsonProtocol
< BaseProtocol
139 @
@kJSONObjectStart = '{'
140 @
@kJSONObjectEnd = '}'
141 @
@kJSONArrayStart = '['
142 @
@kJSONArrayEnd = ']'
143 @
@kJSONNewline = '\n'
144 @
@kJSONBackslash = '\\'
145 @
@kJSONStringDelimiter = '"'
147 @
@kThriftVersion1 = 1
150 @
@kThriftInfinity = "Infinity"
151 @
@kThriftNegativeInfinity = "-Infinity"
153 def initialize(trans
)
155 @context = JSONContext
.new
156 @contexts = Array
.new
157 @reader = LookaheadReader
.new(trans
)
160 def get_type_name_for_type_id(id
)
185 raise NotImplementedError
189 def get_type_id_for_type_name(name
)
194 elsif (name
== "i16")
196 elsif (name
== "i32")
198 elsif (name
== "i64")
200 elsif (name
== "dbl")
201 result
= Types
::DOUBLE
202 elsif (name
== "str")
203 result
= Types
::STRING
204 elsif (name
== "rec")
205 result
= Types
::STRUCT
206 elsif (name
== "map")
208 elsif (name
== "set")
210 elsif (name
== "lst")
215 if (result
== Types
::STOP)
216 raise NotImplementedError
221 # Static helper functions
223 # Read 1 character from the trans and verify that it is the expected character ch.
224 # Throw a protocol exception if it is not.
225 def self.read_syntax_char(reader
, ch
)
228 raise ProtocolException
.new(ProtocolException
::INVALID_DATA, "Expected \'#{ch}\' got \'#{ch2}\'.")
232 # Return true if the character ch is in [-+0-9.Ee]; false otherwise
233 def is_json_numeric(ch
)
235 when '+', '-', '.', '0' .. '9', 'E', "e"
242 def push_context(context
)
243 @contexts.push(@context)
248 @context = @contexts.pop
251 # Write the character ch as a JSON escape sequence ("\u00xx")
252 def write_json_escape_char(ch
)
255 if (ch_value
.kind_of
? String
)
256 ch_value
= ch
.bytes
.first
258 trans
.write(ch_value
.to_s(16).rjust(4,'0'))
261 # Write the character ch as part of a JSON string, escaping as appropriate.
262 def write_json_char(ch
)
263 # This table describes the handling for the first 0x30 characters
264 # 0 : escape using "\u00xx" notation
265 # 1 : just output index
266 # <other> : escape using "\<other>" notation
268 # 0 1 2 3 4 5 6 7 8 9 A B C D E F
269 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, # 0
270 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 1
271 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 2
275 if (ch_value
.kind_of
? String
)
276 ch_value
= ch
.bytes
.first
278 if (ch_value
>= 0x30)
279 if (ch
== @
@kJSONBackslash) # Only special character >= 0x30 is '\'
280 trans
.write(@
@kJSONBackslash)
281 trans
.write(@
@kJSONBackslash)
286 outCh
= kJSONCharTable
[ch_value
];
287 # Check if regular character, backslash escaped, or JSON escaped
288 if outCh
.kind_of
? String
289 trans
.write(@
@kJSONBackslash)
294 write_json_escape_char(ch
)
299 # Write out the contents of the string str as a JSON string, escaping characters as appropriate.
300 def write_json_string(str
)
301 @context.write(trans
)
302 trans
.write(@
@kJSONStringDelimiter)
303 str
.split('').each
do |ch
|
306 trans
.write(@
@kJSONStringDelimiter)
309 # Write out the contents of the string as JSON string, base64-encoding
310 # the string's contents, and escaping as appropriate
311 def write_json_base64(str
)
312 @context.write(trans
)
313 trans
.write(@
@kJSONStringDelimiter)
314 trans
.write(Base64
.strict_encode64(str
))
315 trans
.write(@
@kJSONStringDelimiter)
318 # Convert the given integer type to a JSON number, or a string
319 # if the context requires it (eg: key in a map pair).
320 def write_json_integer(num
)
321 @context.write(trans
)
322 escapeNum
= @context.escapeNum
324 trans
.write(@
@kJSONStringDelimiter)
326 trans
.write(num
.to_s
);
328 trans
.write(@
@kJSONStringDelimiter)
332 # Convert the given double to a JSON string, which is either the number,
333 # "NaN" or "Infinity" or "-Infinity".
334 def write_json_double(num
)
335 @context.write(trans
)
336 # Normalize output of thrift::to_string for NaNs and Infinities
341 elsif (num
.infinite
?)
343 val
= @
@kThriftInfinity;
345 val
= @
@kThriftNegativeInfinity;
351 escapeNum
= special
|| @context.escapeNum
353 trans
.write(@
@kJSONStringDelimiter)
357 trans
.write(@
@kJSONStringDelimiter)
361 def write_json_object_start
362 @context.write(trans
)
363 trans
.write(@
@kJSONObjectStart)
364 push_context(JSONPairContext
.new
);
367 def write_json_object_end
369 trans
.write(@
@kJSONObjectEnd)
372 def write_json_array_start
373 @context.write(trans
)
374 trans
.write(@
@kJSONArrayStart)
375 push_context(JSONListContext
.new
);
378 def write_json_array_end
380 trans
.write(@
@kJSONArrayEnd)
383 def write_message_begin(name
, type
, seqid
)
384 write_json_array_start
385 write_json_integer(@
@kThriftVersion1)
386 write_json_string(name
)
387 write_json_integer(type
)
388 write_json_integer(seqid
)
391 def write_message_end
395 def write_struct_begin(name
)
396 write_json_object_start
400 write_json_object_end
403 def write_field_begin(name
, type
, id
)
404 write_json_integer(id
)
405 write_json_object_start
406 write_json_string(get_type_name_for_type_id(type
))
410 write_json_object_end
413 def write_field_stop
; nil; end
415 def write_map_begin(ktype
, vtype
, size
)
416 write_json_array_start
417 write_json_string(get_type_name_for_type_id(ktype
))
418 write_json_string(get_type_name_for_type_id(vtype
))
419 write_json_integer(size
)
420 write_json_object_start
424 write_json_object_end
428 def write_list_begin(etype
, size
)
429 write_json_array_start
430 write_json_string(get_type_name_for_type_id(etype
))
431 write_json_integer(size
)
438 def write_set_begin(etype
, size
)
439 write_json_array_start
440 write_json_string(get_type_name_for_type_id(etype
))
441 write_json_integer(size
)
449 write_json_integer(bool
? 1 : 0)
453 write_json_integer(byte
)
457 write_json_integer(i16
)
461 write_json_integer(i32
)
465 write_json_integer(i64
)
468 def write_double(dub
)
469 write_json_double(dub
)
472 def write_string(str
)
473 write_json_string(str
)
476 def write_binary(str
)
477 write_json_base64(str
)
484 # Reads 1 byte and verifies that it matches ch.
485 def read_json_syntax_char(ch
)
486 JsonProtocol
::read_syntax_char(@reader, ch
)
489 # Decodes the four hex parts of a JSON escaped string character and returns
490 # the character via out.
492 # Note - this only supports Unicode characters in the BMP (U+0000 to U+FFFF);
493 # characters above the BMP are encoded as two escape sequences (surrogate pairs),
494 # which is not yet implemented
495 def read_json_escape_char
500 if RUBY_VERSION >= '1.9'
501 str
.hex
.chr(Encoding
::UTF_8)
507 # Decodes a JSON string, including unescaping, and returns the string via str
508 def read_json_string(skipContext
= false)
509 # This string's characters must match up with the elements in escape_char_vals.
510 # I don't have '/' on this list even though it appears on www.json.org --
511 # it is not in the RFC -> it is. See RFC 4627
512 escape_chars
= "\"\\/bfnrt"
514 # The elements of this array must match up with the sequence of characters in
517 "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t",
521 @context.read(@reader)
523 read_json_syntax_char(@
@kJSONStringDelimiter)
528 if (ch
== @
@kJSONStringDelimiter)
531 if (ch
== @
@kJSONBackslash)
534 ch
= read_json_escape_char
536 pos
= escape_chars
.index(ch
);
537 if (pos
.nil?) # not found
538 raise ProtocolException
.new(ProtocolException
::INVALID_DATA, "Expected control char, got \'#{ch}\'.")
540 ch
= escape_char_vals
[pos
]
548 # Reads a block of base64 characters, decoding it, and returns via str
550 str
= read_json_string
553 # Add missing padding
558 Base64
.strict_decode64(str
)
561 # Reads a sequence of characters, stopping at the first one that is not
562 # a valid JSON numeric character.
563 def read_json_numeric_chars
567 if (!is_json_numeric(ch
))
576 # Reads a sequence of characters and assembles them into a number,
577 # returning them via num
578 def read_json_integer
579 @context.read(@reader)
580 if (@context.escapeNum
)
581 read_json_syntax_char(@
@kJSONStringDelimiter)
583 str
= read_json_numeric_chars
588 raise ProtocolException
.new(ProtocolException
::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
591 if (@context.escapeNum
)
592 read_json_syntax_char(@
@kJSONStringDelimiter)
598 # Reads a JSON number or string and interprets it as a double.
600 @context.read(@reader)
602 if (@reader.peek
== @
@kJSONStringDelimiter)
603 str
= read_json_string(true)
604 # Check for NaN, Infinity and -Infinity
605 if (str
== @
@kThriftNan)
606 num
= (+1.0/0.0)/(+1.0/0.0)
607 elsif (str
== @
@kThriftInfinity)
609 elsif (str
== @
@kThriftNegativeInfinity)
612 if (!@context.escapeNum
)
613 # Raise exception -- we should not be in a string in this case
614 raise ProtocolException
.new(ProtocolException
::INVALID_DATA, "Numeric data unexpectedly quoted")
619 raise ProtocolException
.new(ProtocolException
::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
623 if (@context.escapeNum
)
624 # This will throw - we should have had a quote if escapeNum == true
625 read_json_syntax_char(@
@kJSONStringDelimiter)
627 str
= read_json_numeric_chars
631 raise ProtocolException
.new(ProtocolException
::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
637 def read_json_object_start
638 @context.read(@reader)
639 read_json_syntax_char(@
@kJSONObjectStart)
640 push_context(JSONPairContext
.new
)
644 def read_json_object_end
645 read_json_syntax_char(@
@kJSONObjectEnd)
650 def read_json_array_start
651 @context.read(@reader)
652 read_json_syntax_char(@
@kJSONArrayStart)
653 push_context(JSONListContext
.new
)
657 def read_json_array_end
658 read_json_syntax_char(@
@kJSONArrayEnd)
663 def read_message_begin
664 read_json_array_start
665 version = read_json_integer
666 if (version != @
@kThriftVersion1)
667 raise ProtocolException
.new(ProtocolException
::BAD_VERSION, 'Message contained bad version.')
669 name
= read_json_string
670 message_type
= read_json_integer
671 seqid
= read_json_integer
672 [name
, message_type
, seqid
]
680 def read_struct_begin
681 read_json_object_start
691 # Check if we hit the end of the list
693 if (ch
== @
@kJSONObjectEnd)
694 field_type
= Types
::STOP
696 field_id
= read_json_integer
697 read_json_object_start
698 field_type
= get_type_id_for_type_name(read_json_string
)
700 [nil, field_type
, field_id
]
708 read_json_array_start
709 key_type
= get_type_id_for_type_name(read_json_string
)
710 val_type
= get_type_id_for_type_name(read_json_string
)
711 size
= read_json_integer
712 read_json_object_start
713 [key_type
, val_type
, size
]
722 read_json_array_start
723 [get_type_id_for_type_name(read_json_string
), read_json_integer
]
731 read_json_array_start
732 [get_type_id_for_type_name(read_json_string
), read_json_integer
]
773 "json(#{super.to_s})"
777 class JsonProtocolFactory
< BaseProtocolFactory
778 def get_protocol(trans
)
779 return Thrift
::JsonProtocol.new(trans
)