1 /***************************************************************************/
5 /* Experimental Type 1 parser (body). */
7 /* Copyright 1996-2000 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
10 /* This file is part of the FreeType project, and may only be used, */
11 /* modified, and distributed under the terms of the FreeType project */
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13 /* this file you indicate that you have read the license and */
14 /* understand and accept it fully. */
16 /***************************************************************************/
19 /*************************************************************************/
21 /* The Type 1 parser is in charge of the following: */
23 /* - provide an implementation of a growing sequence of objects called */
24 /* a `Z1_Table' (used to build various tables needed by the loader). */
26 /* - opening .pfb and .pfa files to extract their top-level and private */
29 /* - read numbers, arrays & strings from any dictionary. */
31 /* See `z1load.c' to see how data is loaded from the font file. */
33 /*************************************************************************/
36 #include <freetype/internal/ftdebug.h>
37 #include <freetype/internal/ftcalc.h>
38 #include <freetype/internal/ftobjs.h>
39 #include <freetype/internal/ftstream.h>
40 #include <freetype/internal/t1errors.h>
43 #ifdef FT_FLAT_COMPILE
49 #include <type1z/z1parse.h>
54 #include <string.h> /* for strncmp() */
57 /*************************************************************************/
59 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
60 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
61 /* messages during execution. */
64 #define FT_COMPONENT trace_z1parse
67 /*************************************************************************/
68 /*************************************************************************/
69 /*************************************************************************/
71 /***** IMPLEMENTATION OF Z1_TABLE OBJECT *****/
73 /*************************************************************************/
74 /*************************************************************************/
75 /*************************************************************************/
78 /*************************************************************************/
84 /* Initialises a Z1_Table. */
87 /* table :: The address of the target table. */
90 /* count :: The table size = the maximum number of elements. */
92 /* memory :: The memory object to use for all subsequent */
96 /* FreeType error code. 0 means success. */
99 FT_Error
Z1_New_Table( Z1_Table
* table
,
106 table
->memory
= memory
;
107 if ( ALLOC_ARRAY( table
->elements
, count
, FT_Byte
* ) ||
108 ALLOC_ARRAY( table
->lengths
, count
, FT_Byte
* ) )
111 table
->max_elems
= count
;
112 table
->init
= 0xdeadbeef;
113 table
->num_elems
= 0;
120 FREE( table
->elements
);
127 void shift_elements( Z1_Table
* table
,
130 FT_Long delta
= table
->block
- old_base
;
131 FT_Byte
** offset
= table
->elements
;
132 FT_Byte
** limit
= offset
+ table
->max_elems
;
136 for ( ; offset
< limit
; offset
++ )
145 FT_Error
reallocate_t1_table( Z1_Table
* table
,
148 FT_Memory memory
= table
->memory
;
149 FT_Byte
* old_base
= table
->block
;
153 /* reallocate the base block */
154 if ( REALLOC( table
->block
, table
->capacity
, new_size
) )
157 table
->capacity
= new_size
;
159 /* shift all offsets if necessary */
161 shift_elements( table
, old_base
);
167 /*************************************************************************/
173 /* Adds an object to a Z1_Table, possibly growing its memory block. */
176 /* table :: The target table. */
179 /* index :: The index of the object in the table. */
181 /* object :: The address of the object to copy in memory. */
183 /* length :: The length in bytes of the source object. */
186 /* FreeType error code. 0 means success. An error is returned if a */
187 /* reallocation fails. */
190 FT_Error
Z1_Add_Table( Z1_Table
* table
,
195 if ( index
< 0 || index
> table
->max_elems
)
197 FT_ERROR(( "Z1_Add_Table: invalid index\n" ));
198 return T1_Err_Syntax_Error
;
201 /* grow the base block if needed */
202 if ( table
->cursor
+ length
> table
->capacity
)
205 FT_Int new_size
= table
->capacity
;
208 while ( new_size
< table
->cursor
+ length
)
211 error
= reallocate_t1_table( table
, new_size
);
216 /* add the object to the base block and adjust offset */
217 table
->elements
[index
] = table
->block
+ table
->cursor
;
218 table
->lengths
[index
] = length
;
219 MEM_Copy( table
->block
+ table
->cursor
, object
, length
);
221 table
->cursor
+= length
;
228 /*************************************************************************/
234 /* Finalizes a Z1_Table (i.e., reallocate it to its current cursor). */
237 /* table :: The target table. */
240 /* This function does NOT release the heap's memory block. It is up */
241 /* to the caller to clean it, or reference it in its own structures. */
244 void Z1_Done_Table( Z1_Table
* table
)
246 FT_Memory memory
= table
->memory
;
251 /* should never fail, as rec.cursor <= rec.size */
252 old_base
= table
->block
;
256 (void)REALLOC( table
->block
, table
->capacity
, table
->cursor
);
257 table
->capacity
= table
->cursor
;
259 if ( old_base
!= table
->block
)
260 shift_elements( table
, old_base
);
267 void Z1_Release_Table( Z1_Table
* table
)
269 FT_Memory memory
= table
->memory
;
272 if ( table
->init
== (FT_Long
)0xDEADBEEF )
274 FREE( table
->block
);
275 FREE( table
->elements
);
276 FREE( table
->lengths
);
282 /*************************************************************************/
283 /*************************************************************************/
284 /*************************************************************************/
286 /***** INPUT STREAM PARSER *****/
288 /*************************************************************************/
289 /*************************************************************************/
290 /*************************************************************************/
293 #define IS_Z1_WHITESPACE( c ) ( (c) == ' ' || (c) == '\t' )
294 #define IS_Z1_LINESPACE( c ) ( (c) == '\r' || (c) == '\n' )
296 #define IS_Z1_SPACE( c ) ( IS_Z1_WHITESPACE( c ) || IS_Z1_LINESPACE( c ) )
300 void Z1_Skip_Spaces( Z1_Parser
* parser
)
302 FT_Byte
* cur
= parser
->cursor
;
303 FT_Byte
* limit
= parser
->limit
;
306 while ( cur
< limit
)
311 if ( !IS_Z1_SPACE( c
) )
315 parser
->cursor
= cur
;
320 void Z1_ToToken( Z1_Parser
* parser
,
321 Z1_Token_Rec
* token
)
325 FT_Byte starter
, ender
;
329 token
->type
= t1_token_none
;
333 /* first of all, skip space */
334 Z1_Skip_Spaces( parser
);
336 cur
= parser
->cursor
;
337 limit
= parser
->limit
;
343 /************* check for strings ***********************/
345 token
->type
= t1_token_string
;
349 /************* check for programs/array ****************/
351 token
->type
= t1_token_array
;
355 /************* check for table/array ******************/
357 token
->type
= t1_token_array
;
364 while ( cur
< limit
)
366 if ( *cur
== starter
)
368 else if ( *cur
== ender
)
373 token
->limit
= cur
++;
381 /* **************** otherwise, it's any token **********/
383 token
->start
= cur
++;
384 token
->type
= t1_token_any
;
385 while ( cur
< limit
&& !IS_Z1_SPACE( *cur
) )
394 token
->type
= t1_token_none
;
397 parser
->cursor
= cur
;
403 void Z1_ToTokenArray( Z1_Parser
* parser
,
404 Z1_Token_Rec
* tokens
,
406 FT_Int
* pnum_tokens
)
413 Z1_ToToken( parser
, &master
);
414 if ( master
.type
== t1_token_array
)
416 FT_Byte
* old_cursor
= parser
->cursor
;
417 FT_Byte
* old_limit
= parser
->limit
;
418 Z1_Token_Rec
* cur
= tokens
;
419 Z1_Token_Rec
* limit
= cur
+ max_tokens
;
422 parser
->cursor
= master
.start
;
423 parser
->limit
= master
.limit
;
425 while ( parser
->cursor
< parser
->limit
)
430 Z1_ToToken( parser
, &token
);
440 *pnum_tokens
= cur
- tokens
;
442 parser
->cursor
= old_cursor
;
443 parser
->limit
= old_limit
;
449 FT_Long
t1_toint( FT_Byte
** cursor
,
453 FT_Byte
* cur
= *cursor
;
457 for ( ; cur
< limit
; cur
++ )
460 d
= (FT_Byte
)( c
- '0' );
475 d
= (FT_Byte
)( cur
[0] - '0' );
479 result
= result
* 10 + d
;
482 } while ( cur
< limit
);
494 FT_Long
t1_tofixed( FT_Byte
** cursor
,
498 FT_Byte
* cur
= *cursor
;
499 FT_Long num
, divider
, result
;
507 /* first of all, read the integer part */
508 result
= t1_toint( &cur
, limit
) << 16;
521 /* read decimal part, if any */
522 if ( *cur
== '.' && cur
+ 1 < limit
)
528 d
= (FT_Byte
)( *cur
- '0' );
532 if ( divider
< 10000000L )
544 /* read exponent, if any */
545 if ( cur
+ 1 < limit
&& ( *cur
== 'e' || *cur
== 'E' ) )
548 power_ten
+= t1_toint( &cur
, limit
);
552 /* raise to power of ten if needed */
553 while ( power_ten
> 0 )
555 result
= result
* 10;
560 while ( power_ten
< 0 )
562 result
= result
/ 10;
563 divider
= divider
* 10;
568 result
+= FT_DivFix( num
, divider
);
579 FT_Int
t1_tocoordarray( FT_Byte
** cursor
,
584 FT_Byte
* cur
= *cursor
;
592 /* check for the beginning of an array. If not, only one number will */
606 /* now, read the coordinates */
607 for ( ; cur
< limit
; )
609 /* skip whitespace in front of data */
613 if ( c
!= ' ' && c
!= '\t' )
621 if ( count
>= max_coords
|| c
== ender
)
624 coords
[count
] = (FT_Short
)( t1_tofixed( &cur
, limit
, 0 ) >> 16 );
638 FT_Int
t1_tofixedarray( FT_Byte
** cursor
,
644 FT_Byte
* cur
= *cursor
;
649 if ( cur
>= limit
) goto Exit
;
651 /* check for the beginning of an array. If not, only one number will */
665 /* now, read the values */
666 for ( ; cur
< limit
; )
668 /* skip whitespace in front of data */
672 if ( c
!= ' ' && c
!= '\t' )
680 if ( count
>= max_values
|| c
== ender
)
683 values
[count
] = t1_tofixed( &cur
, limit
, power_ten
);
699 FT_String
* t1_tostring( FT_Byte
** cursor
,
703 FT_Byte
* cur
= *cursor
;
710 /* XXX: some stupid fonts have a `Notice' or `Copyright' string */
711 /* that simply doesn't begin with an opening parenthesis, even */
712 /* though they have a closing one! E.g. "amuncial.pfb" */
714 /* We must deal with these ill-fated cases there. Note that */
715 /* these fonts didn't work with the old Type 1 driver as the */
716 /* notice/copyright was not recognized as a valid string token */
717 /* and made the old token parser commit errors. */
719 while ( cur
< limit
&& ( *cur
== ' ' || *cur
== '\t' ) )
721 if ( cur
+ 1 >= limit
)
725 cur
++; /* skip the opening parenthesis, if there is one */
730 /* then, count its length */
731 for ( ; cur
< limit
; cur
++ )
736 else if ( *cur
== ')' )
745 if ( cur
>= limit
|| ALLOC( result
, len
+ 1 ) )
748 /* now copy the string */
749 MEM_Copy( result
, *cursor
, len
);
759 int t1_tobool( FT_Byte
** cursor
,
762 FT_Byte
* cur
= *cursor
;
766 /* return 1 if we find `true', 0 otherwise */
767 if ( cur
+ 3 < limit
&&
776 else if ( cur
+ 4 < limit
&&
792 /* Load a simple field (i.e. non-table) into the current list of objects */
794 FT_Error
Z1_Load_Field( Z1_Parser
* parser
,
795 const Z1_Field_Rec
* field
,
808 Z1_ToToken( parser
, &token
);
817 if ( token
.type
== t1_token_array
)
819 /* if this is an array, and we have no blend, an error occurs */
820 if ( max_objects
== 0 )
827 for ( ; count
> 0; count
--, index
++ )
829 FT_Byte
* q
= (FT_Byte
*)objects
[index
] + field
->offset
;
833 switch ( field
->type
)
836 val
= t1_tobool( &cur
, limit
);
840 val
= t1_tofixed( &cur
, limit
, 3 );
843 case t1_field_integer
:
844 val
= t1_toint( &cur
, limit
);
847 switch ( field
->size
)
850 *(FT_Byte
*)q
= (FT_Byte
)val
;
854 *(FT_UShort
*)q
= (FT_UShort
)val
;
858 *(FT_UInt32
*)q
= (FT_UInt32
)val
;
861 default: /* for 64-bit systems */
866 case t1_field_string
:
868 FT_Memory memory
= parser
->memory
;
869 FT_UInt len
= limit
-cur
;
871 if ( ALLOC( string
, len
+ 1 ) )
874 MEM_Copy( string
, cur
, len
);
877 *(FT_String
**)q
= string
;
882 /* an error occured */
888 *pflags
|= 1L << field
->flag_bit
;
896 error
= T1_Err_Invalid_File_Format
;
901 #define T1_MAX_TABLE_ELEMENTS 32
905 FT_Error
Z1_Load_Field_Table( Z1_Parser
* parser
,
906 const Z1_Field_Rec
* field
,
911 Z1_Token_Rec elements
[T1_MAX_TABLE_ELEMENTS
];
917 Z1_Field_Rec fieldrec
= *(Z1_Field_Rec
*)field
;
920 Z1_ToTokenArray( parser
, elements
, 32, &num_elements
);
921 if ( num_elements
< 0 )
924 if ( num_elements
> T1_MAX_TABLE_ELEMENTS
)
925 num_elements
= T1_MAX_TABLE_ELEMENTS
;
927 old_cursor
= parser
->cursor
;
928 old_limit
= parser
->limit
;
930 /* we store the elements count */
931 *(FT_Byte
*)( (FT_Byte
*)objects
[0] + field
->count_offset
) = num_elements
;
933 /* we now load each element, adjusting the field.offset on each one */
935 for ( ; num_elements
> 0; num_elements
--, token
++ )
937 parser
->cursor
= token
->start
;
938 parser
->limit
= token
->limit
;
939 Z1_Load_Field( parser
, &fieldrec
, objects
, max_objects
, 0 );
940 fieldrec
.offset
+= fieldrec
.size
;
944 *pflags
|= 1L << field
->flag_bit
;
946 parser
->cursor
= old_cursor
;
947 parser
->limit
= old_limit
;
953 error
= T1_Err_Invalid_File_Format
;
959 FT_Long
Z1_ToInt ( Z1_Parser
* parser
)
961 return t1_toint( &parser
->cursor
, parser
->limit
);
966 FT_Long
Z1_ToFixed( Z1_Parser
* parser
,
969 return t1_tofixed( &parser
->cursor
, parser
->limit
, power_ten
);
974 FT_Int
Z1_ToCoordArray( Z1_Parser
* parser
,
978 return t1_tocoordarray( &parser
->cursor
, parser
->limit
,
979 max_coords
, coords
);
984 FT_Int
Z1_ToFixedArray( Z1_Parser
* parser
,
989 return t1_tofixedarray( &parser
->cursor
, parser
->limit
,
990 max_values
, values
, power_ten
);
997 FT_String
* Z1_ToString( Z1_Parser
* parser
)
999 return t1_tostring( &parser
->cursor
, parser
->limit
, parser
->memory
);
1004 FT_Bool
Z1_ToBool( Z1_Parser
* parser
)
1006 return t1_tobool( &parser
->cursor
, parser
->limit
);
1013 FT_Error
read_pfb_tag( FT_Stream stream
,
1020 if ( READ_UShort( *tag
) )
1023 if ( *tag
== 0x8001 || *tag
== 0x8002 )
1028 if ( READ_ULong( asize
) )
1031 /* swap between big and little endianness */
1032 *size
= ( ( asize
& 0xFF000000L
) >> 24 ) |
1033 ( ( asize
& 0x00FF0000L
) >> 8 ) |
1034 ( ( asize
& 0x0000FF00L
) << 8 ) |
1035 ( ( asize
& 0x000000FFL
) << 24 );
1044 FT_Error
Z1_New_Parser( Z1_Parser
* parser
,
1053 parser
->stream
= stream
;
1054 parser
->memory
= memory
;
1055 parser
->base_len
= 0;
1056 parser
->base_dict
= 0;
1057 parser
->private_len
= 0;
1058 parser
->private_dict
= 0;
1060 parser
->in_memory
= 0;
1061 parser
->single_block
= 0;
1066 /******************************************************************/
1068 /* Here a short summary of what is going on: */
1070 /* When creating a new Type 1 parser, we try to locate and load */
1071 /* the base dictionary if this is possible (i.e. for PFB */
1072 /* files). Otherwise, we load the whole font into memory. */
1074 /* When `loading' the base dictionary, we only setup pointers */
1075 /* in the case of a memory-based stream. Otherwise, we */
1076 /* allocate and load the base dictionary in it. */
1078 /* parser->in_pfb is set if we are in a binary (".pfb") font. */
1079 /* parser->in_memory is set if we have a memory stream. */
1082 /* try to compute the size of the base dictionary; */
1083 /* look for a Postscript binary file tag, i.e 0x8001 */
1084 if ( FILE_Seek( 0L ) )
1087 error
= read_pfb_tag( stream
, &tag
, &size
);
1091 if ( tag
!= 0x8001 )
1093 /* assume that this is a PFA file for now; an error will */
1094 /* be produced later when more things are checked */
1095 (void)FILE_Seek( 0L );
1096 size
= stream
->size
;
1101 /* now, try to load `size' bytes of the `base' dictionary we */
1102 /* found previously */
1104 /* if it is a memory-based resource, set up pointers */
1105 if ( !stream
->read
)
1107 parser
->base_dict
= (FT_Byte
*)stream
->base
+ stream
->pos
;
1108 parser
->base_len
= size
;
1109 parser
->in_memory
= 1;
1111 /* check that the `size' field is valid */
1112 if ( FILE_Skip( size
) )
1117 /* read segment in memory */
1118 if ( ALLOC( parser
->base_dict
, size
) ||
1119 FILE_Read( parser
->base_dict
, size
) )
1121 parser
->base_len
= size
;
1124 /* Now check font format; we must see `%!PS-AdobeFont-1' */
1125 /* or `%!FontType' */
1128 ( strncmp( (const char*)parser
->base_dict
,
1129 "%!PS-AdobeFont-1", 16 ) &&
1130 strncmp( (const char*)parser
->base_dict
,
1131 "%!FontType", 10 ) ) )
1133 FT_TRACE2(( "[not a Type1 font]\n" ));
1134 error
= FT_Err_Unknown_File_Format
;
1138 parser
->cursor
= parser
->base_dict
;
1139 parser
->limit
= parser
->cursor
+ parser
->base_len
;
1144 if ( error
&& !parser
->in_memory
)
1145 FREE( parser
->base_dict
);
1152 void Z1_Done_Parser( Z1_Parser
* parser
)
1154 FT_Memory memory
= parser
->memory
;
1157 /* always free the private dictionary */
1158 FREE( parser
->private_dict
);
1160 /* free the base dictionary only when we have a disk stream */
1161 if ( !parser
->in_memory
)
1162 FREE( parser
->base_dict
);
1166 /* return the value of an hexadecimal digit */
1168 int hexa_value( char c
)
1173 d
= (unsigned int)( c
- '0' );
1177 d
= (unsigned int)( c
- 'a' );
1179 return (int)( d
+ 10 );
1181 d
= (unsigned int)( c
- 'A' );
1183 return (int)( d
+ 10 );
1190 void Z1_Decrypt( FT_Byte
* buffer
,
1194 while ( length
> 0 )
1199 plain
= ( *buffer
^ ( seed
>> 8 ) );
1200 seed
= ( *buffer
+ seed
) * 52845 + 22719;
1208 FT_Error
Z1_Get_Private_Dict( Z1_Parser
* parser
)
1210 FT_Stream stream
= parser
->stream
;
1211 FT_Memory memory
= parser
->memory
;
1216 if ( parser
->in_pfb
)
1218 /* in the case of the PFB format, the private dictionary can be */
1219 /* made of several segments. We thus first read the number of */
1220 /* segments to compute the total size of the private dictionary */
1221 /* then re-read them into memory. */
1222 FT_Long start_pos
= FILE_Pos();
1227 parser
->private_len
= 0;
1230 error
= read_pfb_tag( stream
, &tag
, &size
);
1234 if ( tag
!= 0x8002 )
1237 parser
->private_len
+= size
;
1239 if ( FILE_Skip( size
) )
1243 /* Check that we have a private dictionary there */
1244 /* and allocate private dictionary buffer */
1245 if ( parser
->private_len
== 0 )
1247 FT_ERROR(( "Z1_Get_Private_Dict:" ));
1248 FT_ERROR(( " invalid private dictionary section\n" ));
1249 error
= T1_Err_Invalid_File_Format
;
1253 if ( FILE_Seek( start_pos
) ||
1254 ALLOC( parser
->private_dict
, parser
->private_len
) )
1257 parser
->private_len
= 0;
1260 error
= read_pfb_tag( stream
, &tag
, &size
);
1261 if ( error
|| tag
!= 0x8002 )
1267 if ( FILE_Read( parser
->private_dict
+ parser
->private_len
, size
) )
1270 parser
->private_len
+= size
;
1275 /* we have already `loaded' the whole PFA font file into memory; */
1276 /* if this is a memory resource, allocate a new block to hold */
1277 /* the private dict. Otherwise, simply overwrite into the base */
1278 /* dictionary block in the heap. */
1280 /* first of all, look at the `eexec' keyword */
1281 FT_Byte
* cur
= parser
->base_dict
;
1282 FT_Byte
* limit
= cur
+ parser
->base_len
;
1289 if ( c
== 'e' && cur
+ 9 < limit
) /* 9 = 5 letters for `eexec' + */
1290 /* newline + 4 chars */
1292 if ( cur
[1] == 'e' && cur
[2] == 'x' &&
1293 cur
[3] == 'e' && cur
[4] == 'c' )
1295 cur
+= 6; /* we skip the newling after the `eexec' */
1297 /* XXX: Some fonts use DOS-linefeeds, i.e. \r\n; we need to */
1298 /* skip the extra \n if we find it */
1299 if ( cur
[0] == '\n' )
1308 FT_ERROR(( "Z1_Get_Private_Dict:" ));
1309 FT_ERROR(( " could not find `eexec' keyword\n" ));
1310 error
= T1_Err_Invalid_File_Format
;
1315 /* now determine where to write the _encrypted_ binary private */
1316 /* dictionary. We overwrite the base dictionary for disk-based */
1317 /* resources and allocate a new block otherwise */
1319 size
= parser
->base_len
- ( cur
- parser
->base_dict
);
1321 if ( parser
->in_memory
)
1323 /* note that we allocate one more byte to put a terminating `0' */
1324 if ( ALLOC( parser
->private_dict
, size
+ 1 ) )
1326 parser
->private_len
= size
;
1330 parser
->single_block
= 1;
1331 parser
->private_dict
= parser
->base_dict
;
1332 parser
->private_len
= size
;
1333 parser
->base_dict
= 0;
1334 parser
->base_len
= 0;
1337 /* now determine whether the private dictionary is encoded in binary */
1338 /* or hexadecimal ASCII format -- decode it accordingly */
1340 /* we need to access the next 4 bytes (after the final \r following */
1341 /* the `eexec' keyword); if they all are hexadecimal digits, then */
1342 /* we have a case of ASCII storage */
1344 if ( ( hexa_value( cur
[0] ) | hexa_value( cur
[1] ) |
1345 hexa_value( cur
[2] ) | hexa_value( cur
[3] ) ) < 0 )
1347 /* binary encoding -- `simply' copy the private dict */
1348 MEM_Copy( parser
->private_dict
, cur
, size
);
1352 /* ASCII hexadecimal encoding */
1358 write
= parser
->private_dict
;
1361 for ( ;cur
< limit
; cur
++ )
1366 /* check for newline */
1367 if ( cur
[0] == '\r' || cur
[0] == '\n' )
1370 /* exit if we have a non-hexadecimal digit that isn't a newline */
1371 hex1
= hexa_value( cur
[0] );
1372 if ( hex1
< 0 || cur
+ 1 >= limit
)
1375 /* otherwise, store byte */
1376 *write
++ = ( hex1
<< 4 ) | hexa_value( cur
[1] );
1381 /* put a safeguard */
1382 parser
->private_len
= write
- parser
->private_dict
;
1387 /* we now decrypt the encoded binary private dictionary */
1388 Z1_Decrypt( parser
->private_dict
, parser
->private_len
, 55665 );
1389 parser
->cursor
= parser
->private_dict
;
1390 parser
->limit
= parser
->cursor
+ parser
->private_len
;