File Coverage

blib/lib/Thrift/JSONProtocol.pm
Criterion Covered Total %
statement 199 476 41.8
branch 16 112 14.2
condition 0 9 0.0
subroutine 58 111 52.2
pod 1 68 1.4
total 274 776 35.3


line stmt bran cond sub pod time code
1             package Thrift::JSONProtocol;
2              
3             =head1 NAME
4              
5             Thrift::JSONProtocol
6              
7             =head1 DESCRIPTION
8              
9             JSON protocol implementation for thrift.
10              
11             This is a full-featured protocol supporting write and read.
12              
13             This code was adapted from the Java implementation.
14              
15             Please see the C++ class header for a detailed description of the protocol's wire format.
16              
17             =cut
18              
19 1     1   57223 use strict;
  1         3  
  1         52  
20 1     1   9 use warnings;
  1         3  
  1         38  
21 1     1   7 use Thrift;
  1         2  
  1         23  
22 1     1   7 use Thrift::Protocol;
  1         3  
  1         28  
23 1     1   6 use base qw(Thrift::Protocol Class::Accessor);
  1         2  
  1         231  
24              
25 1     1   6 use utf8;
  1         2  
  1         9  
26 1     1   29 use Encode;
  1         1  
  1         112  
27 1     1   996 use MIME::Base64;
  1         894  
  1         289  
28              
29             __PACKAGE__->mk_accessors(qw(trans context_ reader_));
30 43     43 0 193 sub transport { shift->{trans} }
31              
32             use constant {
33 1         7059 COMMA => ',',
34             COLON => ':',
35             LBRACE => '{',
36             RBRACE => '}',
37             LBRACKET => '[',
38             RBRACKET => ']',
39             QUOTE => '"',
40             BACKSLASH => '\\',
41             ZERO => '0',
42              
43             ESCSEQ => join('','\\','u','0','0'),
44             VERSION => 1,
45              
46             JSON_CHAR_TABLE => [
47             # 0 1 2 3 4 5 6 7 8 9 A B C D E F
48             0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, # 0
49             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 1
50             1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 2
51             ],
52              
53             ESCAPE_CHARS => "\"\\bfnrt",
54             ESCAPE_CHAR_VALS => [ '"', '\\', "\b", "\f", "\n", "\r", "\t", ],
55              
56             NAME_BOOL => 'tf',
57             NAME_BYTE => 'i8',
58             NAME_I16 => 'i16',
59             NAME_I32 => 'i32',
60             NAME_I64 => 'i64',
61             NAME_DOUBLE => 'dbl',
62             NAME_STRUCT => 'rec',
63             NAME_STRING => 'str',
64             NAME_MAP => 'map',
65             NAME_LIST => 'lst',
66             NAME_SET => 'set',
67 1     1   8 };
  1         3  
