TypedData C-API conversion
[kcar.git] / ext / kcar / kcar.rl
blob4c66fb50c0db0c96293de93d3162fc6d0725e509
1 /**
2  * Copyright (c) 2009, 2010 Eric Wong (all bugs are Eric's fault)
3  * Copyright (c) 2005 Zed A. Shaw
4  * You can redistribute it and/or modify it under the same terms as Ruby 1.8
5  * or the GPLv2 or later.
6  */
7 #include "ruby.h"
8 #include "ext_help.h"
9 #include <assert.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/types.h>
13 #include "c_util.h"
15 static VALUE eParserError;
16 static ID id_sq, id_sq_set;
18 /** Defines common length and error messages for input length validation. */
19 #define DEF_MAX_LENGTH(N, length) \
20   static const size_t MAX_##N##_LENGTH = length; \
21   static const char MAX_##N##_LENGTH_ERR[] = \
22     "HTTP element " # N  " is longer than the " # length " allowed length."
24 /**
25  * Validates the max length of given input and throws an ParserError
26  * exception if over.
27  */
28 #define VALIDATE_MAX_LENGTH(len, N) do { \
29   if (len > MAX_##N##_LENGTH) \
30     rb_raise(eParserError, MAX_##N##_LENGTH_ERR); \
31 } while (0)
33 /* Defines the maximum allowed lengths for various input elements.*/
34 DEF_MAX_LENGTH(FIELD_NAME, 256);
35 DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
36 DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
38 #define UH_FL_CHUNKED  0x1
39 #define UH_FL_HASBODY  0x2
40 #define UH_FL_INBODY   0x4
41 #define UH_FL_HASTRAILER   0x8
42 #define UH_FL_INTRAILER 0x10
43 #define UH_FL_INCHUNK  0x20
44 #define UH_FL_KEEPALIVE 0x40
45 #define UH_FL_HASHEADER 0x80
47 struct http_parser {
48   int cs; /* Ragel internal state */
49   unsigned int flags;
50   size_t mark;
51   size_t offset;
52   union { /* these 2 fields don't nest */
53     size_t field;
54     size_t query;
55   } start;
56   union {
57     size_t field_len; /* only used during header processing */
58     size_t dest_offset; /* only used during body processing */
59   } s;
60   VALUE cont; /* Qfalse: unset, Qnil: ignored header, T_STRING: append */
61   VALUE status; /* String or Qnil */
62   union {
63     off_t content;
64     off_t chunk;
65   } len;
68 #define REMAINING (unsigned long)(pe - p)
69 #define LEN(AT, FPC) (FPC - buffer - hp->AT)
70 #define MARK(M,FPC) (hp->M = (FPC) - buffer)
71 #define PTR_TO(F) (buffer + hp->F)
72 #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
73 #define STRIPPED_STR_NEW(M,FPC) stripped_str_new(PTR_TO(M), LEN(M, FPC))
75 #define HP_FL_TEST(hp,fl) ((hp)->flags & (UH_FL_##fl))
76 #define HP_FL_SET(hp,fl) ((hp)->flags |= (UH_FL_##fl))
77 #define HP_FL_UNSET(hp,fl) ((hp)->flags &= ~(UH_FL_##fl))
78 #define HP_FL_ALL(hp,fl) (HP_FL_TEST(hp, fl) == (UH_FL_##fl))
80 static int is_lws(char c)
82   return (c == ' ' || c == '\t');
85 static VALUE stripped_str_new(const char *str, long len)
87   long end;
89   for (end = len - 1; end >= 0 && is_lws(str[end]); end--);
91   return rb_str_new(str, end + 1);
94 static void finalize_header(struct http_parser *hp)
96   if ((HP_FL_TEST(hp, HASTRAILER) && ! HP_FL_TEST(hp, CHUNKED)))
97     rb_raise(eParserError, "trailer but not chunked");
101  * handles values of the "Connection:" header, keepalive is implied
102  * for HTTP/1.1 but needs to be explicitly enabled with HTTP/1.0
103  * Additionally, we require GET/HEAD requests to support keepalive.
104  */
105 static void hp_keepalive_connection(struct http_parser *hp, VALUE val)
107   /* REQUEST_METHOD is always set before any headers */
108   if (STR_CSTR_CASE_EQ(val, "keep-alive")) {
109     /* basically have HTTP/1.0 masquerade as HTTP/1.1+ */
110     HP_FL_SET(hp, KEEPALIVE);
111   } else if (STR_CSTR_CASE_EQ(val, "close")) {
112     /*
113      * it doesn't matter what HTTP version or request method we have,
114      * if a server says "Connection: close", we disable keepalive
115      */
116     HP_FL_UNSET(hp, KEEPALIVE);
117   } else {
118     /*
119      * server could've sent anything, ignore it for now.  Maybe
120      * "HP_FL_UNSET(hp, KEEPALIVE);" just in case?
121      * Raising an exception might be too mean...
122      */
123   }
126 static void
127 http_version(struct http_parser *hp, VALUE hdr, const char *ptr, size_t len)
129   if (CONST_MEM_EQ("HTTP/1.1", ptr, len)) {
130     /* HTTP/1.1 implies keepalive unless "Connection: close" is set */
131     HP_FL_SET(hp, KEEPALIVE);
132   }
135 static void
136 status_phrase(struct http_parser *hp, VALUE hdr, const char *ptr, size_t len)
138   long nr;
140   hp->status = rb_str_new(ptr, len);
142   /* RSTRING_PTR is null terminated, ptr is not */
143   nr = strtol(RSTRING_PTR(hp->status), NULL, 10);
145   if (nr < 100 || nr > 999)
146     rb_raise(eParserError, "invalid status: %s", RSTRING_PTR(hp->status));
148   if ( !((nr >= 100 && nr <= 199) || nr == 204 || nr == 304) )
149     HP_FL_SET(hp, HASBODY);
152 static inline void invalid_if_trailer(struct http_parser *hp)
154   if (HP_FL_TEST(hp, INTRAILER))
155     rb_raise(eParserError, "invalid Trailer");
158 static void write_cont_value(struct http_parser *hp,
159                              char *buffer, const char *p)
161   char *vptr;
162   long end;
163   long len = LEN(mark, p);
164   long cont_len;
166   if (hp->cont == Qfalse)
167     rb_raise(eParserError, "invalid continuation line");
169   if (NIL_P(hp->cont))
170     return; /* we're ignoring this header (probably Status:) */
172   assert(TYPE(hp->cont) == T_STRING && "continuation line is not a string");
173   assert(hp->mark > 0 && "impossible continuation line offset");
175   if (len == 0)
176     return;
178   cont_len = RSTRING_LEN(hp->cont);
179   if (cont_len > 0) {
180     --hp->mark;
181     len = LEN(mark, p);
182   }
184   vptr = PTR_TO(mark);
186   /* normalize tab to space */
187   if (cont_len > 0) {
188     assert((' ' == *vptr || '\t' == *vptr) && "invalid leading white space");
189     *vptr = ' ';
190   }
191   for (end = len - 1; end >= 0 && is_lws(vptr[end]); end--);
192   rb_str_buf_cat(hp->cont, vptr, end + 1);
195 static void write_value(VALUE hdr, struct http_parser *hp,
196                         const char *buffer, const char *p)
198   VALUE f, v;
199   VALUE hclass;
200   const char *fptr = PTR_TO(start.field);
201   size_t flen = hp->s.field_len;
202   const char *vptr;
203   size_t vlen;
205   HP_FL_SET(hp, HASHEADER);
207   /* Rack does not like Status headers, so we never send them */
208   if (CSTR_CASE_EQ(fptr, flen, "status")) {
209     hp->cont = Qnil;
210     return;
211   }
213   vptr = PTR_TO(mark);
214   vlen = LEN(mark, p);
215   VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
216   VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
217   f = rb_str_new(fptr, (long)flen);
218   v = stripped_str_new(vptr, (long)vlen);
220   /* needs more tests for error-checking here */
221   /*
222    * TODO:
223    * some of these tests might be too strict for real-world HTTP servers,
224    * report real-world examples as we find them:
225    */
226   if (STR_CSTR_CASE_EQ(f, "connection")) {
227     hp_keepalive_connection(hp, v);
228   } else if (STR_CSTR_CASE_EQ(f, "content-length")) {
229     if (! HP_FL_TEST(hp, HASBODY))
230       rb_raise(eParserError, "Content-Length with no body expected");
231     if (HP_FL_TEST(hp, CHUNKED))
232       rb_raise(eParserError,
233                "Content-Length when chunked Transfer-Encoding is set");
234     hp->len.content = parse_length(vptr, vlen);
236     if (hp->len.content < 0)
237       rb_raise(eParserError, "invalid Content-Length");
239     invalid_if_trailer(hp);
240   } else if (STR_CSTR_CASE_EQ(f, "transfer-encoding")) {
241     if (STR_CSTR_CASE_EQ(v, "chunked")) {
242       if (! HP_FL_TEST(hp, HASBODY))
243         rb_raise(eParserError,
244                  "chunked Transfer-Encoding with no body expected");
245       if (hp->len.content >= 0)
246         rb_raise(eParserError,
247                  "chunked Transfer-Encoding when Content-Length is set");
249       hp->len.chunk = 0;
250       HP_FL_SET(hp, CHUNKED);
251     }
252     invalid_if_trailer(hp);
253   } else if (STR_CSTR_CASE_EQ(f, "trailer")) {
254     if (! HP_FL_TEST(hp, HASBODY))
255       rb_raise(eParserError, "trailer with no body");
256     HP_FL_SET(hp, HASTRAILER);
257     invalid_if_trailer(hp);
258   }
260   hclass = CLASS_OF(hdr);
261   if (hclass == rb_cArray) {
262     rb_ary_push(hdr, rb_ary_new3(2, f, v));
263     hp->cont = v;
264   } else {
265     /* hash-ish, try rb_hash_* first and fall back to slow rb_funcall */
266     VALUE e;
268     /* try to read the existing value */
269     if (hclass == rb_cHash)
270       e = rb_hash_aref(hdr, f);
271     else
272       e = rb_funcall(hdr, id_sq, 1, f);
274     if (NIL_P(e)) {
275       /* new value, freeze it since it speeds up MRI slightly */
276       OBJ_FREEZE(f);
278       if (hclass == rb_cHash)
279         rb_hash_aset(hdr, f, v);
280       else
281         rb_funcall(hdr, id_sq_set, 2, f, v);
283       hp->cont = v;
284     } else {
285       /*
286        * existing value, append to it, Rack 1.x uses newlines to represent
287        * repeated cookies:
288        *    { 'Set-Cookie' => "a=b\nc=d" }
289        * becomes:
290        *    "Set-Cookie: a=b\r\nSet-Cookie: c=d\r\n"
291        */
292       rb_str_buf_cat(e, "\n", 1);
293       hp->cont = rb_str_buf_append(e, v);
294     }
295   }
298 /** Machine **/
301   machine http_parser;
303   action mark {MARK(mark, fpc); }
305   action start_field { MARK(start.field, fpc); }
306   action write_field { hp->s.field_len = LEN(start.field, fpc); }
307   action start_value { MARK(mark, fpc); }
308   action write_value { write_value(hdr, hp, buffer, fpc); }
309   action write_cont_value { write_cont_value(hp, buffer, fpc); }
310   action http_version { http_version(hp, hdr, PTR_TO(mark), LEN(mark, fpc)); }
311   action status_phrase { status_phrase(hp, hdr, PTR_TO(mark), LEN(mark, fpc)); }
313   action add_to_chunk_size {
314     hp->len.chunk = step_incr(hp->len.chunk, fc, 16);
315     if (hp->len.chunk < 0)
316       rb_raise(eParserError, "invalid chunk size");
317   }
318   action header_done {
319     finalize_header(hp);
320     cs = http_parser_first_final;
322     if (HP_FL_TEST(hp, CHUNKED))
323       cs = http_parser_en_ChunkedBody;
325     /*
326      * go back to Ruby so we can call the Rack application, we'll reenter
327      * the parser iff the body needs to be processed.
328      */
329     goto post_exec;
330   }
332   action end_trailers {
333     cs = http_parser_first_final;
334     goto post_exec;
335   }
337   action end_chunked_body {
338     HP_FL_SET(hp, INTRAILER);
339     cs = http_parser_en_Trailers;
340     ++p;
341     assert(p <= pe && "buffer overflow after chunked body");
342     goto post_exec;
343   }
345   action skip_chunk_data {
346   skip_chunk_data_hack: {
347     size_t nr = MIN((size_t)hp->len.chunk, REMAINING);
348     memcpy(RSTRING_PTR(hdr) + hp->s.dest_offset, fpc, nr);
349     hp->s.dest_offset += nr;
350     hp->len.chunk -= nr;
351     p += nr;
352     assert(hp->len.chunk >= 0 && "negative chunk length");
353     if ((size_t)hp->len.chunk > REMAINING) {
354       HP_FL_SET(hp, INCHUNK);
355       goto post_exec;
356     } else {
357       fhold;
358       fgoto chunk_end;
359     }
360   }}
362   include kcar_http_common "kcar_http_common.rl";
365 /** Data **/
366 %% write data;
368 static void http_parser_init(struct http_parser *hp)
370   int cs = 0;
371   memset(hp, 0, sizeof(struct http_parser));
372   hp->cont = Qfalse; /* zero on MRI, should be optimized away by above */
373   hp->status = Qnil;
374   hp->len.content = -1;
375   %% write init;
376   hp->cs = cs;
379 /** exec **/
380 static void http_parser_execute(struct http_parser *hp,
381   VALUE hdr, char *buffer, size_t len)
383   const char *p, *pe;
384   int cs = hp->cs;
385   size_t off = hp->offset;
387   if (cs == http_parser_first_final)
388     return;
390   assert(off <= len && "offset past end of buffer");
392   p = buffer+off;
393   pe = buffer+len;
395   assert((void *)(pe - p) == (void *)(len - off) &&
396          "pointers aren't same distance");
398   if (HP_FL_TEST(hp, INCHUNK)) {
399     HP_FL_UNSET(hp, INCHUNK);
400     goto skip_chunk_data_hack;
401   }
402   %% write exec;
403 post_exec: /* "_out:" also goes here */
404   if (hp->cs != http_parser_error)
405     hp->cs = cs;
406   hp->offset = p - buffer;
408   assert(p <= pe && "buffer overflow after parsing execute");
409   assert(hp->offset <= len && "offset longer than length");
412 static void kcar_mark(void *ptr)
414   struct http_parser *hp = ptr;
416   rb_gc_mark(hp->cont);
417   rb_gc_mark(hp->status);
420 static size_t kcar_memsize(const void *ptr)
422   return sizeof(struct http_parser);
425 static const rb_data_type_t kcar_type = {
426   "kcar_parser",
427   { kcar_mark, RUBY_TYPED_DEFAULT_FREE, kcar_memsize, /* reserved */ },
428   /* parent, data, [ flags ] */
431 static VALUE kcar_alloc(VALUE klass)
433   struct http_parser *hp;
434   return TypedData_Make_Struct(klass, struct http_parser, &kcar_type, hp);
437 static struct http_parser *data_get(VALUE self)
439   struct http_parser *hp;
441   TypedData_Get_Struct(self, struct http_parser, &kcar_type, hp);
442   assert(hp && "failed to extract http_parser struct");
443   return hp;
448  * call-seq:
449  *    Kcar::Parser.new => parser
451  * Creates a new parser.
453  * Document-method: reset
455  * call-seq:
456  *    parser.reset => parser
458  * Resets the parser so it can be reused by another client
459  */
460 static VALUE initialize(VALUE self)
462   http_parser_init(data_get(self));
464   return self;
467 static void advance_str(VALUE str, off_t nr)
469   long len = RSTRING_LEN(str);
471   if (len == 0)
472     return;
474   rb_str_modify(str);
476   assert(nr <= len && "trying to advance past end of buffer");
477   len -= nr;
478   if (len > 0) /* unlikely, len is usually 0 */
479     memmove(RSTRING_PTR(str), RSTRING_PTR(str) + nr, len);
480   rb_str_set_len(str, len);
484  * call-seq:
485  *   parser.body_bytes_left => nil or Integer
487  * Returns the number of bytes left to run through Parser#filter_body.
488  * This will initially be the value of the "Content-Length" HTTP header
489  * after header parsing is complete and will decrease in value as
490  * Parser#filter_body is called for each chunk.  This should return
491  * zero for responses with no body.
493  * This will return nil on "Transfer-Encoding: chunked" responses as
494  * well as HTTP/1.0 responses where Content-Length is not set
495  */
496 static VALUE body_bytes_left(VALUE self)
498   struct http_parser *hp = data_get(self);
500   if (HP_FL_TEST(hp, CHUNKED))
501     return Qnil;
502   if (hp->len.content >= 0)
503     return OFFT2NUM(hp->len.content);
505   return Qnil;
509  * call-seq:
510  *   parser.body_bytes_left = Integer
512  * Sets the number of bytes left to download for HTTP responses
513  * with "Content-Length".  This raises RuntimeError for chunked
514  * responses.
515  */
516 static VALUE body_bytes_left_set(VALUE self, VALUE bytes)
518   struct http_parser *hp = data_get(self);
520   if (HP_FL_TEST(hp, CHUNKED))
521     rb_raise(rb_eRuntimeError, "body_bytes_left= is not for chunked bodies");
522   hp->len.content = NUM2OFFT(bytes);
523   return bytes;
527  * Document-method: chunked
528  * call-seq:
529  *    parser.chunked? => true or false
531  * This is used to detect if a response uses chunked Transfer-Encoding or not.
532  */
533 static VALUE chunked(VALUE self)
535   struct http_parser *hp = data_get(self);
537   return HP_FL_TEST(hp, CHUNKED) ? Qtrue : Qfalse;
541  * Document-method: headers
542  * call-seq:
543  *    parser.headers(hdr, data) => hdr or nil
545  * Takes a Hash and a String of data, parses the String of data filling
546  * in the Hash returning the Hash if parsing is finished, nil otherwise
547  * When returning the hdr Hash, it may modify data to point to where
548  * body processing should begin.
550  * Raises ParserError if there are parsing errors.
551  */
552 static VALUE headers(VALUE self, VALUE hdr, VALUE data)
554   struct http_parser *hp = data_get(self);
556   http_parser_execute(hp, hdr, RSTRING_PTR(data), RSTRING_LEN(data));
557   VALIDATE_MAX_LENGTH(hp->offset, HEADER);
559   if (hp->cs == http_parser_first_final ||
560       hp->cs == http_parser_en_ChunkedBody) {
561     advance_str(data, hp->offset + 1);
562     hp->offset = 0;
563     if (HP_FL_TEST(hp, INTRAILER))
564       return hdr;
565     else
566       return rb_ary_new3(2, hp->status, hdr);
567   }
569   if (hp->cs == http_parser_error)
570     rb_raise(eParserError, "Invalid HTTP format, parsing fails.");
572   return Qnil;
575 static int chunked_eof(struct http_parser *hp)
577   return ((hp->cs == http_parser_first_final) || HP_FL_TEST(hp, INTRAILER));
581  * call-seq:
582  *    parser.body_eof? => true or false
584  * Detects if we're done filtering the body or not.  This can be used
585  * to detect when to stop calling Parser#filter_body.
586  */
587 static VALUE body_eof(VALUE self)
589   struct http_parser *hp = data_get(self);
591   if (!HP_FL_TEST(hp, HASHEADER) && HP_FL_ALL(hp, KEEPALIVE))
592     return Qtrue;
594   if (HP_FL_TEST(hp, CHUNKED))
595     return chunked_eof(hp) ? Qtrue : Qfalse;
597   if (! HP_FL_TEST(hp, HASBODY))
598     return Qtrue;
600   return hp->len.content == 0 ? Qtrue : Qfalse;
604  * call-seq:
605  *    parser.keepalive? => true or false
607  * This should be used to detect if a request can really handle
608  * keepalives and pipelining.  Currently, the rules are:
610  * 1. MUST be HTTP/1.1 +or+ HTTP/1.0 with "Connection: keep-alive"
611  * 2. MUST NOT have "Connection: close" set
612  * 3. If there is a response body, either a) Content-Length is set
613  *    or b) chunked encoding is used
614  */
615 static VALUE keepalive(VALUE self)
617   struct http_parser *hp = data_get(self);
619   if (HP_FL_ALL(hp, KEEPALIVE)) {
620     if (HP_FL_TEST(hp, HASHEADER) && HP_FL_TEST(hp, HASBODY) ) {
621       if (HP_FL_TEST(hp, CHUNKED) || (hp->len.content >= 0))
622         return Qtrue;
624       /* unknown Content-Length and not chunked, we must assume close */
625       return Qfalse;
626     } else {
627       /* 100 Continue, 304 Not Modified, etc... */
628       return Qtrue;
629     }
630   }
631   return Qfalse;
635  * call-seq:
636  *    parser.filter_body(buf, data) => nil/data
638  * Takes a String of +data+, will modify data if dechunking is done.
639  * Returns +nil+ if there is more data left to process.  Returns
640  * +data+ if body processing is complete. When returning +data+,
641  * it may modify +data+ so the start of the string points to where
642  * the body ended so that trailer processing can begin.
644  * Raises ParserError if there are dechunking errors.
645  * Basically this is a glorified memcpy(3) that copies +data+
646  * into +buf+ while filtering it through the dechunker.
647  */
648 static VALUE filter_body(VALUE self, VALUE buf, VALUE data)
650   struct http_parser *hp = data_get(self);
651   char *dptr;
652   long dlen;
654   dptr = RSTRING_PTR(data);
655   dlen = RSTRING_LEN(data);
657   StringValue(buf);
658   rb_str_modify(buf);
659   rb_str_resize(buf, dlen); /* we can never copy more than dlen bytes */
660   OBJ_TAINT(buf); /* keep weirdo $SAFE users happy */
662   if (!HP_FL_TEST(hp, CHUNKED))
663     rb_raise(rb_eRuntimeError, "filter_body is only for chunked bodies");
665   if (!chunked_eof(hp)) {
666     hp->s.dest_offset = 0;
667     http_parser_execute(hp, buf, dptr, dlen);
668     if (hp->cs == http_parser_error)
669       rb_raise(eParserError, "Invalid HTTP format, parsing fails.");
671     assert(hp->s.dest_offset <= hp->offset &&
672            "destination buffer overflow");
673     advance_str(data, hp->offset);
674     rb_str_set_len(buf, hp->s.dest_offset);
676     if (RSTRING_LEN(buf) == 0 && chunked_eof(hp)) {
677       assert(hp->len.chunk == 0 && "chunk at EOF but more to parse");
678     } else {
679       data = Qnil;
680     }
681   }
682   hp->offset = 0; /* for trailer parsing */
683   return data;
686 void Init_kcar_ext(void)
688   VALUE mKcar = rb_define_module("Kcar");
689   VALUE cParser = rb_define_class_under(mKcar, "Parser", rb_cObject);
691   eParserError = rb_define_class_under(mKcar, "ParserError", rb_eIOError);
693   rb_define_alloc_func(cParser, kcar_alloc);
694   rb_define_method(cParser, "initialize", initialize, 0);
695   rb_define_method(cParser, "reset", initialize, 0);
696   rb_define_method(cParser, "headers", headers, 2);
697   rb_define_method(cParser, "trailers", headers, 2);
698   rb_define_method(cParser, "filter_body", filter_body, 2);
699   rb_define_method(cParser, "body_bytes_left", body_bytes_left, 0);
700   rb_define_method(cParser, "body_bytes_left=", body_bytes_left_set, 1);
701   rb_define_method(cParser, "body_eof?", body_eof, 0);
702   rb_define_method(cParser, "keepalive?", keepalive, 0);
703   rb_define_method(cParser, "chunked?", chunked, 0);
705   /*
706    * The maximum size a single chunk when using chunked transfer encoding.
707    * This is only a theoretical maximum used to detect errors in clients,
708    * it is highly unlikely to encounter clients that send more than
709    * several kilobytes at once.
710    */
711   rb_define_const(cParser, "CHUNK_MAX", OFFT2NUM(UH_OFF_T_MAX));
713   /*
714    * The maximum size of the body as specified by Content-Length.
715    * This is only a theoretical maximum, the actual limit is subject
716    * to the limits of the file system used for +Dir.tmpdir+.
717    */
718   rb_define_const(cParser, "LENGTH_MAX", OFFT2NUM(UH_OFF_T_MAX));
719   id_sq = rb_intern("[]");
720   id_sq_set = rb_intern("[]=");