OLD | NEW |
(Empty) | |
| 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 } |
OLD | NEW |