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