68              
69             my %_getTypeNameForTypeID = (
70             TType::BOOL => NAME_BOOL,
71             TType::BYTE => NAME_BYTE,
72             TType::I16 => NAME_I16,
73             TType::I32 => NAME_I32,
74             TType::I64 => NAME_I64,
75             TType::DOUBLE => NAME_DOUBLE,
76             TType::STRING => NAME_STRING,
77             TType::STRUCT => NAME_STRUCT,
78             TType::MAP => NAME_MAP,
79             TType::SET => NAME_SET,
80             TType::LIST => NAME_LIST,
81             );
82              
83             ##
84             ## Class methods
85             ##
86              
87             sub new {
88 1     1 1 426 my $class = shift;
89              
90 1         15 my $self = $class->SUPER::new(@_);
91              
92             # Stack of nested contexts that we may be in
93 1         19 $self->{contextStack_} = [];
94              
95             # Current context that we are in
96 1         11 $self->{context_} = Thrift::JSONProtocol::JSONBaseContext->new( protocol => $self );
97              
98             # Reader that manages a 1-byte buffer
99 1         15 $self->{reader_} = Thrift::JSONProtocol::LookaheadReader->new( protocol => $self );
100              
101 1         4 return $self;
102             }
103              
104             sub getTypeNameForTypeID {
105 2     2 0 6 my ($typeID) = @_;
106              
107 2 50       1225 if (my $typeName = $_getTypeNameForTypeID{$typeID}) {
108 2         11 return $typeName;
109             }
110 0         0 die TProtocolException->new( "Unrecognized type $typeID", TProtocolException::UNKNOWN )
111             }
112              
113             sub getTypeIDForTypeName {
114 0     0 0 0 my ($name) = @_;
115 0         0 my $result = TType::STOP;
116 0         0 my @name = split //, $name;
117 0 0       0 if (int(@name) > 1) {
118 0 0       0 if ($name[0] eq 'd') { $result = TType::DOUBLE }
  0 0       0  
    0          
    0          
    0          
    0          
    0          
119             elsif ($name[0] eq 'i') {
120 0 0       0 if ($name[1] eq '8') { $result = TType::BYTE }
  0 0       0  
    0          
    0          
121 0         0 elsif ($name[1] eq '1') { $result = TType::I16 }
122 0         0 elsif ($name[1] eq '3') { $result = TType::I32 }
123 0         0 elsif ($name[1] eq '6') { $result = TType::I64 }
124             }
125 0         0 elsif ($name[0] eq 'l') { $result = TType::LIST }
126 0         0 elsif ($name[0] eq 'm') { $result = TType::MAP }
127 0         0 elsif ($name[0] eq 'r') { $result = TType::STRUCT }
128             elsif ($name[0] eq 's') {
129 0 0       0 if ($name[1] eq 't') { $result = TType::STRING }
  0 0       0  
130 0         0 elsif ($name[1] eq 'e') { $result = TType::SET }
131             }
132 0         0 elsif ($name[0] eq 't') { $result = TType::BOOL }
133             }
134 0 0       0 if ($result == TType::STOP) {
135 0         0 die TProtocolException->new("Unrecognized type", TProtocolException::UNKNOWN);
136             }
137 0         0 return $result;
138             }
139              
140             sub check_utf8 {
141 8     8 0 11 my ($string_ref) = @_;
142              
143 8 50       31 return if ! utf8::is_utf8($$string_ref);
144 0         0 $$string_ref = Encode::encode_utf8($$string_ref);
145             }
146              
147             ##
148             ## Object methods
149             ##
150              
151             #
152             # Helper methods
153             #
154              
155             # Push a new JSON context onto the stack.
156             sub pushContext {
157 4     4 0 6 my ($self, $context) = @_;
158 4         7 push @{ $self->{contextStack_} }, delete $self->{context_};
  4         13  
159 4         11 $self->{context_} = $context;
160             }
161              
162             # Pop the last JSON context off the stack
163             sub popContext {
164 4     4 0 6 my ($self) = @_;
165 4         3 my $context = pop @{ $self->{contextStack_} };
  4         9  
166 4         9 $self->{context_} = $context;
167 4         37 return $context;
168             }
169              
170             # Read a byte that must match $expected; otherwise an excpetion is thrown.
171             sub readJSONSyntaxChar {
172 0     0 0 0 my ($self, $expected) = @_;
173 0         0 my $got = $self->{reader_}->read();
174 0 0       0 if ($got ne $expected) {
175 0         0 die TProtocolException->new("Unexpected character: $got", TProtocolException::INVALID_DATA);
176             }
177 0         0 return length $got;
178             }
179              
180             # Convenience method for writing and getting the length of the string written
181             sub write {
182 34     34 0 44 my ($self, $string) = @_;
183 34         71 $self->transport->write($string);
184 34         331 return length $string;
185             }
186              
187             #
188             # Read/write JSON methods
189             #
190              
191             # Write the bytes in array buf as a JSON characters, escaping as needed
192             sub writeJSONString {
193 3     3 0 8 my ($self, $string) = @_;
194 3         19 my @b = split //, $string;
195              
196 3         8 my $xfer = 0;
197              
198 3         14 $xfer += $self->context_->write();
199              
200 3         12 $xfer += $self->write(QUOTE);
201              
202 3         7 my $len = int @b;
203 3         16 for (my $i = 0; $i < $len; $i++) {
204 9         18 my $ord = ord($b[$i]);
205 9 50       26 if (($ord & 0x00FF) >= 0x30) {
206 9 50       20 if ($b[$i] eq BACKSLASH) {
207 0         0 $xfer += $self->write(BACKSLASH . BACKSLASH);
208             }
209             else {
210 9         25 $xfer += $self->write($b[$i]);
211             }
212             }
213             else {
214 0         0 my $tmp = JSON_CHAR_TABLE->[$ord];
215 0 0       0 if ($tmp eq '1') {
    0          
216 0         0 $xfer += $self->write($b[$i]);
217             }
218             elsif ($tmp eq '0') {
219 0         0 my $hex = unpack 'H*', chr($ord);
220 0         0 $xfer += $self->write(ESCSEQ . $hex);
221             }
222             else {
223 0         0 $xfer += $self->write(BACKSLASH . $tmp);
224             }
225             }
226             }
227              
228 3         8 $xfer += $self->write(QUOTE);
229              
230 3         8 return $xfer;
231             }
232              
233             # Read in a JSON string, unescaping as appropriate.. Skip reading from the
234             # context if skipContext is true.
235             sub readJSONString {
236 0     0 0 0 my ($self, $string, $skipContext) = @_;
237              
238 0         0 my $xfer = 0;
239              
240 0 0       0 $xfer += $self->context_->read() if ! $skipContext;
241              
242 0         0 $xfer += $self->readJSONSyntaxChar(QUOTE);
243              
244 0         0 $$string = '';
245              
246 0         0 while (1) {
247 0         0 my $ch = $self->reader_->read();
248 0         0 $xfer++;
249 0 0       0 if ($ch eq QUOTE) {
250 0         0 last;
251             }
252 0 0       0 if ($ch eq substr ESCSEQ, 0, 1) {
253 0         0 $ch = $self->reader_->read();
254 0         0 $xfer++;
255 0 0       0 if ($ch eq substr ESCSEQ, 1, 1) {
256 0         0 $xfer += $self->readJSONSyntaxChar(ZERO);
257 0         0 $xfer += $self->readJSONSyntaxChar(ZERO);
258 0         0 my $tmp = $self->transport->readAll(2);
259 0         0 $ch = chr(hex($tmp));
260 0         0 $xfer += 2;
261             }
262             else {
263 0         0 my $off = index ESCAPE_CHARS, $ch;
264 0 0       0 if ($off == -1) {
265 0         0 die TProtocolException->new("Expected control char, got '$ch'", TProtocolException::INVALID_DATA);
266             }
267 0         0 $ch = ESCAPE_CHAR_VALS->[$off];
268             }
269             }
270 0         0 $$string .= $ch;
271             }
272              
273 0         0 return $xfer;
274             }
275              
276             # Write out number as a JSON value. If the context dictates so, it will be
277             # wrapped in quotes to output as a JSON string.
278              
279             sub writeJSONInteger {
280 7     7 0 13 my ($self, $num) = @_;
281 7         10 my $xfer = 0;
282 7         23 $xfer += $self->context_->write;
283 7         19 my $str = $num . '';
284 7         18 check_utf8(\$str);
285 7         19 my $escapeNum = $self->context_->escapeNum();
286              
287 7 100       46 $xfer += $self->write(QUOTE) if $escapeNum;
288 7         17 $xfer += $self->write($str);
289 7 100       22 $xfer += $self->write(QUOTE) if $escapeNum;
290 7         16 return $xfer;
291             }
292              
293             # Return true if the given byte could be a valid part of a JSON number.
294             sub isJSONNumeric {
295 0     0 0 0 my ($char) = @_;
296 0 0       0 return $char =~ m{^[-+.0-9Ee]$} ? 1 : 0;
297             }
298              
299             # Read in a sequence of characters that are all valid in JSON numbers. Does
300             # not do a complete regex check to validate that this is actually a number.
301             sub readJSONNumericChars {
302 0     0 0 0 my ($self, $str) = @_;
303 0         0 my $xfer = 0;
304 0         0 while (1) {
305 0         0 my $ch;
306 0         0 eval {
307 0         0 $ch = $self->reader_->peek();
308             };
309 0 0       0 if (my $ex = $@) {
310 0 0 0     0 if ($ex->isa('TTransportException') && $ex->{code} == TTransportException::END_OF_FILE) {
311 0         0 last;
312             }
313 0         0 die $ex;
314             }
315 0 0       0 if (! isJSONNumeric($ch)) {
316 0         0 last;
317             }
318 0         0 $$str .= $self->reader_->read();
319 0         0 $xfer++;
320             }
321 0         0 return $xfer;
322             }
323              
324             # Read in a JSON number. If the context dictates, read in enclosing quotes.
325             sub readJSONInteger {
326 0     0 0 0 my ($self, $int) = @_;
327 0         0 my $xfer = 0;
328 0         0 $xfer += $self->context_->read();
329 0         0 my $escapeNum = $self->context_->escapeNum();
330              
331 0         0 my $str;
332 0 0       0 $xfer += $self->readJSONSyntaxChar(QUOTE) if $escapeNum;
333 0         0 $xfer += $self->readJSONNumericChars(\$str);
334 0 0       0 $xfer += $self->readJSONSyntaxChar(QUOTE) if $escapeNum;
335              
336 0         0 $$int = $str * 1;
337 0         0 return $xfer;
338             }
339              
340             # Write out a double as a JSON value. If it is NaN or infinity or if the
341             # context dictates escaping, write out as JSON string.
342             sub writeJSONDouble {
343 0     0 0 0 my ($self, $num) = @_;
344 0         0 my $xfer = 0;
345 0         0 $xfer += $self->context_->write();
346 0         0 my $str = $num . '';
347 0         0 check_utf8(\$str);
348 0 0       0 my $special = $str =~ m{^-?(N|I)} ? 1 : 0;
349 0   0     0 my $escapeNum = $special || $self->context_->escapeNum;
350              
351 0 0       0 $xfer += $self->write(QUOTE) if $escapeNum;
352 0         0 $xfer += $self->write($str);
353 0 0       0 $xfer += $self->write(QUOTE) if $escapeNum;
354 0         0 return $xfer;
355             }
356              
357             # Read in a JSON double value. Throw if the value is not wrapped in quotes
358             # when expected or if wrapped in quotes when not expected.
359             sub readJSONDouble {
360 0     0 0 0 my ($self, $dub) = @_;
361 0         0 my $xfer = 0;
362 0         0 $xfer += $self->context_->read();
363              
364 0 0       0 if ($self->reader_->peek() eq QUOTE) {
365 0         0 my $str;
366 0         0 $xfer += $self->readJSONString(\$str, 1);
367 0 0       0 my $special = $str =~ m{^-?(N|I)} ? 1 : 0;
368 0 0 0     0 if (! $self->context_->escapeNum && ! $special) {
369             # Throw exception -- we should not be in a string in this case
370 0         0 die TProtocolException->new(
371             "Numeric data unexpectedly quoted",
372             TProtocolException::INVALID_DATA,
373             );
374             }
375 0         0 $$dub = $str;
376             }
377             else {
378 0 0       0 if ($self->context_->escapeNum()) {
379             # This will throw - we should have had a quote if escapeNum == true
380 0         0 $xfer += $self->readJSONSyntaxChar(QUOTE);
381             }
382 0         0 $xfer += $self->readJSONNumericChars($dub);
383             }
384 0         0 return $xfer;
385             }
386              
387             # Write out contents of byte array b as a JSON string with base-64 encoded
388             # data
389             sub writeJSONBase64 {
390 0     0 0 0 my ($self, $string) = @_;
391 0         0 return $self->writeJSONString( encode_base64($string, '') );
392             }
393              
394             # Read in a JSON string containing base-64 encoded data and decode it.
395             sub readJSONBase64 {
396 0     0 0 0 my ($self, $string) = @_;
397              
398 0         0 my $xfer = $self->readJSONString($string);
399 0         0 my $tmp = decode_base64($$string);
400 0         0 $$string = $tmp;
401 0         0 return $xfer;
402             }
403              
404             sub writeJSONObjectStart {
405 3     3 0 8 my ($self) = @_;
406 3         6 my $xfer = 0;
407 3         11 $xfer += $self->context_->write();
408 3         11 $xfer += $self->write(LBRACE);
409 3         19 $self->pushContext(
410             Thrift::JSONProtocol::JSONPairContext->new( protocol => $self )
411             );
412 3         5 return $xfer;
413             }
414              
415             sub readJSONObjectStart {
416 0     0 0 0 my ($self) = @_;
417 0         0 my $xfer = 0;
418 0         0 $xfer += $self->context_->read();
419 0         0 $xfer += $self->readJSONSyntaxChar(LBRACE);
420 0         0 $self->pushContext(
421             Thrift::JSONProtocol::JSONPairContext->new( protocol => $self )
422             );
423 0         0 return $xfer;
424             }
425              
426             sub writeJSONObjectEnd {
427 3     3 0 6 my ($self) = @_;
428 3         9 $self->popContext();
429 3         8 return $self->write(RBRACE);
430             }
431              
432             sub readJSONObjectEnd {
433 0     0 0 0 my ($self) = @_;
434 0         0 $self->popContext();
435 0         0 return $self->readJSONSyntaxChar(RBRACE);
436             }
437              
438             sub writeJSONArrayStart {
439 1     1 0 2 my ($self) = @_;
440 1         3 my $xfer = 0;
441 1         7 $xfer += $self->context_->write();
442 1         5 $xfer += $self->write(LBRACKET);
443 1         11 $self->pushContext(
444             Thrift::JSONProtocol::JSONListContext->new( protocol => $self )
445             );
446 1         2 return $xfer;
447             }
448              
449             sub readJSONArrayStart {
450 0     0 0 0 my ($self) = @_;
451 0         0 my $xfer = 0;
452 0         0 $xfer += $self->context_->read();
453 0         0 $xfer += $self->readJSONSyntaxChar(LBRACKET);
454 0         0 $self->pushContext(
455             Thrift::JSONProtocol::JSONListContext->new( protocol => $self )
456             );
457 0         0 return $xfer;
458             }
459              
460             sub writeJSONArrayEnd {
461 1     1 0 2 my ($self) = @_;
462 1         3 $self->popContext();
463 1         3 return $self->write(RBRACKET);
464             }
465              
466             sub readJSONArrayEnd {
467 0     0 0 0 my ($self) = @_;
468 0         0 $self->popContext();
469 0         0 return $self->readJSONSyntaxChar(RBRACKET);
470             }
471              
472             #
473             # Thrift::Protocol methods
474             #
475              
476             sub writeMessageBegin {
477 1     1 0 506 my ($self, $name, $type, $seqid) = @_;
478              
479 1         8 check_utf8(\$name);
480            
481 1         2 my $xfer = 0;
482 1         7 $xfer += $self->writeJSONArrayStart();
483 1         6 $xfer += $self->writeJSONInteger(VERSION);
484 1         5 $xfer += $self->writeJSONString($name);
485 1         6 $xfer += $self->writeJSONInteger($type);
486 1         6 $xfer += $self->writeJSONInteger($seqid);
487 1         4 return $xfer;
488             }
489              
490             sub readMessageBegin {
491 0     0 0 0 my ($self, $name, $type, $seqid) = @_;
492              
493 0         0 my $xfer = 0;
494              
495 0         0 $xfer += $self->readJSONArrayStart();
496 0         0 my $version;
497 0         0 $xfer += $self->readJSONInteger(\$version);
498              
499 0 0       0 if ($version != VERSION) {
500 0         0 die TProtocolException->new("Message contained bad version.", TProtocolException::BAD_VERSION);
501             }
502              
503 0         0 $xfer += $self->readJSONString($name);
504 0         0 $xfer += $self->readJSONInteger($type);
505 0         0 $xfer += $self->readJSONInteger($seqid);
506 0         0 return $xfer;
507             }
508              
509             sub writeMessageEnd {
510 1     1 0 2 my ($self) = @_;
511 1         5 $self->writeJSONArrayEnd();
512             }
513              
514             sub readMessageEnd {
515 0     0 0 0 my ($self) = @_;
516 0         0 $self->readJSONArrayEnd();
517             }
518              
519             sub writeStructBegin {
520 1     1 0 3 my ($self) = @_;
521 1         5 $self->writeJSONObjectStart();
522             }
523              
524             sub readStructBegin {
525 0     0 0 0 my ($self, $name) = @_;
526 0         0 $self->readJSONObjectStart();
527             }
528              
529             sub writeStructEnd {
530 1     1 0 2 my ($self) = @_;
531 1         3 $self->writeJSONObjectEnd();
532             }
533              
534             sub readStructEnd {
535 0     0 0 0 my ($self) = @_;
536 0         0 $self->readJSONObjectEnd();
537             }
538              
539             sub writeFieldBegin {
540 2     2 0 37 my ($self, $fieldName, $fieldType, $fieldId) = @_;
541 2         4 my $xfer = 0;
542 2         7 $xfer += $self->writeJSONInteger($fieldId);
543 2         8 $xfer += $self->writeJSONObjectStart();
544 2         9 $xfer += $self->writeJSONString(getTypeNameForTypeID($fieldType));
545 2         8 return $xfer;
546             }
547              
548             sub readFieldBegin {
549 0     0 0 0 my ($self, $name, $fieldType, $fieldId) = @_;
550              
551 0         0 my $xfer = 0;
552 0         0 my $ch = $self->reader_->peek();
553 0 0       0 if ($ch eq RBRACE) {
554 0         0 $$fieldType = TType::STOP;
555             }
556             else {
557 0         0 $xfer += $self->readJSONInteger($fieldId);
558 0         0 $xfer += $self->readJSONObjectStart();
559 0         0 my $type;
560 0         0 $xfer += $self->readJSONString(\$type);
561 0         0 $$fieldType = getTypeIDForTypeName($type);
562             }
563 0         0 return $xfer;
564             }
565              
566             sub writeFieldEnd {
567 2     2 0 5 my ($self) = @_;
568 2         7 $self->writeJSONObjectEnd();
569             }
570              
571             sub readFieldEnd {
572 0     0 0 0 my ($self) = @_;
573 0         0 $self->readJSONObjectEnd();
574             }
575              
576 1     1 0 3 sub writeFieldStop { 0 }
577              
578             sub writeMapBegin {
579 0     0 0 0 my ($self, $keyType, $valType, $size) = @_;
580 0         0 my $xfer = 0;
581 0         0 $xfer += $self->writeJSONArrayStart();
582 0         0 $xfer += $self->writeJSONString(getTypeNameForTypeID($keyType));
583 0         0 $xfer += $self->writeJSONString(getTypeNameForTypeID($valType));
584 0         0 $xfer += $self->writeJSONInteger($size);
585 0         0 $xfer += $self->writeJSONObjectStart();
586 0         0 return $xfer;
587             }
588              
589             sub readMapBegin {
590 0     0 0 0 my ($self, $keyType, $valType, $size) = @_;
591 0         0 my $xfer = 0;
592 0         0 $xfer += $self->readJSONArrayStart();
593              
594 0         0 $xfer += $self->readJSONString($keyType);
595 0         0 $$keyType = getTypeIDForTypeName($$keyType);
596              
597 0         0 $xfer += $self->readJSONString($valType);
598 0         0 $$valType = getTypeIDForTypeName($$valType);
599              
600 0         0 $xfer += $self->readJSONInteger($size);
601 0         0 $xfer += $self->readJSONObjectStart();
602 0         0 return $xfer;
603             }
604              
605             sub writeMapEnd {
606 0     0 0 0 my ($self) = @_;
607 0         0 return $self->writeJSONObjectEnd() + $self->writeJSONArrayEnd();
608             }
609              
610             sub readMapEnd {
611 0     0 0 0 my ($self) = @_;
612 0         0 return $self->readJSONObjectEnd() + $self->readJSONArrayEnd();
613             }
614              
615             sub writeListBegin {
616 0     0 0 0 my ($self, $elemType, $size) = @_;
617 0         0 my $xfer = 0;
618 0         0 $xfer += $self->writeJSONArrayStart();
619 0         0 $xfer += $self->writeJSONString(getTypeNameForTypeID($elemType));
620 0         0 $xfer += $self->writeJSONInteger($size);
621 0         0 return $xfer;
622             }
623              
624             sub readListBegin {
625 0     0 0 0 my ($self, $elemType, $size) = @_;
626 0         0 my $xfer = 0;
627              
628 0         0 $xfer += $self->readJSONArrayStart();
629              
630 0         0 $xfer += $self->readJSONString($elemType);
631 0         0 $$elemType = getTypeIDForTypeName($$elemType);
632              
633 0         0 $xfer += $self->readJSONInteger($size);
634              
635 0         0 return $xfer;
636             }
637              
638             sub writeListEnd {
639 0     0 0 0 my ($self) = @_;
640 0         0 $self->writeJSONArrayEnd();
641             }
642              
643             sub readListEnd {
644 0     0 0 0 my ($self) = @_;
645 0         0 $self->readJSONArrayEnd();
646             }
647              
648             sub writeSetBegin {
649 0     0 0 0 my $self = shift;
650 0         0 $self->writeListBegin(@_);
651             }
652              
653             sub readSetBegin {
654 0     0 0 0 my $self = shift;
655 0         0 $self->readListBegin(@_);
656             }
657              
658             sub writeSetEnd {
659 0     0 0 0 my ($self) = @_;
660 0         0 $self->writeListEnd();
661             }
662              
663             sub readSetEnd {
664 0     0 0 0 my ($self) = @_;
665 0         0 $self->readListEnd();
666             }
667              
668             sub writeBool {
669 0     0 0 0 my ($self, $b) = @_;
670 0 0       0 $self->writeJSONInteger($b ? 1 : 0);
671             }
672              
673             sub readBool {
674 0     0 0 0 my ($self, $b) = @_;
675 0         0 my $xfer = $self->readJSONInteger($b);
676 0 0       0 $$b = $$b ? 1 : 0;
677 0         0 return $xfer;
678             }
679              
680             sub writeByte {
681 0     0 0 0 my ($self, $b) = @_;
682 0         0 $self->writeJSONInteger(ord($b));
683             }
684              
685             sub readByte {
686 0     0 0 0 my ($self, $b) = @_;
687 0         0 my $xfer = $self->readJSONInteger($b);
688 0         0 $$b = chr($$b);
689 0         0 return $xfer;
690             }
691              
692             sub writeI16 {
693 0     0 0 0 my ($self, $i16) = @_;
694 0         0 $self->writeJSONInteger($i16);
695             }
696              
697             sub readI16 {
698 0     0 0 0 my ($self, $i16) = @_;
699 0         0 $self->readJSONInteger($i16);
700             }
701              
702             sub writeI32 {
703 2     2 0 256 my ($self, $i32) = @_;
704 2         12 $self->writeJSONInteger($i32);
705             }
706              
707             sub readI32 {
708 0     0 0 0 my ($self, $i32) = @_;
709 0         0 $self->readJSONInteger($i32);
710             }
711              
712             sub writeI64 {
713 0     0 0 0 my ($self, $i64) = @_;
714 0         0 $self->writeJSONInteger($i64);
715             }
716              
717             sub readI64 {
718 0     0 0 0 my ($self, $i64) = @_;
719 0         0 $self->readJSONInteger($i64);
720             }
721              
722             sub writeDouble {
723 0     0 0 0 my ($self, $dub) = @_;
724 0         0 $self->writeJSONDouble($dub);
725             }
726              
727             sub readDouble {
728 0     0 0 0 my ($self, $dub) = @_;
729 0         0 $self->readJSONDouble($dub);
730             }
731              
732             sub writeString {
733 0     0 0 0 my ($self, $str) = @_;
734 0         0 check_utf8(\$str);
735 0         0 $self->writeJSONString($str);
736             }
737              
738             sub readString {
739 0     0 0 0 my ($self, $str) = @_;
740 0         0 $self->readJSONString($str);
741             }
742              
743             sub writeBinary {
744 0     0 0 0 my ($self, $str) = @_;
745 0         0 $self->writeJSONBase64($str);
746             }
747              
748             sub readBinary {
749 0     0 0 0 my ($self, $str) = @_;
750 0         0 $self->readJSONBase64($str);
751             }
752             #
753             # Other related packages
754             #
755              
756             {
757             package Thrift::JSONProtocolFactory;
758              
759 1     1   17 use strict;
  1         3  
  1         50  
760 1     1   7 use warnings;
  1         11  
  1         46  
761 1     1   5 use base qw(TProtocolFactory);
  1         4  
  1         1057  
762              
763             sub getProtocol {
764 0     0   0 my ($self, $transport) = @_;
765 0         0 return Thrift::JSONProtocol->new($transport);
766             }
767             }
768              
769             {
770             # Base class for tracking JSON contexts that may require inserting/reading
771             # additional JSON syntax characters
772             # This base context does nothing.
773             package Thrift::JSONProtocol::JSONBaseContext;
774              
775 1     1   9 use strict;
  1         2  
  1         32  
776 1     1   6 use warnings;
  1         4  
  1         40  
777 1     1   6 use base qw(Class::Accessor);
  1         3  
  1         224  
778             BEGIN {
779 1     1   12 __PACKAGE__->mk_accessors(qw(protocol));
780             };
781              
782             sub new {
783 5     5   19 my ($class, %self) = @_;
784 5         24 return bless \%self, $class;
785             }
786              
787 1     1   20 sub write { 0 }
788 0     0   0 sub read { 0 }
789 3     3   31 sub escapeNum { 0 }
790             }
791              
792             {
793             # Context for JSON lists. Will insert/read commas before each item except
794             # for the first one
795             package Thrift::JSONProtocol::JSONListContext;
796              
797 1     1   6454 use strict;
  1         3  
  1         38  
798 1     1   5 use warnings;
  1         3  
  1         30  
799 1     1   5 use base qw(Thrift::JSONProtocol::JSONBaseContext);
  1         3  
  1         765  
800             BEGIN {
801 1     1   18 __PACKAGE__->mk_accessors(qw(first_));
802             };
803              
804             sub new {
805 1     1   3 my $class = shift;
806 1         8 my $self = $class->SUPER::new(@_);
807 1         6 $self->first_(1);
808 1         25 return $self;
809             }
810              
811             sub write {
812 5     5   111 my ($self) = @_;
813 5 100       14 if ($self->first_) {
814 1         20 $self->first_(0);
815 1         8 return 0;
816             }
817             else {
818 4         43 $self->protocol->transport->write(Thrift::JSONProtocol::COMMA);
819 4         24 return length Thrift::JSONProtocol::COMMA;
820             }
821             }
822              
823             sub read {
824 0     0   0 my ($self) = @_;
825 0 0       0 if ($self->first_) {
826 0         0 $self->first_(0);
827 0         0 return 0;
828             }
829             else {
830 0         0 $self->protocol->readJSONSyntaxChar(Thrift::JSONProtocol::COMMA);
831 0         0 return length Thrift::JSONProtocol::COMMA;
832             }
833             }
834             }
835              
836             {
837             # Context for JSON records. Will insert/read colons before the value portion
838             # of each record pair, and commas before each key except the first. In
839             # addition, will indicate that numbers in the key position need to be
840             # escaped in quotes (since JSON keys must be strings).
841             package Thrift::JSONProtocol::JSONPairContext;
842              
843 1     1   614 use strict;
  1         2  
  1         38  
844 1     1   5 use warnings;
  1         3  
  1         37  
845 1     1   7 use base qw(Thrift::JSONProtocol::JSONBaseContext);
  1         2  
  1         647  
846             BEGIN {
847 1     1   14 __PACKAGE__->mk_accessors(qw(first_ colon_));
848             };
849              
850             sub new {
851 3     3   7 my $class = shift;
852 3         16 my $self = $class->SUPER::new(@_);
853 3         11 $self->first_(1);
854 3         42 $self->colon_(1);
855 3         28 return $self;
856             }
857              
858             sub write {
859 8     8   81 my ($self) = @_;
860              
861 8 100       26 if ($self->first_) {
862 3         38 $self->first_(0);
863 3         31 $self->colon_(1);
864 3         23 return 0;
865             }
866             else {
867 5 100       49 my $string = $self->colon_ ? Thrift::JSONProtocol::COLON : Thrift::JSONProtocol::COMMA;
868 5         62 $self->protocol->transport->write($string);
869 5 100       37 $self->colon_($self->colon_ ? 0 : 1);
870 5         162 return length $string;
871             }
872             }
873              
874             sub read {
875 0     0   0 my ($self) = @_;
876              
877 0 0       0 if ($self->first_) {
878 0         0 $self->first_(0);
879 0         0 $self->colon_(1);
880 0         0 return 0;
881             }
882             else {
883 0 0       0 my $string = $self->colon_ ? Thrift::JSONProtocol::COLON : Thrift::JSONProtocol::COMMA;
884 0         0 $self->protocol->readJSONSyntaxChar($string);
885 0 0       0 $self->colon_($self->colon_ ? 0 : 1);
886 0         0 return length $string;
887             }
888             }
889              
890             sub escapeNum {
891 4     4   35 my ($self) = @_;
892 4         11 return $self->colon_;
893             }
894             }
895              
896             {
897             # Holds up to one byte from the transport
898             package Thrift::JSONProtocol::LookaheadReader;
899              
900 1     1   11380 use strict;
  1         3  
  1         43  
901 1     1   7 use warnings;
  1         2  
  1         58  
902 1     1   6 use base qw(Class::Accessor);
  1         2  
  1         451  
903             BEGIN {
904 1     1   11 __PACKAGE__->mk_accessors(qw(protocol hasData_ data_));
905             };
906              
907             sub new {
908 1     1   4 my ($class, %self) = @_;
909 1         7 return bless \%self, $class;
910             }
911              
912             # Return and consume the next byte to be read, either taking it from the
913             # data buffer if present or getting it from the transport otherwise.
914             sub read {
915 0     0     my ($self) = @_;
916 0 0         if ($self->hasData_) {
917 0           $self->hasData_(0);
918             }
919             else {
920 0           $self->data_( $self->protocol->transport->readAll(1) );
921             }
922 0           return $self->data_;
923             }
924              
925             # Return the next byte to be read without consuming, filling the data
926             # buffer if it has not been filled already.
927             sub peek {
928 0     0     my ($self) = @_;
929 0 0         if (! $self->hasData_) {
930 0           $self->data_( $self->protocol->transport->readAll(1) );
931             }
932 0           $self->hasData_(1);
933 0           return $self->data_;
934             }
935             }
936              
937             1;