Home | History | Annotate | Download | only in protobuf
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2014 Google Inc.  All rights reserved.
      3 // https://developers.google.com/protocol-buffers/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are
      7 // met:
      8 //
      9 //     * Redistributions of source code must retain the above copyright
     10 // notice, this list of conditions and the following disclaimer.
     11 //     * Redistributions in binary form must reproduce the above
     12 // copyright notice, this list of conditions and the following disclaimer
     13 // in the documentation and/or other materials provided with the
     14 // distribution.
     15 //     * Neither the name of Google Inc. nor the names of its
     16 // contributors may be used to endorse or promote products derived from
     17 // this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 #include <php.h>
     32 
     33 #include "protobuf.h"
     34 
     35 // -----------------------------------------------------------------------------
     36 // Class/module creation from msgdefs and enumdefs, respectively.
     37 // -----------------------------------------------------------------------------
     38 
     39 void* message_data(void* msg) {
     40   return ((uint8_t *)msg) + sizeof(MessageHeader);
     41 }
     42 
     43 void message_set_property(zval* object, zval* field_name, zval* value,
     44                           const zend_literal* key TSRMLS_DC) {
     45   const upb_fielddef* field;
     46 
     47   MessageHeader* self = zend_object_store_get_object(object TSRMLS_CC);
     48 
     49   CHECK_TYPE(field_name, IS_STRING);
     50   field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(field_name));
     51   if (field == NULL) {
     52     zend_error(E_ERROR, "Unknown field: %s", Z_STRVAL_P(field_name));
     53   }
     54   layout_set(self->descriptor->layout, message_data(self), field, value);
     55 }
     56 
     57 zval* message_get_property(zval* object, zval* member, int type,
     58                              const zend_literal* key TSRMLS_DC) {
     59   MessageHeader* self =
     60       (MessageHeader*)zend_object_store_get_object(object TSRMLS_CC);
     61   CHECK_TYPE(member, IS_STRING);
     62 
     63   const upb_fielddef* field;
     64   field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
     65   if (field == NULL) {
     66     return EG(uninitialized_zval_ptr);
     67   }
     68   zval* retval = layout_get(self->descriptor->layout, message_data(self), field TSRMLS_CC);
     69   return retval;
     70 }
     71 
     72 static  zend_function_entry message_methods[] = {
     73   PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC)
     74   {NULL, NULL, NULL}
     75 };
     76 
     77 /* stringsink *****************************************************************/
     78 
     79 // This should probably be factored into a common upb component.
     80 
     81 typedef struct {
     82   upb_byteshandler handler;
     83   upb_bytessink sink;
     84   char *ptr;
     85   size_t len, size;
     86 } stringsink;
     87 
     88 static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) {
     89   stringsink *sink = _sink;
     90   sink->len = 0;
     91   return sink;
     92 }
     93 
     94 static size_t stringsink_string(void *_sink, const void *hd, const char *ptr,
     95                                 size_t len, const upb_bufhandle *handle) {
     96   stringsink *sink = _sink;
     97   size_t new_size = sink->size;
     98 
     99   UPB_UNUSED(hd);
    100   UPB_UNUSED(handle);
    101 
    102   while (sink->len + len > new_size) {
    103     new_size *= 2;
    104   }
    105 
    106   if (new_size != sink->size) {
    107     sink->ptr = realloc(sink->ptr, new_size);
    108     sink->size = new_size;
    109   }
    110 
    111   memcpy(sink->ptr + sink->len, ptr, len);
    112   sink->len += len;
    113 
    114   return len;
    115 }
    116 
    117 void stringsink_init(stringsink *sink) {
    118   upb_byteshandler_init(&sink->handler);
    119   upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL);
    120   upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL);
    121 
    122   upb_bytessink_reset(&sink->sink, &sink->handler, sink);
    123 
    124   sink->size = 32;
    125   sink->ptr = malloc(sink->size);
    126   sink->len = 0;
    127 }
    128 
    129 void stringsink_uninit(stringsink *sink) { free(sink->ptr); }
    130 
    131 // Stack-allocated context during an encode/decode operation. Contains the upb
    132 // environment and its stack-based allocator, an initial buffer for allocations
    133 // to avoid malloc() when possible, and a template for PHP exception messages
    134 // if any error occurs.
    135 #define STACK_ENV_STACKBYTES 4096
    136 typedef struct {
    137   upb_env env;
    138   upb_seededalloc alloc;
    139   const char *php_error_template;
    140   char allocbuf[STACK_ENV_STACKBYTES];
    141 } stackenv;
    142 
    143 static void stackenv_init(stackenv* se, const char* errmsg);
    144 static void stackenv_uninit(stackenv* se);
    145 
    146 // Callback invoked by upb if any error occurs during parsing or serialization.
    147 static bool env_error_func(void* ud, const upb_status* status) {
    148     stackenv* se = ud;
    149     // Free the env -- rb_raise will longjmp up the stack past the encode/decode
    150     // function so it would not otherwise have been freed.
    151     stackenv_uninit(se);
    152 
    153     // TODO(teboring): have a way to verify that this is actually a parse error,
    154     // instead of just throwing "parse error" unconditionally.
    155     zend_error(E_ERROR, se->php_error_template);
    156     // Never reached.
    157     return false;
    158 }
    159 
    160 static void stackenv_init(stackenv* se, const char* errmsg) {
    161   se->php_error_template = errmsg;
    162   upb_env_init(&se->env);
    163   upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES);
    164   upb_env_setallocfunc(&se->env, upb_seededalloc_getallocfunc(&se->alloc),
    165                        &se->alloc);
    166   upb_env_seterrorfunc(&se->env, env_error_func, se);
    167 }
    168 
    169 static void stackenv_uninit(stackenv* se) {
    170   upb_env_uninit(&se->env);
    171   upb_seededalloc_uninit(&se->alloc);
    172 }
    173 
    174 // -----------------------------------------------------------------------------
    175 // Message
    176 // -----------------------------------------------------------------------------
    177 
    178 static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) {
    179   if (desc->pb_serialize_handlers == NULL) {
    180     desc->pb_serialize_handlers =
    181         upb_pb_encoder_newhandlers(desc->msgdef, &desc->pb_serialize_handlers);
    182   }
    183   return desc->pb_serialize_handlers;
    184 }
    185 
    186 PHP_METHOD(Message, encode) {
    187   Descriptor* desc = (Descriptor*)zend_object_store_get_object(
    188       CE_STATIC_MEMBERS(Z_OBJCE_P(getThis()))[0] TSRMLS_CC);
    189 
    190   stringsink sink;
    191   stringsink_init(&sink);
    192 
    193   {
    194     const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc);
    195 
    196     stackenv se;
    197     upb_pb_encoder* encoder;
    198 
    199     stackenv_init(&se, "Error occurred during encoding: %s");
    200     encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink);
    201 
    202     putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0);
    203 
    204     RETVAL_STRINGL(sink.ptr, sink.len, 1);
    205 
    206     stackenv_uninit(&se);
    207     stringsink_uninit(&sink);
    208   }
    209 }
    210 
    211 void message_free(void * object TSRMLS_DC) {
    212   FREE(object);
    213 }
    214 
    215 zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) {
    216   zend_object_value return_value;
    217 
    218   zval* php_descriptor = get_def_obj(ce);
    219 
    220   Descriptor* desc = zend_object_store_get_object(php_descriptor TSRMLS_CC);
    221   MessageHeader* msg = (MessageHeader*)ALLOC_N(
    222       uint8_t, sizeof(MessageHeader) + desc->layout->size);
    223   memset(message_data(msg), 0, desc->layout->size);
    224 
    225   // We wrap first so that everything in the message object is GC-rooted in case
    226   // a collection happens during object creation in layout_init().
    227   msg->descriptor = desc;
    228 
    229   layout_init(desc->layout, message_data(msg));
    230   zend_object_std_init(&msg->std, ce TSRMLS_CC);
    231 
    232   return_value.handle = zend_objects_store_put(
    233       msg, (zend_objects_store_dtor_t)zend_objects_destroy_object,
    234       message_free, NULL TSRMLS_CC);
    235 
    236   return_value.handlers = PROTOBUF_G(message_handlers);
    237   return return_value;
    238 }
    239 
    240 const zend_class_entry* build_class_from_descriptor(
    241     zval* php_descriptor TSRMLS_DC) {
    242   Descriptor* desc = php_to_descriptor(php_descriptor);
    243   if (desc->layout == NULL) {
    244     MessageLayout* layout = create_layout(desc->msgdef);
    245     desc->layout = layout;
    246   }
    247   // TODO(teboring): Add it back.
    248   // if (desc->fill_method == NULL) {
    249   //   desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method);
    250   // }
    251 
    252   const char* name = upb_msgdef_fullname(desc->msgdef);
    253   if (name == NULL) {
    254     zend_error(E_ERROR, "Descriptor does not have assigned name.");
    255   }
    256 
    257   zend_class_entry class_entry;
    258   INIT_CLASS_ENTRY_EX(class_entry, name, strlen(name), message_methods);
    259   zend_class_entry* registered_ce =
    260       zend_register_internal_class(&class_entry TSRMLS_CC);
    261 
    262   add_def_obj(registered_ce, php_descriptor);
    263 
    264   if (PROTOBUF_G(message_handlers) == NULL) {
    265     PROTOBUF_G(message_handlers) = ALLOC(zend_object_handlers);
    266     memcpy(PROTOBUF_G(message_handlers), zend_get_std_object_handlers(),
    267            sizeof(zend_object_handlers));
    268     PROTOBUF_G(message_handlers)->write_property = message_set_property;
    269     PROTOBUF_G(message_handlers)->read_property = message_get_property;
    270   }
    271 
    272   registered_ce->create_object = message_create;
    273 }
    274