|  | @@ -148,6 +148,11 @@ static const void* newhandlerdata(upb_handlers* h, uint32_t ofs) {
 | 
	
		
			
				|  |  |    return hd_ofs;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +typedef struct {
 | 
	
		
			
				|  |  | +  void* closure;
 | 
	
		
			
				|  |  | +  stringsink sink;
 | 
	
		
			
				|  |  | +} stringfields_parseframe_t;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  typedef size_t (*encodeunknown_handlerfunc)(void* _sink, const void* hd,
 | 
	
		
			
				|  |  |                                              const char* ptr, size_t len,
 | 
	
		
			
				|  |  |                                              const upb_bufhandle* handle);
 | 
	
	
		
			
				|  | @@ -245,46 +250,41 @@ DEFINE_APPEND_HANDLER(int64,  int64_t)
 | 
	
		
			
				|  |  |  DEFINE_APPEND_HANDLER(uint64, uint64_t)
 | 
	
		
			
				|  |  |  DEFINE_APPEND_HANDLER(double, double)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Appends a string to a repeated field.
 | 
	
		
			
				|  |  | +// Appends a string or 'bytes' string to a repeated field.
 | 
	
		
			
				|  |  |  static void* appendstr_handler(void *closure,
 | 
	
		
			
				|  |  |                                 const void *hd,
 | 
	
		
			
				|  |  |                                 size_t size_hint) {
 | 
	
		
			
				|  |  | -  zval* array = (zval*)closure;
 | 
	
		
			
				|  |  | -  TSRMLS_FETCH();
 | 
	
		
			
				|  |  | -  RepeatedField* intern = UNBOX(RepeatedField, array);
 | 
	
		
			
				|  |  | +  UPB_UNUSED(hd);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#if PHP_MAJOR_VERSION < 7
 | 
	
		
			
				|  |  | -  zval* str;
 | 
	
		
			
				|  |  | -  MAKE_STD_ZVAL(str);
 | 
	
		
			
				|  |  | -  PHP_PROTO_ZVAL_STRING(str, "", 1);
 | 
	
		
			
				|  |  | -  repeated_field_push_native(intern, &str);
 | 
	
		
			
				|  |  | -  return (void*)str;
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -  zend_string* str = zend_string_init("", 0, 1);
 | 
	
		
			
				|  |  | -  repeated_field_push_native(intern, &str);
 | 
	
		
			
				|  |  | -  return intern;
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | +  stringfields_parseframe_t* frame =
 | 
	
		
			
				|  |  | +      (stringfields_parseframe_t*)malloc(sizeof(stringfields_parseframe_t));
 | 
	
		
			
				|  |  | +  frame->closure = closure;
 | 
	
		
			
				|  |  | +  stringsink_init(&frame->sink);
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  return frame;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Appends a 'bytes' string to a repeated field.
 | 
	
		
			
				|  |  | -static void* appendbytes_handler(void *closure,
 | 
	
		
			
				|  |  | -                                 const void *hd,
 | 
	
		
			
				|  |  | -                                 size_t size_hint) {
 | 
	
		
			
				|  |  | -  zval* array = (zval*)closure;
 | 
	
		
			
				|  |  | +static bool appendstr_end_handler(void *closure, const void *hd) {
 | 
	
		
			
				|  |  | +  stringfields_parseframe_t* frame = closure;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  zval* array = (zval*)frame->closure;
 | 
	
		
			
				|  |  |    TSRMLS_FETCH();
 | 
	
		
			
				|  |  |    RepeatedField* intern = UNBOX(RepeatedField, array);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #if PHP_MAJOR_VERSION < 7
 | 
	
		
			
				|  |  |    zval* str;
 | 
	
		
			
				|  |  |    MAKE_STD_ZVAL(str);
 | 
	
		
			
				|  |  | -  PHP_PROTO_ZVAL_STRING(str, "", 1);
 | 
	
		
			
				|  |  | +  PHP_PROTO_ZVAL_STRINGL(str, frame->sink.ptr, frame->sink.len, 1);
 | 
	
		
			
				|  |  |    repeated_field_push_native(intern, &str);
 | 
	
		
			
				|  |  | -  return (void*)str;
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  | -  zend_string* str = zend_string_init("", 0, 1);
 | 
	
		
			
				|  |  | +  zend_string* str = zend_string_init(frame->sink.ptr, frame->sink.len, 1);
 | 
	
		
			
				|  |  |    repeated_field_push_native(intern, &str);
 | 
	
		
			
				|  |  | -  return intern;
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  stringsink_uninit(&frame->sink);
 | 
	
		
			
				|  |  | +  free(frame);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return true;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Handlers that append primitive values to a repeated field.
 | 
	
	
		
			
				|  | @@ -326,64 +326,76 @@ static void *empty_php_string(zval* value_ptr) {
 | 
	
		
			
				|  |  |    return value_ptr;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | +#if PHP_MAJOR_VERSION < 7
 | 
	
		
			
				|  |  | +static void *empty_php_string2(zval** value_ptr) {
 | 
	
		
			
				|  |  | +  SEPARATE_ZVAL_IF_NOT_REF(value_ptr);
 | 
	
		
			
				|  |  | +  if (Z_TYPE_PP(value_ptr) == IS_STRING &&
 | 
	
		
			
				|  |  | +      !IS_INTERNED(Z_STRVAL_PP(value_ptr))) {
 | 
	
		
			
				|  |  | +    FREE(Z_STRVAL_PP(value_ptr));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  ZVAL_EMPTY_STRING(*value_ptr);
 | 
	
		
			
				|  |  | +  return (void*)(*value_ptr);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +static void new_php_string(zval** value_ptr, const char* str, size_t len) {
 | 
	
		
			
				|  |  | +  SEPARATE_ZVAL_IF_NOT_REF(value_ptr);
 | 
	
		
			
				|  |  | +  if (Z_TYPE_PP(value_ptr) == IS_STRING &&
 | 
	
		
			
				|  |  | +      !IS_INTERNED(Z_STRVAL_PP(value_ptr))) {
 | 
	
		
			
				|  |  | +    FREE(Z_STRVAL_PP(value_ptr));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  ZVAL_EMPTY_STRING(*value_ptr);
 | 
	
		
			
				|  |  | +  ZVAL_STRINGL(*value_ptr, str, len, 1);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +static void *empty_php_string2(zval* value_ptr) {
 | 
	
		
			
				|  |  | +  if (Z_TYPE_P(value_ptr) == IS_STRING) {
 | 
	
		
			
				|  |  | +    zend_string_release(Z_STR_P(value_ptr));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  ZVAL_EMPTY_STRING(value_ptr);
 | 
	
		
			
				|  |  | +  return value_ptr;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +static void new_php_string(zval* value_ptr, const char* str, size_t len) {
 | 
	
		
			
				|  |  | +  if (Z_TYPE_P(value_ptr) == IS_STRING) {
 | 
	
		
			
				|  |  | +    zend_string_release(Z_STR_P(value_ptr));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  ZVAL_NEW_STR(value_ptr, zend_string_init(str, len, 0));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Sets a non-repeated string field in a message.
 | 
	
		
			
				|  |  | +// Sets a non-repeated string/bytes field in a message.
 | 
	
		
			
				|  |  |  static void* str_handler(void *closure,
 | 
	
		
			
				|  |  |                           const void *hd,
 | 
	
		
			
				|  |  |                           size_t size_hint) {
 | 
	
		
			
				|  |  | -  MessageHeader* msg = closure;
 | 
	
		
			
				|  |  | -  const size_t *ofs = hd;
 | 
	
		
			
				|  |  | -  return empty_php_string(DEREF(message_data(msg), *ofs, CACHED_VALUE*));
 | 
	
		
			
				|  |  | +  UPB_UNUSED(hd);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  stringfields_parseframe_t* frame =
 | 
	
		
			
				|  |  | +      (stringfields_parseframe_t*)malloc(sizeof(stringfields_parseframe_t));
 | 
	
		
			
				|  |  | +  frame->closure = closure;
 | 
	
		
			
				|  |  | +  stringsink_init(&frame->sink);
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  return frame;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Sets a non-repeated 'bytes' field in a message.
 | 
	
		
			
				|  |  | -static void* bytes_handler(void *closure,
 | 
	
		
			
				|  |  | -                           const void *hd,
 | 
	
		
			
				|  |  | -                           size_t size_hint) {
 | 
	
		
			
				|  |  | -  MessageHeader* msg = closure;
 | 
	
		
			
				|  |  | +static bool str_end_handler(void *closure, const void *hd) {
 | 
	
		
			
				|  |  | +  stringfields_parseframe_t* frame = closure;
 | 
	
		
			
				|  |  |    const size_t *ofs = hd;
 | 
	
		
			
				|  |  | -  return empty_php_string(DEREF(message_data(msg), *ofs, CACHED_VALUE*));
 | 
	
		
			
				|  |  | +  MessageHeader* msg = (MessageHeader*)frame->closure;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  new_php_string(DEREF(message_data(msg), *ofs, CACHED_VALUE*),
 | 
	
		
			
				|  |  | +                 frame->sink.ptr, frame->sink.len);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  stringsink_uninit(&frame->sink);
 | 
	
		
			
				|  |  | +  free(frame);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return true;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static size_t stringdata_handler(void* closure, const void* hd,
 | 
	
		
			
				|  |  |                                   const char* str, size_t len,
 | 
	
		
			
				|  |  |                                   const upb_bufhandle* handle) {
 | 
	
		
			
				|  |  | -  zval* php_str = (zval*)closure;
 | 
	
		
			
				|  |  | -#if PHP_MAJOR_VERSION < 7
 | 
	
		
			
				|  |  | -  // Oneof string/bytes fields may have NULL initial value, which doesn't need
 | 
	
		
			
				|  |  | -  // to be freed.
 | 
	
		
			
				|  |  | -  if (Z_TYPE_P(php_str) == IS_STRING && !IS_INTERNED(Z_STRVAL_P(php_str))) {
 | 
	
		
			
				|  |  | -    FREE(Z_STRVAL_P(php_str));
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  ZVAL_STRINGL(php_str, str, len, 1);
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -  if (Z_TYPE_P(php_str) == IS_STRING) {
 | 
	
		
			
				|  |  | -    zend_string_release(Z_STR_P(php_str));
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  ZVAL_NEW_STR(php_str, zend_string_init(str, len, 0));
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -  return len;
 | 
	
		
			
				|  |  | +  stringfields_parseframe_t* frame = closure;
 | 
	
		
			
				|  |  | +  return stringsink_string(&frame->sink, hd, str, len, handle);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#if PHP_MAJOR_VERSION >= 7
 | 
	
		
			
				|  |  | -static size_t zendstringdata_handler(void* closure, const void* hd,
 | 
	
		
			
				|  |  | -                                     const char* str, size_t len,
 | 
	
		
			
				|  |  | -                                     const upb_bufhandle* handle) {
 | 
	
		
			
				|  |  | -  RepeatedField* intern = (RepeatedField*)closure;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  unsigned char memory[NATIVE_SLOT_MAX_SIZE];
 | 
	
		
			
				|  |  | -  memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
 | 
	
		
			
				|  |  | -  *(zend_string**)memory = zend_string_init(str, len, 0);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  HashTable *ht = PHP_PROTO_HASH_OF(intern->array);
 | 
	
		
			
				|  |  | -  int index = zend_hash_num_elements(ht) - 1;
 | 
	
		
			
				|  |  | -  php_proto_zend_hash_index_update_mem(
 | 
	
		
			
				|  |  | -      ht, index, memory, sizeof(zend_string*), NULL);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  return len;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  // Appends a submessage to a repeated field.
 | 
	
		
			
				|  |  |  static void *appendsubmsg_handler(void *closure, const void *hd) {
 | 
	
		
			
				|  |  |    zval* array = (zval*)closure;
 | 
	
	
		
			
				|  | @@ -744,13 +756,38 @@ static void *oneofbytes_handler(void *closure,
 | 
	
		
			
				|  |  |     return empty_php_string(DEREF(
 | 
	
		
			
				|  |  |         message_data(msg), oneofdata->ofs, CACHED_VALUE*));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +static bool oneofstr_end_handler(void *closure, const void *hd) {
 | 
	
		
			
				|  |  | +  stringfields_parseframe_t* frame = closure;
 | 
	
		
			
				|  |  | +  MessageHeader* msg = (MessageHeader*)frame->closure;
 | 
	
		
			
				|  |  | +  const oneof_handlerdata_t *oneofdata = hd;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  oneof_cleanup(msg, oneofdata);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  DEREF(message_data(msg), oneofdata->case_ofs, uint32_t) =
 | 
	
		
			
				|  |  | +      oneofdata->oneof_case_num;
 | 
	
		
			
				|  |  | +  DEREF(message_data(msg), oneofdata->ofs, CACHED_VALUE*) =
 | 
	
		
			
				|  |  | +      OBJ_PROP(&msg->std, oneofdata->property_ofs);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  new_php_string(DEREF(message_data(msg), oneofdata->ofs, CACHED_VALUE*),
 | 
	
		
			
				|  |  | +                 frame->sink.ptr, frame->sink.len);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  stringsink_uninit(&frame->sink);
 | 
	
		
			
				|  |  | +  free(frame);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return true;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void *oneofstr_handler(void *closure,
 | 
	
		
			
				|  |  |                                const void *hd,
 | 
	
		
			
				|  |  |                                size_t size_hint) {
 | 
	
		
			
				|  |  | -  // TODO(teboring): Add it back.
 | 
	
		
			
				|  |  | -  // rb_enc_associate(str, kRubyString8bitEncoding);
 | 
	
		
			
				|  |  | -  return oneofbytes_handler(closure, hd, size_hint);
 | 
	
		
			
				|  |  | +  UPB_UNUSED(hd);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  stringfields_parseframe_t* frame =
 | 
	
		
			
				|  |  | +      (stringfields_parseframe_t*)malloc(sizeof(stringfields_parseframe_t));
 | 
	
		
			
				|  |  | +  frame->closure = closure;
 | 
	
		
			
				|  |  | +  stringsink_init(&frame->sink);
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  return frame;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Handler for a submessage field in a oneof.
 | 
	
	
		
			
				|  | @@ -822,15 +859,9 @@ static void add_handlers_for_repeated_field(upb_handlers *h,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case UPB_TYPE_STRING:
 | 
	
		
			
				|  |  |      case UPB_TYPE_BYTES: {
 | 
	
		
			
				|  |  | -      bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
 | 
	
		
			
				|  |  | -      upb_handlers_setstartstr(h, f, is_bytes ?
 | 
	
		
			
				|  |  | -                               appendbytes_handler : appendstr_handler,
 | 
	
		
			
				|  |  | -                               NULL);
 | 
	
		
			
				|  |  | -#if PHP_MAJOR_VERSION < 7
 | 
	
		
			
				|  |  | +      upb_handlers_setstartstr(h, f, appendstr_handler, NULL);
 | 
	
		
			
				|  |  |        upb_handlers_setstring(h, f, stringdata_handler, NULL);
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -      upb_handlers_setstring(h, f, zendstringdata_handler, NULL);
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | +      upb_handlers_setendstr(h, f, appendstr_end_handler, &attr);
 | 
	
		
			
				|  |  |        break;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      case UPB_TYPE_MESSAGE: {
 | 
	
	
		
			
				|  | @@ -870,13 +901,11 @@ static void add_handlers_for_singular_field(upb_handlers *h,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case UPB_TYPE_STRING:
 | 
	
		
			
				|  |  |      case UPB_TYPE_BYTES: {
 | 
	
		
			
				|  |  | -      bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
 | 
	
		
			
				|  |  |        upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
 | 
	
		
			
				|  |  |        upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset));
 | 
	
		
			
				|  |  | -      upb_handlers_setstartstr(h, f,
 | 
	
		
			
				|  |  | -                               is_bytes ? bytes_handler : str_handler,
 | 
	
		
			
				|  |  | -                               &attr);
 | 
	
		
			
				|  |  | +      upb_handlers_setstartstr(h, f, str_handler, &attr);
 | 
	
		
			
				|  |  |        upb_handlers_setstring(h, f, stringdata_handler, &attr);
 | 
	
		
			
				|  |  | +      upb_handlers_setendstr(h, f, str_end_handler, &attr);
 | 
	
		
			
				|  |  |        upb_handlerattr_uninit(&attr);
 | 
	
		
			
				|  |  |        break;
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -958,11 +987,9 @@ static void add_handlers_for_oneof_field(upb_handlers *h,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case UPB_TYPE_STRING:
 | 
	
		
			
				|  |  |      case UPB_TYPE_BYTES: {
 | 
	
		
			
				|  |  | -      bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
 | 
	
		
			
				|  |  | -      upb_handlers_setstartstr(h, f, is_bytes ?
 | 
	
		
			
				|  |  | -                               oneofbytes_handler : oneofstr_handler,
 | 
	
		
			
				|  |  | -                               &attr);
 | 
	
		
			
				|  |  | +      upb_handlers_setstartstr(h, f, oneofstr_handler, &attr);
 | 
	
		
			
				|  |  |        upb_handlers_setstring(h, f, stringdata_handler, NULL);
 | 
	
		
			
				|  |  | +      upb_handlers_setendstr(h, f, oneofstr_end_handler, &attr);
 | 
	
		
			
				|  |  |        break;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      case UPB_TYPE_MESSAGE: {
 |