00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "dbus-gtype-specialized.h"
00025 #include <glib.h>
00026 #include <string.h>
00027 #include <gobject/gvaluecollector.h>
00028
00037 typedef enum {
00038 DBUS_G_SPECTYPE_COLLECTION,
00039 DBUS_G_SPECTYPE_MAP,
00040 DBUS_G_SPECTYPE_STRUCT
00041 } DBusGTypeSpecializedType;
00042
00043 typedef struct {
00044 DBusGTypeSpecializedType type;
00045 const DBusGTypeSpecializedVtable *vtable;
00046 } DBusGTypeSpecializedContainer;
00047
00048 typedef struct {
00049 guint num_types;
00050 GType *types;
00051 const DBusGTypeSpecializedContainer *klass;
00052 } DBusGTypeSpecializedData;
00053
00054 static GHashTable *specialized_containers;
00055
00056 static GQuark
00057 specialized_type_data_quark ()
00058 {
00059 static GQuark quark;
00060 if (!quark)
00061 quark = g_quark_from_static_string ("DBusGTypeSpecializedData");
00062
00063 return quark;
00064 }
00065
00066 void
00067 dbus_g_type_specialized_init (void)
00068 {
00069 specialized_containers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
00070 }
00071
00072 static gboolean
00073 specialized_types_is_initialized (void)
00074 {
00075 return specialized_containers != NULL;
00076 }
00077
00078 static DBusGTypeSpecializedData *
00079 lookup_specialization_data (GType type)
00080 {
00081 return g_type_get_qdata (type, specialized_type_data_quark ());
00082 }
00083
00084
00085
00086 static void
00087 proxy_value_init (GValue *value)
00088 {
00089 value->data[0].v_pointer = NULL;
00090 }
00091
00092
00093 static void
00094 proxy_value_free (GValue *value)
00095 {
00096 if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
00097 {
00098 DBusGTypeSpecializedData *data;
00099 GType type;
00100
00101 type = G_VALUE_TYPE (value);
00102 data = lookup_specialization_data (type);
00103 g_assert (data != NULL);
00104
00105 if (data->klass->vtable->free_func)
00106 {
00107 data->klass->vtable->free_func (type, value->data[0].v_pointer);
00108 }
00109 else
00110 {
00111 g_assert (data->klass->vtable->simple_free_func != NULL);
00112 data->klass->vtable->simple_free_func (value->data[0].v_pointer);
00113 }
00114 }
00115 }
00116
00117
00118 static void
00119 proxy_value_copy (const GValue *src_value,
00120 GValue *dest_value)
00121 {
00122 if (src_value->data[0].v_pointer)
00123 {
00124 DBusGTypeSpecializedData *data;
00125 GType type;
00126 type = G_VALUE_TYPE (src_value);
00127 data = lookup_specialization_data (type);
00128 g_assert (data != NULL);
00129 dest_value->data[0].v_pointer = data->klass->vtable->copy_func (type, src_value->data[0].v_pointer);
00130 }
00131 else
00132 dest_value->data[0].v_pointer = src_value->data[0].v_pointer;
00133 }
00134
00135
00136 static gpointer
00137 proxy_value_peek_pointer (const GValue *value)
00138 {
00139 return value->data[0].v_pointer;
00140 }
00141
00142
00143 static gchar*
00144 proxy_collect_value (GValue *value,
00145 guint n_collect_values,
00146 GTypeCValue *collect_values,
00147 guint collect_flags)
00148 {
00149 DBusGTypeSpecializedData *data;
00150 GType type;
00151
00152 type = G_VALUE_TYPE (value);
00153 data = lookup_specialization_data (type);
00154
00155 if (!collect_values[0].v_pointer)
00156 value->data[0].v_pointer = NULL;
00157 else
00158 {
00159 if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
00160 {
00161 value->data[0].v_pointer = collect_values[0].v_pointer;
00162 value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
00163 }
00164 else
00165 {
00166 value->data[0].v_pointer = data->klass->vtable->copy_func (type, collect_values[0].v_pointer);
00167 }
00168 }
00169
00170 return NULL;
00171 }
00172
00173
00174 static gchar*
00175 proxy_lcopy_value (const GValue *value,
00176 guint n_collect_values,
00177 GTypeCValue *collect_values,
00178 guint collect_flags)
00179 {
00180 gpointer *boxed_p = collect_values[0].v_pointer;
00181
00182 if (!boxed_p)
00183 return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
00184
00185 if (!value->data[0].v_pointer)
00186 *boxed_p = NULL;
00187 else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
00188 *boxed_p = value->data[0].v_pointer;
00189 else
00190 {
00191 DBusGTypeSpecializedData *data;
00192 GType type;
00193
00194 type = G_VALUE_TYPE (value);
00195 data = lookup_specialization_data (type);
00196
00197 *boxed_p = data->klass->vtable->copy_func (type, value->data[0].v_pointer);
00198 }
00199
00200 return NULL;
00201 }
00202
00203 static char *
00204 build_specialization_name (const char *prefix, guint num_types, GType *types)
00205 {
00206 GString *fullname;
00207 guint i;
00208
00209 fullname = g_string_new (prefix);
00210
00211 g_string_append_c (fullname, '_');
00212 for (i=0; i < num_types; i++)
00213 {
00214 if (i!=0)
00215 g_string_append_c (fullname, '+');
00216 g_string_append (fullname, g_type_name (types[i]));
00217 }
00218 g_string_append_c (fullname, '_');
00219 return g_string_free (fullname, FALSE);
00220 }
00221
00222 static void
00223 register_container (const char *name,
00224 DBusGTypeSpecializedType type,
00225 const DBusGTypeSpecializedVtable *vtable)
00226 {
00227 DBusGTypeSpecializedContainer *klass;
00228
00229 klass = g_new0 (DBusGTypeSpecializedContainer, 1);
00230 klass->type = type;
00231 klass->vtable = vtable;
00232
00233 g_hash_table_insert (specialized_containers, g_strdup (name), klass);
00234 }
00235
00236 void
00237 dbus_g_type_register_collection (const char *name,
00238 const DBusGTypeSpecializedCollectionVtable *vtable,
00239 guint flags)
00240 {
00241 g_return_if_fail (specialized_types_is_initialized ());
00242 register_container (name, DBUS_G_SPECTYPE_COLLECTION, (const DBusGTypeSpecializedVtable*) vtable);
00243 }
00244
00245 void
00246 dbus_g_type_register_map (const char *name,
00247 const DBusGTypeSpecializedMapVtable *vtable,
00248 guint flags)
00249 {
00250 g_return_if_fail (specialized_types_is_initialized ());
00251 register_container (name, DBUS_G_SPECTYPE_MAP, (const DBusGTypeSpecializedVtable*) vtable);
00252 }
00253
00254 void
00255 dbus_g_type_register_struct (const char *name,
00256 const DBusGTypeSpecializedStructVtable *vtable,
00257 guint flags)
00258 {
00259 g_return_if_fail (specialized_types_is_initialized ());
00260 register_container (name, DBUS_G_SPECTYPE_STRUCT, (const DBusGTypeSpecializedVtable*) vtable);
00261 }
00262
00263
00264 const DBusGTypeSpecializedMapVtable* dbus_g_type_map_peek_vtable (GType map_type)
00265 {
00266 DBusGTypeSpecializedData *data;
00267 g_return_val_if_fail (dbus_g_type_is_map(map_type), NULL);
00268
00269 data = lookup_specialization_data (map_type);
00270 g_assert (data != NULL);
00271
00272 return (DBusGTypeSpecializedMapVtable *)(data->klass->vtable);
00273 }
00274
00275 const DBusGTypeSpecializedCollectionVtable* dbus_g_type_collection_peek_vtable (GType collection_type)
00276 {
00277 DBusGTypeSpecializedData *data;
00278 g_return_val_if_fail (dbus_g_type_is_collection(collection_type), NULL);
00279
00280 data = lookup_specialization_data (collection_type);
00281 g_assert (data != NULL);
00282
00283 return (DBusGTypeSpecializedCollectionVtable *)(data->klass->vtable);
00284 }
00285
00286 const DBusGTypeSpecializedStructVtable* dbus_g_type_struct_peek_vtable (GType struct_type)
00287 {
00288 DBusGTypeSpecializedData *data;
00289 g_return_val_if_fail (dbus_g_type_is_struct (struct_type), NULL);
00290
00291 data = lookup_specialization_data (struct_type);
00292 g_assert (data != NULL);
00293
00294 return (DBusGTypeSpecializedStructVtable *)(data->klass->vtable);
00295 }
00296
00297 static GType
00298 register_specialized_instance (const DBusGTypeSpecializedContainer *klass,
00299 char *name,
00300 guint num_types,
00301 GType *types)
00302 {
00303 GType ret;
00304
00305 static const GTypeValueTable vtable =
00306 {
00307 proxy_value_init,
00308 proxy_value_free,
00309 proxy_value_copy,
00310 proxy_value_peek_pointer,
00311 "p",
00312 proxy_collect_value,
00313 "p",
00314 proxy_lcopy_value,
00315 };
00316 static const GTypeInfo derived_info =
00317 {
00318 0,
00319 NULL,
00320 NULL,
00321 NULL,
00322 NULL,
00323 NULL,
00324 0,
00325 0,
00326 NULL,
00327 &vtable,
00328 };
00329
00330 ret = g_type_register_static (G_TYPE_BOXED, name, &derived_info, 0);
00331
00332 if (ret != G_TYPE_INVALID)
00333 {
00334 DBusGTypeSpecializedData *data;
00335 data = g_new0 (DBusGTypeSpecializedData, 1);
00336 data->num_types = num_types;
00337 data->types = g_memdup (types, sizeof (GType) * num_types);
00338 data->klass = klass;
00339 g_type_set_qdata (ret, specialized_type_data_quark (), data);
00340 }
00341
00342 return ret;
00343 }
00344
00345 static GType
00346 lookup_or_register_specialized (const char *container,
00347 guint num_types,
00348 GType *types)
00349 {
00350 GType ret;
00351 char *name;
00352 const DBusGTypeSpecializedContainer *klass;
00353
00354 g_return_val_if_fail (specialized_types_is_initialized (), G_TYPE_INVALID);
00355
00356 klass = g_hash_table_lookup (specialized_containers, container);
00357 g_return_val_if_fail (klass != NULL, G_TYPE_INVALID);
00358
00359 name = build_specialization_name (container, num_types, types);
00360 ret = g_type_from_name (name);
00361 if (ret == G_TYPE_INVALID)
00362 {
00363
00364 ret = register_specialized_instance (klass, name,
00365 num_types,
00366 types);
00367 }
00368 g_free (name);
00369 return ret;
00370 }
00371
00372 GType
00373 dbus_g_type_get_collection (const char *container,
00374 GType specialization)
00375 {
00376 return lookup_or_register_specialized (container, 1, &specialization);
00377 }
00378
00379 GType
00380 dbus_g_type_get_map (const char *container,
00381 GType key_specialization,
00382 GType value_specialization)
00383 {
00384 GType types[2] = {key_specialization, value_specialization};
00385 return lookup_or_register_specialized (container, 2, types);
00386 }
00387
00388 GType
00389 dbus_g_type_get_structv (const char *container,
00390 guint num_items,
00391 GType *types)
00392 {
00393 return lookup_or_register_specialized (container, num_items, types);
00394 }
00395
00396 GType
00397 dbus_g_type_get_struct (const char *container,
00398 GType first_type,
00399 ...)
00400 {
00401 GArray *types;
00402 GType curtype, ret;
00403 va_list args;
00404 va_start (args, first_type);
00405
00406 types = g_array_new (FALSE, FALSE, sizeof (GType));
00407 curtype = first_type;
00408 while (curtype != G_TYPE_INVALID)
00409 {
00410 g_array_append_val (types, curtype);
00411 curtype = va_arg (args, GType);
00412 }
00413 va_end (args);
00414
00415 ret = lookup_or_register_specialized (container, types->len,
00416 (GType *) types->data);
00417
00418 g_array_free (types, TRUE);
00419
00420 return ret;
00421 }
00422
00423
00424
00425 gboolean
00426 dbus_g_type_is_collection (GType gtype)
00427 {
00428 DBusGTypeSpecializedData *data;
00429 data = lookup_specialization_data (gtype);
00430 if (data == NULL)
00431 return FALSE;
00432 return data->klass->type == DBUS_G_SPECTYPE_COLLECTION;
00433 }
00434
00435 gboolean
00436 dbus_g_type_is_map (GType gtype)
00437 {
00438 DBusGTypeSpecializedData *data;
00439 data = lookup_specialization_data (gtype);
00440 if (data == NULL)
00441 return FALSE;
00442 return data->klass->type == DBUS_G_SPECTYPE_MAP;
00443 }
00444
00445 gboolean
00446 dbus_g_type_is_struct (GType gtype)
00447 {
00448 DBusGTypeSpecializedData *data;
00449 data = lookup_specialization_data (gtype);
00450 if (data == NULL)
00451 return FALSE;
00452 return data->klass->type == DBUS_G_SPECTYPE_STRUCT;
00453 }
00454
00455
00456 static GType
00457 get_specialization_index (GType gtype, guint i)
00458 {
00459 DBusGTypeSpecializedData *data;
00460
00461 data = lookup_specialization_data (gtype);
00462 if (i < data->num_types)
00463 return data->types[i];
00464 else
00465 return G_TYPE_INVALID;
00466 }
00467
00468 GType
00469 dbus_g_type_get_collection_specialization (GType gtype)
00470 {
00471 g_return_val_if_fail (dbus_g_type_is_collection (gtype), G_TYPE_INVALID);
00472 return get_specialization_index (gtype, 0);
00473 }
00474
00475 GType
00476 dbus_g_type_get_map_key_specialization (GType gtype)
00477 {
00478 g_return_val_if_fail (dbus_g_type_is_map (gtype), G_TYPE_INVALID);
00479 return get_specialization_index (gtype, 0);
00480 }
00481
00482 GType
00483 dbus_g_type_get_map_value_specialization (GType gtype)
00484 {
00485 g_return_val_if_fail (dbus_g_type_is_map (gtype), G_TYPE_INVALID);
00486 return get_specialization_index (gtype, 1);
00487 }
00488
00489 GType
00490 dbus_g_type_get_struct_member_type (GType gtype, guint index_)
00491 {
00492 g_return_val_if_fail (dbus_g_type_is_struct (gtype), G_TYPE_INVALID);
00493 return get_specialization_index (gtype, index_);
00494 }
00495
00496 guint
00497 dbus_g_type_get_struct_size (GType gtype)
00498 {
00499 DBusGTypeSpecializedData *data;
00500 g_return_val_if_fail (dbus_g_type_is_struct (gtype), G_TYPE_INVALID);
00501
00502 data = lookup_specialization_data (gtype);
00503 return data->num_types;
00504 }
00505
00506
00507
00508 gpointer
00509 dbus_g_type_specialized_construct (GType type)
00510 {
00511 DBusGTypeSpecializedData *data;
00512 g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
00513
00514 data = lookup_specialization_data (type);
00515 g_return_val_if_fail (data != NULL, FALSE);
00516
00517 return data->klass->vtable->constructor (type);
00518 }
00519
00520 gboolean
00521 dbus_g_type_collection_get_fixed (GValue *value,
00522 gpointer *data_ret,
00523 guint *len_ret)
00524 {
00525 DBusGTypeSpecializedData *data;
00526 GType gtype;
00527
00528 g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
00529 g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), FALSE);
00530
00531 gtype = G_VALUE_TYPE (value);
00532 data = lookup_specialization_data (gtype);
00533 g_return_val_if_fail (data != NULL, FALSE);
00534
00535 return ((DBusGTypeSpecializedCollectionVtable *) (data->klass->vtable))->fixed_accessor (gtype,
00536 g_value_get_boxed (value),
00537 data_ret, len_ret);
00538 }
00539
00540 void
00541 dbus_g_type_collection_value_iterate (const GValue *value,
00542 DBusGTypeSpecializedCollectionIterator iterator,
00543 gpointer user_data)
00544 {
00545 DBusGTypeSpecializedData *data;
00546 GType gtype;
00547
00548 g_return_if_fail (specialized_types_is_initialized ());
00549 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
00550
00551 gtype = G_VALUE_TYPE (value);
00552 data = lookup_specialization_data (gtype);
00553 g_return_if_fail (data != NULL);
00554
00555 ((DBusGTypeSpecializedCollectionVtable *) data->klass->vtable)->iterator (gtype,
00556 g_value_get_boxed (value),
00557 iterator, user_data);
00558 }
00559
00560 typedef struct {
00561 GValue *val;
00562 GType specialization_type;
00563 DBusGTypeSpecializedData *specdata;
00564 } DBusGTypeSpecializedAppendContextReal;
00565
00566 void
00567 dbus_g_type_specialized_init_append (GValue *value, DBusGTypeSpecializedAppendContext *ctx)
00568 {
00569 DBusGTypeSpecializedAppendContextReal *realctx = (DBusGTypeSpecializedAppendContextReal *) ctx;
00570 GType gtype;
00571 DBusGTypeSpecializedData *specdata;
00572
00573 g_return_if_fail (specialized_types_is_initialized ());
00574 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
00575 gtype = G_VALUE_TYPE (value);
00576 specdata = lookup_specialization_data (gtype);
00577 g_return_if_fail (specdata != NULL);
00578 g_return_if_fail (specdata->num_types != 0);
00579
00580 realctx->val = value;
00581 realctx->specialization_type = specdata->types[0];
00582 realctx->specdata = specdata;
00583 }
00584
00585 void
00586 dbus_g_type_specialized_collection_append (DBusGTypeSpecializedAppendContext *ctx,
00587 GValue *elt)
00588 {
00589 DBusGTypeSpecializedAppendContextReal *realctx = (DBusGTypeSpecializedAppendContextReal *) ctx;
00590 ((DBusGTypeSpecializedCollectionVtable *) realctx->specdata->klass->vtable)->append_func (ctx, elt);
00591 }
00592
00593 void
00594 dbus_g_type_specialized_collection_end_append (DBusGTypeSpecializedAppendContext *ctx)
00595 {
00596 DBusGTypeSpecializedAppendContextReal *realctx = (DBusGTypeSpecializedAppendContextReal *) ctx;
00597 if (((DBusGTypeSpecializedCollectionVtable *) realctx->specdata->klass->vtable)->end_append_func != NULL)
00598 ((DBusGTypeSpecializedCollectionVtable *) realctx->specdata->klass->vtable)->end_append_func (ctx);
00599 }
00600
00601 void
00602 dbus_g_type_specialized_map_append (DBusGTypeSpecializedAppendContext *ctx,
00603 GValue *key,
00604 GValue *val)
00605 {
00606 DBusGTypeSpecializedAppendContextReal *realctx = (DBusGTypeSpecializedAppendContextReal *) ctx;
00607 ((DBusGTypeSpecializedMapVtable *) realctx->specdata->klass->vtable)->append_func (ctx, key, val);
00608 }
00609
00610 void
00611 dbus_g_type_map_value_iterate (const GValue *value,
00612 DBusGTypeSpecializedMapIterator iterator,
00613 gpointer user_data)
00614 {
00615 DBusGTypeSpecializedData *data;
00616 GType gtype;
00617
00618 g_return_if_fail (specialized_types_is_initialized ());
00619 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
00620
00621 gtype = G_VALUE_TYPE (value);
00622 data = lookup_specialization_data (gtype);
00623 g_return_if_fail (data != NULL);
00624
00625 ((DBusGTypeSpecializedMapVtable *) data->klass->vtable)->iterator (gtype,
00626 g_value_get_boxed (value),
00627 iterator, user_data);
00628 }
00629
00630 gboolean
00631 dbus_g_type_struct_get_member (const GValue *value,
00632 guint index_,
00633 GValue *dest)
00634 {
00635 DBusGTypeSpecializedData *data;
00636 GType gtype;
00637
00638 g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
00639 g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), FALSE);
00640
00641 gtype = G_VALUE_TYPE (value);
00642 data = lookup_specialization_data (gtype);
00643 g_return_val_if_fail (data != NULL, FALSE);
00644
00645 return ((DBusGTypeSpecializedStructVtable *) (data->klass->vtable))->get_member(gtype,
00646 g_value_get_boxed (value),
00647 index_, dest);
00648 }
00649
00650 gboolean
00651 dbus_g_type_struct_set_member (GValue *value,
00652 guint index_,
00653 const GValue *src)
00654 {
00655 DBusGTypeSpecializedData *data;
00656 GType gtype;
00657
00658 g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
00659 g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), FALSE);
00660
00661 gtype = G_VALUE_TYPE (value);
00662 data = lookup_specialization_data (gtype);
00663 g_return_val_if_fail (data != NULL, FALSE);
00664
00665 return ((DBusGTypeSpecializedStructVtable *) (data->klass->vtable))->set_member(gtype,
00666 g_value_get_boxed (value),
00667 index_, src);
00668 }
00669
00684 gboolean
00685 dbus_g_type_struct_get (const GValue *value,
00686 guint first_member,
00687 ...)
00688 {
00689 va_list var_args;
00690 GType type;
00691 guint size,i;
00692 gchar *error;
00693 GValue val = {0,};
00694
00695 g_return_val_if_fail (dbus_g_type_is_struct (G_VALUE_TYPE (value)), FALSE);
00696
00697 va_start (var_args, first_member);
00698 size = dbus_g_type_get_struct_size (G_VALUE_TYPE (value));
00699 i = first_member;
00700 while (i != G_MAXUINT)
00701 {
00702 if (i >= size)
00703 goto error;
00704
00705 type = dbus_g_type_get_struct_member_type (G_VALUE_TYPE (value),i);
00706
00707 g_value_init (&val, type);
00708 dbus_g_type_struct_get_member (value, i, &val);
00709
00710 G_VALUE_LCOPY (&val, var_args, 0, &error);
00711 if (error)
00712 {
00713 g_warning ("%s, %s", G_STRFUNC, error);
00714 g_free (error);
00715 g_value_unset (&val);
00716 goto error;
00717 }
00718 g_value_unset (&val);
00719 i = va_arg (var_args, guint);
00720 }
00721 va_end (var_args);
00722 return TRUE;
00723 error:
00724 va_end (var_args);
00725 return FALSE;
00726 }
00727
00740 gboolean
00741 dbus_g_type_struct_set (GValue *value,
00742 guint first_member,
00743 ...)
00744 {
00745 va_list var_args;
00746 GType type;
00747 guint size,i;
00748 gchar *error;
00749 GValue val = {0,};
00750
00751 g_return_val_if_fail (dbus_g_type_is_struct (G_VALUE_TYPE (value)), FALSE);
00752
00753 va_start (var_args, first_member);
00754 size = dbus_g_type_get_struct_size (G_VALUE_TYPE (value));
00755 i = first_member;
00756 while (i != G_MAXUINT)
00757 {
00758 if (i >= size)
00759 goto error;
00760
00761 type = dbus_g_type_get_struct_member_type (G_VALUE_TYPE (value),i);
00762
00763 g_value_init (&val, type);
00764
00765 G_VALUE_COLLECT (&val, var_args, 0, &error);
00766 if (error)
00767 {
00768 g_warning ("%s, %s", G_STRFUNC, error);
00769 g_free (error);
00770 g_value_unset (&val);
00771 goto error;
00772 }
00773
00774 dbus_g_type_struct_set_member (value, i, &val);
00775
00776 g_value_unset (&val);
00777 i = va_arg (var_args, guint);
00778 }
00779 va_end (var_args);
00780 return TRUE;
00781 error:
00782 va_end (var_args);
00783 return FALSE;
00784 }
00785