blob: 4947e264e4fbc3168dc2bf0f2897aeb734014234 [file] [log] [blame]
Kevin Clark916f3532009-03-20 04:21:39 +00001/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
Bryan Duxburyc0166282009-02-02 00:48:17 +000020#include <struct.h>
21#include <constants.h>
22
Bryan Duxbury1e80d442009-02-03 18:16:54 +000023#ifndef HAVE_STRLCPY
24
25static
26size_t
27strlcpy (char *dst, const char *src, size_t dst_sz)
28{
29 size_t n;
30
31 for (n = 0; n < dst_sz; n++) {
32 if ((*dst++ = *src++) == '\0')
33 break;
34 }
35
36 if (n < dst_sz)
37 return n;
38 if (n > 0)
39 *(dst - 1) = '\0';
40 return n + strlen (src);
41}
42
43#endif
44
Bryan Duxburyc0166282009-02-02 00:48:17 +000045static native_proto_method_table *mt;
Bryan Duxburyd815c212009-03-19 18:57:43 +000046static native_proto_method_table *default_mt;
47static VALUE last_proto_class = Qnil;
Bryan Duxburyc0166282009-02-02 00:48:17 +000048
49#define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
50#define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
51
Bryan Duxburyd815c212009-03-19 18:57:43 +000052static void set_native_proto_function_pointers(VALUE protocol) {
53 VALUE method_table_object = rb_const_get(CLASS_OF(protocol), rb_intern("@native_method_table"));
54 // TODO: check nil?
55 Data_Get_Struct(method_table_object, native_proto_method_table, mt);
56}
57
58static void check_native_proto_method_table(VALUE protocol) {
59 VALUE protoclass = CLASS_OF(protocol);
60 if (protoclass != last_proto_class) {
61 last_proto_class = protoclass;
62 if (rb_funcall(protocol, native_qmark_method_id, 0) == Qtrue) {
63 set_native_proto_function_pointers(protocol);
64 } else {
65 mt = default_mt;
66 }
67 }
68}
69
Bryan Duxburyc0166282009-02-02 00:48:17 +000070//-------------------------------------------
71// Writing section
72//-------------------------------------------
73
74// default fn pointers for protocol stuff here
75
76VALUE default_write_bool(VALUE protocol, VALUE value) {
77 rb_funcall(protocol, write_boolean_method_id, 1, value);
78 return Qnil;
79}
80
81VALUE default_write_byte(VALUE protocol, VALUE value) {
82 rb_funcall(protocol, write_byte_method_id, 1, value);
83 return Qnil;
84}
85
86VALUE default_write_i16(VALUE protocol, VALUE value) {
87 rb_funcall(protocol, write_i16_method_id, 1, value);
88 return Qnil;
89}
90
91VALUE default_write_i32(VALUE protocol, VALUE value) {
92 rb_funcall(protocol, write_i32_method_id, 1, value);
93 return Qnil;
94}
95
96VALUE default_write_i64(VALUE protocol, VALUE value) {
97 rb_funcall(protocol, write_i64_method_id, 1, value);
98 return Qnil;
99}
100
101VALUE default_write_double(VALUE protocol, VALUE value) {
102 rb_funcall(protocol, write_double_method_id, 1, value);
103 return Qnil;
104}
105
106VALUE default_write_string(VALUE protocol, VALUE value) {
107 rb_funcall(protocol, write_string_method_id, 1, value);
108 return Qnil;
109}
110
111VALUE default_write_list_begin(VALUE protocol, VALUE etype, VALUE length) {
112 rb_funcall(protocol, write_list_begin_method_id, 2, etype, length);
113 return Qnil;
114}
115
116VALUE default_write_list_end(VALUE protocol) {
117 rb_funcall(protocol, write_list_end_method_id, 0);
118 return Qnil;
119}
120
121VALUE default_write_set_begin(VALUE protocol, VALUE etype, VALUE length) {
122 rb_funcall(protocol, write_set_begin_method_id, 2, etype, length);
123 return Qnil;
124}
125
126VALUE default_write_set_end(VALUE protocol) {
127 rb_funcall(protocol, write_set_end_method_id, 0);
128 return Qnil;
129}
130
131VALUE default_write_map_begin(VALUE protocol, VALUE ktype, VALUE vtype, VALUE length) {
132 rb_funcall(protocol, write_map_begin_method_id, 3, ktype, vtype, length);
133 return Qnil;
134}
135
136VALUE default_write_map_end(VALUE protocol) {
137 rb_funcall(protocol, write_map_end_method_id, 0);
138 return Qnil;
139}
140
141VALUE default_write_struct_begin(VALUE protocol, VALUE struct_name) {
142 rb_funcall(protocol, write_struct_begin_method_id, 1, struct_name);
143 return Qnil;
144}
145
146VALUE default_write_struct_end(VALUE protocol) {
147 rb_funcall(protocol, write_struct_end_method_id, 0);
148 return Qnil;
149}
150
151VALUE default_write_field_begin(VALUE protocol, VALUE name, VALUE type, VALUE id) {
152 rb_funcall(protocol, write_field_begin_method_id, 3, name, type, id);
153 return Qnil;
154}
155
156VALUE default_write_field_end(VALUE protocol) {
157 rb_funcall(protocol, write_field_end_method_id, 0);
158 return Qnil;
159}
160
161VALUE default_write_field_stop(VALUE protocol) {
162 rb_funcall(protocol, write_field_stop_method_id, 0);
163 return Qnil;
164}
165
166VALUE default_read_field_begin(VALUE protocol) {
167 return rb_funcall(protocol, read_field_begin_method_id, 0);
168}
169
170VALUE default_read_field_end(VALUE protocol) {
171 return rb_funcall(protocol, read_field_end_method_id, 0);
172}
173
174VALUE default_read_map_begin(VALUE protocol) {
175 return rb_funcall(protocol, read_map_begin_method_id, 0);
176}
177
178VALUE default_read_map_end(VALUE protocol) {
179 return rb_funcall(protocol, read_map_end_method_id, 0);
180}
181
182VALUE default_read_list_begin(VALUE protocol) {
183 return rb_funcall(protocol, read_list_begin_method_id, 0);
184}
185
186VALUE default_read_list_end(VALUE protocol) {
187 return rb_funcall(protocol, read_list_end_method_id, 0);
188}
189
190VALUE default_read_set_begin(VALUE protocol) {
191 return rb_funcall(protocol, read_set_begin_method_id, 0);
192}
193
194VALUE default_read_set_end(VALUE protocol) {
195 return rb_funcall(protocol, read_set_end_method_id, 0);
196}
197
198VALUE default_read_byte(VALUE protocol) {
199 return rb_funcall(protocol, read_byte_method_id, 0);
200}
201
202VALUE default_read_bool(VALUE protocol) {
203 return rb_funcall(protocol, read_bool_method_id, 0);
204}
205
206VALUE default_read_i16(VALUE protocol) {
207 return rb_funcall(protocol, read_i16_method_id, 0);
208}
209
210VALUE default_read_i32(VALUE protocol) {
211 return rb_funcall(protocol, read_i32_method_id, 0);
212}
213
214VALUE default_read_i64(VALUE protocol) {
215 return rb_funcall(protocol, read_i64_method_id, 0);
216}
217
218VALUE default_read_double(VALUE protocol) {
219 return rb_funcall(protocol, read_double_method_id, 0);
220}
221
222VALUE default_read_string(VALUE protocol) {
223 return rb_funcall(protocol, read_string_method_id, 0);
224}
225
226VALUE default_read_struct_begin(VALUE protocol) {
227 return rb_funcall(protocol, read_struct_begin_method_id, 0);
228}
229
230VALUE default_read_struct_end(VALUE protocol) {
231 return rb_funcall(protocol, read_struct_end_method_id, 0);
232}
233
234static void set_default_proto_function_pointers() {
Bryan Duxburyd815c212009-03-19 18:57:43 +0000235 default_mt = ALLOC(native_proto_method_table);
Bryan Duxburyc0166282009-02-02 00:48:17 +0000236
Bryan Duxburyd815c212009-03-19 18:57:43 +0000237 default_mt->write_field_begin = default_write_field_begin;
238 default_mt->write_field_stop = default_write_field_stop;
239 default_mt->write_map_begin = default_write_map_begin;
240 default_mt->write_map_end = default_write_map_end;
241 default_mt->write_list_begin = default_write_list_begin;
242 default_mt->write_list_end = default_write_list_end;
243 default_mt->write_set_begin = default_write_set_begin;
244 default_mt->write_set_end = default_write_set_end;
245 default_mt->write_byte = default_write_byte;
246 default_mt->write_bool = default_write_bool;
247 default_mt->write_i16 = default_write_i16;
248 default_mt->write_i32 = default_write_i32;
249 default_mt->write_i64 = default_write_i64;
250 default_mt->write_double = default_write_double;
251 default_mt->write_string = default_write_string;
252 default_mt->write_struct_begin = default_write_struct_begin;
253 default_mt->write_struct_end = default_write_struct_end;
254 default_mt->write_field_end = default_write_field_end;
Bryan Duxburyc0166282009-02-02 00:48:17 +0000255
Bryan Duxburyd815c212009-03-19 18:57:43 +0000256 default_mt->read_struct_begin = default_read_struct_begin;
257 default_mt->read_struct_end = default_read_struct_end;
258 default_mt->read_field_begin = default_read_field_begin;
259 default_mt->read_field_end = default_read_field_end;
260 default_mt->read_map_begin = default_read_map_begin;
261 default_mt->read_map_end = default_read_map_end;
262 default_mt->read_list_begin = default_read_list_begin;
263 default_mt->read_list_end = default_read_list_end;
264 default_mt->read_set_begin = default_read_set_begin;
265 default_mt->read_set_end = default_read_set_end;
266 default_mt->read_byte = default_read_byte;
267 default_mt->read_bool = default_read_bool;
268 default_mt->read_i16 = default_read_i16;
269 default_mt->read_i32 = default_read_i32;
270 default_mt->read_i64 = default_read_i64;
271 default_mt->read_double = default_read_double;
272 default_mt->read_string = default_read_string;
Bryan Duxburyc0166282009-02-02 00:48:17 +0000273}
274
275// end default protocol methods
276
277
278static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol);
279static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info);
280
281VALUE get_field_value(VALUE obj, VALUE field_name) {
Bryan Duxburye3ab50d2009-03-25 21:06:53 +0000282 char name_buf[RSTRING_LEN(field_name) + 1];
Bryan Duxburyc0166282009-02-02 00:48:17 +0000283
284 name_buf[0] = '@';
Bryan Duxburye3ab50d2009-03-25 21:06:53 +0000285 strlcpy(&name_buf[1], RSTRING_PTR(field_name), sizeof(name_buf));
Bryan Duxburyc0166282009-02-02 00:48:17 +0000286
287 VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
288
289 return value;
290}
291
292static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol) {
293 int sz, i;
294
295 if (ttype == TTYPE_MAP) {
296 VALUE keys;
297 VALUE key;
298 VALUE val;
299
300 Check_Type(value, T_HASH);
301
302 VALUE key_info = rb_hash_aref(field_info, key_sym);
303 VALUE keytype_value = rb_hash_aref(key_info, type_sym);
304 int keytype = FIX2INT(keytype_value);
305
306 VALUE value_info = rb_hash_aref(field_info, value_sym);
307 VALUE valuetype_value = rb_hash_aref(value_info, type_sym);
308 int valuetype = FIX2INT(valuetype_value);
309
310 keys = rb_funcall(value, keys_method_id, 0);
311
Bryan Duxburye3ab50d2009-03-25 21:06:53 +0000312 sz = RARRAY_LEN(keys);
Bryan Duxburyc0166282009-02-02 00:48:17 +0000313
314 mt->write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz));
315
316 for (i = 0; i < sz; i++) {
317 key = rb_ary_entry(keys, i);
318 val = rb_hash_aref(value, key);
319
320 if (IS_CONTAINER(keytype)) {
321 write_container(keytype, key_info, key, protocol);
322 } else {
323 write_anything(keytype, key, protocol, key_info);
324 }
325
326 if (IS_CONTAINER(valuetype)) {
327 write_container(valuetype, value_info, val, protocol);
328 } else {
329 write_anything(valuetype, val, protocol, value_info);
330 }
331 }
332
333 mt->write_map_end(protocol);
334 } else if (ttype == TTYPE_LIST) {
335 Check_Type(value, T_ARRAY);
336
Bryan Duxburye3ab50d2009-03-25 21:06:53 +0000337 sz = RARRAY_LEN(value);
Bryan Duxburyc0166282009-02-02 00:48:17 +0000338
339 VALUE element_type_info = rb_hash_aref(field_info, element_sym);
340 VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
341 int element_type = FIX2INT(element_type_value);
342
343 mt->write_list_begin(protocol, element_type_value, INT2FIX(sz));
344 for (i = 0; i < sz; ++i) {
345 VALUE val = rb_ary_entry(value, i);
346 if (IS_CONTAINER(element_type)) {
347 write_container(element_type, element_type_info, val, protocol);
348 } else {
349 write_anything(element_type, val, protocol, element_type_info);
350 }
351 }
352 mt->write_list_end(protocol);
353 } else if (ttype == TTYPE_SET) {
354 VALUE items;
355
356 if (TYPE(value) == T_ARRAY) {
357 items = value;
358 } else {
359 if (rb_cSet == CLASS_OF(value)) {
360 items = rb_funcall(value, entries_method_id, 0);
361 } else {
362 Check_Type(value, T_HASH);
363 items = rb_funcall(value, keys_method_id, 0);
364 }
365 }
366
Bryan Duxburye3ab50d2009-03-25 21:06:53 +0000367 sz = RARRAY_LEN(items);
Bryan Duxburyc0166282009-02-02 00:48:17 +0000368
369 VALUE element_type_info = rb_hash_aref(field_info, element_sym);
370 VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
371 int element_type = FIX2INT(element_type_value);
372
373 mt->write_set_begin(protocol, element_type_value, INT2FIX(sz));
374
375 for (i = 0; i < sz; i++) {
376 VALUE val = rb_ary_entry(items, i);
377 if (IS_CONTAINER(element_type)) {
378 write_container(element_type, element_type_info, val, protocol);
379 } else {
380 write_anything(element_type, val, protocol, element_type_info);
381 }
382 }
383
384 mt->write_set_end(protocol);
385 } else {
386 rb_raise(rb_eNotImpError, "can't write container of type: %d", ttype);
387 }
388}
389
390static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info) {
391 if (ttype == TTYPE_BOOL) {
392 mt->write_bool(protocol, value);
393 } else if (ttype == TTYPE_BYTE) {
394 mt->write_byte(protocol, value);
395 } else if (ttype == TTYPE_I16) {
396 mt->write_i16(protocol, value);
397 } else if (ttype == TTYPE_I32) {
398 mt->write_i32(protocol, value);
399 } else if (ttype == TTYPE_I64) {
400 mt->write_i64(protocol, value);
401 } else if (ttype == TTYPE_DOUBLE) {
402 mt->write_double(protocol, value);
403 } else if (ttype == TTYPE_STRING) {
404 mt->write_string(protocol, value);
405 } else if (IS_CONTAINER(ttype)) {
406 write_container(ttype, field_info, value, protocol);
407 } else if (ttype == TTYPE_STRUCT) {
408 rb_thrift_struct_write(value, protocol);
409 } else {
410 rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype);
411 }
412}
413
414static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
415 // call validate
416 rb_funcall(self, validate_method_id, 0);
417
Bryan Duxburyd815c212009-03-19 18:57:43 +0000418 // if (rb_funcall(protocol, native_qmark_method_id, 0) == Qtrue) {
419 // set_native_proto_function_pointers(protocol);
420 // } else {
421 // set_default_proto_function_pointers();
422 // }
423 check_native_proto_method_table(protocol);
Bryan Duxburyc0166282009-02-02 00:48:17 +0000424
425 // write struct begin
426 mt->write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
427
428 // iterate through all the fields here
429 VALUE struct_fields = STRUCT_FIELDS(self);
430 VALUE struct_field_ids_unordered = rb_funcall(struct_fields, keys_method_id, 0);
431 VALUE struct_field_ids_ordered = rb_funcall(struct_field_ids_unordered, sort_method_id, 0);
432
433 int i = 0;
Bryan Duxburye3ab50d2009-03-25 21:06:53 +0000434 for (i=0; i < RARRAY_LEN(struct_field_ids_ordered); i++) {
Bryan Duxburyc0166282009-02-02 00:48:17 +0000435 VALUE field_id = rb_ary_entry(struct_field_ids_ordered, i);
436 VALUE field_info = rb_hash_aref(struct_fields, field_id);
437
438 VALUE ttype_value = rb_hash_aref(field_info, type_sym);
439 int ttype = FIX2INT(ttype_value);
440 VALUE field_name = rb_hash_aref(field_info, name_sym);
441 VALUE field_value = get_field_value(self, field_name);
442
443 if (!NIL_P(field_value)) {
444 mt->write_field_begin(protocol, field_name, ttype_value, field_id);
445
446 write_anything(ttype, field_value, protocol, field_info);
447
448 mt->write_field_end(protocol);
449 }
450 }
451
452 mt->write_field_stop(protocol);
453
454 // write struct end
455 mt->write_struct_end(protocol);
456
457 return Qnil;
458}
459
460//-------------------------------------------
461// Reading section
462//-------------------------------------------
463
464static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
465
466static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
Bryan Duxburye3ab50d2009-03-25 21:06:53 +0000467 char name_buf[RSTRING_LEN(field_name) + 1];
Bryan Duxburyc0166282009-02-02 00:48:17 +0000468
469 name_buf[0] = '@';
Bryan Duxburye3ab50d2009-03-25 21:06:53 +0000470 strlcpy(&name_buf[1], RSTRING_PTR(field_name), sizeof(name_buf));
Bryan Duxburyc0166282009-02-02 00:48:17 +0000471
472 rb_ivar_set(obj, rb_intern(name_buf), value);
473}
474
475static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
476 VALUE result = Qnil;
477
478 if (ttype == TTYPE_BOOL) {
479 result = mt->read_bool(protocol);
480 } else if (ttype == TTYPE_BYTE) {
481 result = mt->read_byte(protocol);
482 } else if (ttype == TTYPE_I16) {
483 result = mt->read_i16(protocol);
484 } else if (ttype == TTYPE_I32) {
485 result = mt->read_i32(protocol);
486 } else if (ttype == TTYPE_I64) {
487 result = mt->read_i64(protocol);
488 } else if (ttype == TTYPE_STRING) {
489 result = mt->read_string(protocol);
490 } else if (ttype == TTYPE_DOUBLE) {
491 result = mt->read_double(protocol);
492 } else if (ttype == TTYPE_STRUCT) {
493 VALUE klass = rb_hash_aref(field_info, class_sym);
494 result = rb_class_new_instance(0, NULL, klass);
495 rb_thrift_struct_read(result, protocol);
496 } else if (ttype == TTYPE_MAP) {
497 int i;
498
499 VALUE map_header = mt->read_map_begin(protocol);
500 int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
501 int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
502 int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
503
504 VALUE key_info = rb_hash_aref(field_info, key_sym);
505 VALUE value_info = rb_hash_aref(field_info, value_sym);
506
507 result = rb_hash_new();
508
509 for (i = 0; i < num_entries; ++i) {
510 VALUE key, val;
511
512 key = read_anything(protocol, key_ttype, key_info);
513 val = read_anything(protocol, value_ttype, value_info);
514
515 rb_hash_aset(result, key, val);
516 }
517
518 mt->read_map_end(protocol);
519 } else if (ttype == TTYPE_LIST) {
520 int i;
521
522 VALUE list_header = mt->read_list_begin(protocol);
523 int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
524 int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
525 result = rb_ary_new2(num_elements);
526
527 for (i = 0; i < num_elements; ++i) {
528 rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
529 }
530
531
532 mt->read_list_end(protocol);
533 } else if (ttype == TTYPE_SET) {
534 VALUE items;
535 int i;
536
537 VALUE set_header = mt->read_set_begin(protocol);
538 int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
539 int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
540 items = rb_ary_new2(num_elements);
541
542 for (i = 0; i < num_elements; ++i) {
543 rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
544 }
545
546
547 mt->read_set_end(protocol);
548
549 result = rb_class_new_instance(1, &items, rb_cSet);
550 } else {
551 rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype);
552 }
553
554 return result;
555}
556
557static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
Bryan Duxburyd815c212009-03-19 18:57:43 +0000558 check_native_proto_method_table(protocol);
559
Bryan Duxburyc0166282009-02-02 00:48:17 +0000560 // read struct begin
561 mt->read_struct_begin(protocol);
562
563 VALUE struct_fields = STRUCT_FIELDS(self);
564
565 // read each field
566 while (true) {
567 VALUE field_header = rb_funcall(protocol, read_field_begin_method_id, 0);
568 VALUE field_type_value = rb_ary_entry(field_header, 1);
569 int field_type = FIX2INT(field_type_value);
570
571 if (field_type == TTYPE_STOP) {
572 break;
573 }
574
575 // make sure we got a type we expected
576 VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
577
578 if (!NIL_P(field_info)) {
579 int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
580 if (field_type == specified_type) {
581 // read the value
582 VALUE name = rb_hash_aref(field_info, name_sym);
583 set_field_value(self, name, read_anything(protocol, field_type, field_info));
584 } else {
585 rb_funcall(protocol, skip_method_id, 1, field_type_value);
586 }
587 } else {
588 rb_funcall(protocol, skip_method_id, 1, field_type_value);
589 }
590
591 // read field end
592 mt->read_field_end(protocol);
593 }
594
595 // read struct end
596 mt->read_struct_end(protocol);
597
598 return Qnil;
599}
600
601void Init_struct() {
602 VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct"));
603
604 rb_define_method(struct_module, "write", rb_thrift_struct_write, 1);
605 rb_define_method(struct_module, "read", rb_thrift_struct_read, 1);
606
607 set_default_proto_function_pointers();
608}
609