00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "dbus-gtype-specialized.h"
00026 #include <glib.h>
00027 #include <string.h>
00028 #include <gobject/gvaluecollector.h>
00029
00060 typedef enum {
00061 DBUS_G_SPECTYPE_COLLECTION,
00062 DBUS_G_SPECTYPE_MAP,
00063 DBUS_G_SPECTYPE_STRUCT
00064 } DBusGTypeSpecializedType;
00065
00066 typedef struct {
00067 DBusGTypeSpecializedType type;
00068 const DBusGTypeSpecializedVtable *vtable;
00069 } DBusGTypeSpecializedContainer;
00070
00071 typedef struct {
00072 guint num_types;
00073 GType *types;
00074 const DBusGTypeSpecializedContainer *klass;
00075 } DBusGTypeSpecializedData;
00076
00077 static GHashTable *specialized_containers;
00078
00079 static GQuark
00080 specialized_type_data_quark ()
00081 {
00082 static GQuark quark;
00083 if (!quark)
00084 quark = g_quark_from_static_string ("DBusGTypeSpecializedData");
00085
00086 return quark;
00087 }
00088
00089 void
00090 dbus_g_type_specialized_init (void)
00091 {
00092 specialized_containers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
00093 }
00094
00095 static gboolean
00096 specialized_types_is_initialized (void)
00097 {
00098 return specialized_containers != NULL;
00099 }
00100
00101 static DBusGTypeSpecializedData *
00102 lookup_specialization_data (GType type)
00103 {
00104 return g_type_get_qdata (type, specialized_type_data_quark ());
00105 }
00106
00107
00108
00109 static void
00110 proxy_value_init (GValue *value)
00111 {
00112 value->data[0].v_pointer = NULL;
00113 }
00114
00115
00116 static void
00117 proxy_value_free (GValue *value)
00118 {
00119 if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
00120 {
00121 DBusGTypeSpecializedData *data;
00122 GType type;
00123
00124 type = G_VALUE_TYPE (value);
00125 data = lookup_specialization_data (type);
00126 g_assert (data != NULL);
00127
00128 if (data->klass->vtable->free_func)
00129 {
00130 data->klass->vtable->free_func (type, value->data[0].v_pointer);
00131 }
00132 else
00133 {
00134 g_assert (data->klass->vtable->simple_free_func != NULL);
00135 data->klass->vtable->simple_free_func (value->data[0].v_pointer);
00136 }
00137 }
00138 }
00139
00140
00141 static void
00142 proxy_value_copy (const GValue *src_value,
00143 GValue *dest_value)
00144 {
00145 if (src_value->data[0].v_pointer)
00146 {
00147 DBusGTypeSpecializedData *data;
00148 GType type;
00149 type = G_VALUE_TYPE (src_value);
00150 data = lookup_specialization_data (type);
00151 g_assert (data != NULL);
00152 dest_value->data[0].v_pointer = data->klass->vtable->copy_func (type, src_value->data[0].v_pointer);
00153 }
00154 else
00155 dest_value->data[0].v_pointer = src_value->data[0].v_pointer;
00156 }
00157
00158
00159 static gpointer
00160 proxy_value_peek_pointer (const GValue *value)
00161 {
00162 return value->data[0].v_pointer;
00163 }
00164
00165
00166 static gchar*
00167 proxy_collect_value (GValue *value,
00168 guint n_collect_values,
00169 GTypeCValue *collect_values,
00170 guint collect_flags)
00171 {
00172 DBusGTypeSpecializedData *data;
00173 GType type;
00174
00175 type = G_VALUE_TYPE (value);
00176 data = lookup_specialization_data (type);
00177
00178 if (!collect_values[0].v_pointer)
00179 value->data[0].v_pointer = NULL;
00180 else
00181 {
00182 if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
00183 {
00184 value->data[0].v_pointer = collect_values[0].v_pointer;
00185 value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
00186 }
00187 else
00188 {
00189 value->data[0].v_pointer = data->klass->vtable->copy_func (type, collect_values[0].v_pointer);
00190 }
00191 }
00192
00193 return NULL;
00194 }
00195
00196
00197 static gchar*
00198 proxy_lcopy_value (const GValue *value,
00199 guint n_collect_values,
00200 GTypeCValue *collect_values,
00201 guint collect_flags)
00202 {
00203 gpointer *boxed_p = collect_values[0].v_pointer;
00204
00205 if (!boxed_p)
00206 return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
00207
00208 if (!value->data[0].v_pointer)
00209 *boxed_p = NULL;
00210 else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
00211 *boxed_p = value->data[0].v_pointer;
00212 else
00213 {
00214 DBusGTypeSpecializedData *data;
00215 GType type;
00216
00217 type = G_VALUE_TYPE (value);
00218 data = lookup_specialization_data (type);
00219
00220 *boxed_p = data->klass->vtable->copy_func (type, value->data[0].v_pointer);
00221 }
00222
00223 return NULL;
00224 }
00225
00226 static char *
00227 build_specialization_name (const char *prefix, guint num_types, const GType *types)
00228 {
00229 GString *fullname;
00230 guint i;
00231
00232 fullname = g_string_new (prefix);
00233
00234 g_string_append_c (fullname, '_');
00235 for (i=0; i < num_types; i++)
00236 {
00237 if (i!=0)
00238 g_string_append_c (fullname, '+');
00239 g_string_append (fullname, g_type_name (types[i]));
00240 }
00241 g_string_append_c (fullname, '_');
00242 return g_string_free (fullname, FALSE);
00243 }
00244
00245 static void
00246 register_container (const char *name,
00247 DBusGTypeSpecializedType type,
00248 const DBusGTypeSpecializedVtable *vtable)
00249 {
00250 DBusGTypeSpecializedContainer *klass;
00251
00252 klass = g_new0 (DBusGTypeSpecializedContainer, 1);
00253 klass->type = type;
00254 klass->vtable = vtable;
00255
00256 g_hash_table_insert (specialized_containers, g_strdup (name), klass);
00257 }
00258
00267 void
00268 dbus_g_type_register_collection (const char *name,
00269 const DBusGTypeSpecializedCollectionVtable *vtable,
00270 guint flags)
00271 {
00272 g_return_if_fail (specialized_types_is_initialized ());
00273 register_container (name, DBUS_G_SPECTYPE_COLLECTION, (const DBusGTypeSpecializedVtable*) vtable);
00274 }
00275
00284 void
00285 dbus_g_type_register_map (const char *name,
00286 const DBusGTypeSpecializedMapVtable *vtable,
00287 guint flags)
00288 {
00289 g_return_if_fail (specialized_types_is_initialized ());
00290 register_container (name, DBUS_G_SPECTYPE_MAP, (const DBusGTypeSpecializedVtable*) vtable);
00291 }
00292
00301 void
00302 dbus_g_type_register_struct (const char *name,
00303 const DBusGTypeSpecializedStructVtable *vtable,
00304 guint flags)
00305 {
00306 g_return_if_fail (specialized_types_is_initialized ());
00307 register_container (name, DBUS_G_SPECTYPE_STRUCT, (const DBusGTypeSpecializedVtable*) vtable);
00308 }
00309
00316 const DBusGTypeSpecializedMapVtable* dbus_g_type_map_peek_vtable (GType map_type)
00317 {
00318 DBusGTypeSpecializedData *data;
00319 g_return_val_if_fail (dbus_g_type_is_map(map_type), NULL);
00320
00321 data = lookup_specialization_data (map_type);
00322 g_assert (data != NULL);
00323
00324 return (DBusGTypeSpecializedMapVtable *)(data->klass->vtable);
00325 }
00326
00333 const DBusGTypeSpecializedCollectionVtable* dbus_g_type_collection_peek_vtable (GType collection_type)
00334 {
00335 DBusGTypeSpecializedData *data;
00336 g_return_val_if_fail (dbus_g_type_is_collection(collection_type), NULL);
00337
00338 data = lookup_specialization_data (collection_type);
00339 g_assert (data != NULL);
00340
00341 return (DBusGTypeSpecializedCollectionVtable *)(data->klass->vtable);
00342 }
00343
00350 const DBusGTypeSpecializedStructVtable* dbus_g_type_struct_peek_vtable (GType struct_type)
00351 {
00352 DBusGTypeSpecializedData *data;
00353 g_return_val_if_fail (dbus_g_type_is_struct (struct_type), NULL);
00354
00355 data = lookup_specialization_data (struct_type);
00356 g_assert (data != NULL);
00357
00358 return (DBusGTypeSpecializedStructVtable *)(data->klass->vtable);
00359 }
00360
00361 static GType
00362 register_specialized_instance (const DBusGTypeSpecializedContainer *klass,
00363 const char *name,
00364 guint num_types,
00365 const GType *types)
00366 {
00367 GType ret;
00368
00369 static const GTypeValueTable vtable =
00370 {
00371 proxy_value_init,
00372 proxy_value_free,
00373 proxy_value_copy,
00374 proxy_value_peek_pointer,
00375 "p",
00376 proxy_collect_value,
00377 "p",
00378 proxy_lcopy_value,
00379 };
00380 static const GTypeInfo derived_info =
00381 {
00382 0,
00383 NULL,
00384 NULL,
00385 NULL,
00386 NULL,
00387 NULL,
00388 0,
00389 0,
00390 NULL,
00391 &vtable,
00392 };
00393
00394 ret = g_type_register_static (G_TYPE_BOXED, name, &derived_info, 0);
00395
00396 if (ret != G_TYPE_INVALID)
00397 {
00398 DBusGTypeSpecializedData *data;
00399 data = g_new0 (DBusGTypeSpecializedData, 1);
00400 data->num_types = num_types;
00401 data->types = g_memdup (types, sizeof (GType) * num_types);
00402 data->klass = klass;
00403 g_type_set_qdata (ret, specialized_type_data_quark (), data);
00404 }
00405
00406 return ret;
00407 }
00408
00409 static GType
00410 lookup_or_register_specialized (const char *container,
00411 guint num_types,
00412 const GType *types)
00413 {
00414 GType ret;
00415 char *name;
00416 const DBusGTypeSpecializedContainer *klass;
00417
00418 g_return_val_if_fail (specialized_types_is_initialized (), G_TYPE_INVALID);
00419
00420 klass = g_hash_table_lookup (specialized_containers, container);
00421 g_return_val_if_fail (klass != NULL, G_TYPE_INVALID);
00422
00423 name = build_specialization_name (container, num_types, types);
00424 ret = g_type_from_name (name);
00425 if (ret == G_TYPE_INVALID)
00426 {
00427
00428 ret = register_specialized_instance (klass, name,
00429 num_types,
00430 types);
00431 }
00432 g_free (name);
00433 return ret;
00434 }
00435
00436
00447 GType
00448 dbus_g_type_get_collection (const char *container,
00449 GType specialization)
00450 {
00451 return lookup_or_register_specialized (container, 1, &specialization);
00452 }
00453
00465 GType
00466 dbus_g_type_get_map (const char *container,
00467 GType key_specialization,
00468 GType value_specialization)
00469 {
00470 GType types[2] = {key_specialization, value_specialization};
00471 return lookup_or_register_specialized (container, 2, types);
00472 }
00473
00485 GType
00486 dbus_g_type_get_structv (const char *container,
00487 guint num_members,
00488 GType *types)
00489 {
00490 return lookup_or_register_specialized (container, num_members, types);
00491 }
00492
00504 GType
00505 dbus_g_type_get_struct (const char *container,
00506 GType first_type,
00507 ...)
00508 {
00509 GArray *types;
00510 GType curtype, ret;
00511 va_list args;
00512 va_start (args, first_type);
00513
00514 types = g_array_new (FALSE, FALSE, sizeof (GType));
00515 curtype = first_type;
00516 while (curtype != G_TYPE_INVALID)
00517 {
00518 g_array_append_val (types, curtype);
00519 curtype = va_arg (args, GType);
00520 }
00521 va_end (args);
00522
00523 ret = lookup_or_register_specialized (container, types->len,
00524 (GType *) types->data);
00525
00526 g_array_free (types, TRUE);
00527
00528 return ret;
00529 }
00530
00531
00540 gboolean
00541 dbus_g_type_is_collection (GType gtype)
00542 {
00543 DBusGTypeSpecializedData *data;
00544 data = lookup_specialization_data (gtype);
00545 if (data == NULL)
00546 return FALSE;
00547 return data->klass->type == DBUS_G_SPECTYPE_COLLECTION;
00548 }
00549
00559 gboolean
00560 dbus_g_type_is_map (GType gtype)
00561 {
00562 DBusGTypeSpecializedData *data;
00563 data = lookup_specialization_data (gtype);
00564 if (data == NULL)
00565 return FALSE;
00566 return data->klass->type == DBUS_G_SPECTYPE_MAP;
00567 }
00568
00578 gboolean
00579 dbus_g_type_is_struct (GType gtype)
00580 {
00581 DBusGTypeSpecializedData *data;
00582 data = lookup_specialization_data (gtype);
00583 if (data == NULL)
00584 return FALSE;
00585 return data->klass->type == DBUS_G_SPECTYPE_STRUCT;
00586 }
00587
00588
00589 static GType
00590 get_specialization_index (GType gtype, guint i)
00591 {
00592 DBusGTypeSpecializedData *data;
00593
00594 data = lookup_specialization_data (gtype);
00595 if (i < data->num_types)
00596 return data->types[i];
00597 else
00598 return G_TYPE_INVALID;
00599 }
00600
00608 GType
00609 dbus_g_type_get_collection_specialization (GType gtype)
00610 {
00611 g_return_val_if_fail (dbus_g_type_is_collection (gtype), G_TYPE_INVALID);
00612 return get_specialization_index (gtype, 0);
00613 }
00614
00622 GType
00623 dbus_g_type_get_map_key_specialization (GType gtype)
00624 {
00625 g_return_val_if_fail (dbus_g_type_is_map (gtype), G_TYPE_INVALID);
00626 return get_specialization_index (gtype, 0);
00627 }
00628
00636 GType
00637 dbus_g_type_get_map_value_specialization (GType gtype)
00638 {
00639 g_return_val_if_fail (dbus_g_type_is_map (gtype), G_TYPE_INVALID);
00640 return get_specialization_index (gtype, 1);
00641 }
00642
00651 GType
00652 dbus_g_type_get_struct_member_type (GType gtype, guint member)
00653 {
00654 g_return_val_if_fail (dbus_g_type_is_struct (gtype), G_TYPE_INVALID);
00655 return get_specialization_index (gtype, member);
00656 }
00657
00665 guint
00666 dbus_g_type_get_struct_size (GType gtype)
00667 {
00668 DBusGTypeSpecializedData *data;
00669 g_return_val_if_fail (dbus_g_type_is_struct (gtype), G_TYPE_INVALID);
00670
00671 data = lookup_specialization_data (gtype);
00672 return data->num_types;
00673 }
00674
00687 gpointer
00688 dbus_g_type_specialized_construct (GType gtype)
00689 {
00690 DBusGTypeSpecializedData *data;
00691 g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
00692
00693 data = lookup_specialization_data (gtype);
00694 g_return_val_if_fail (data != NULL, FALSE);
00695
00696 return data->klass->vtable->constructor (gtype);
00697 }
00698
00707 gboolean
00708 dbus_g_type_collection_get_fixed (GValue *value,
00709 gpointer *data_ret,
00710 guint *len_ret)
00711 {
00712 DBusGTypeSpecializedData *data;
00713 GType gtype;
00714
00715 g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
00716 g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), FALSE);
00717
00718 gtype = G_VALUE_TYPE (value);
00719 data = lookup_specialization_data (gtype);
00720 g_return_val_if_fail (data != NULL, FALSE);
00721
00722 return ((DBusGTypeSpecializedCollectionVtable *) (data->klass->vtable))->fixed_accessor (gtype,
00723 g_value_get_boxed (value),
00724 data_ret, len_ret);
00725 }
00726
00738 void
00739 dbus_g_type_collection_value_iterate (const GValue *value,
00740 DBusGTypeSpecializedCollectionIterator iterator,
00741 gpointer user_data)
00742 {
00743 DBusGTypeSpecializedData *data;
00744 GType gtype;
00745
00746 g_return_if_fail (specialized_types_is_initialized ());
00747 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
00748
00749 gtype = G_VALUE_TYPE (value);
00750 data = lookup_specialization_data (gtype);
00751 g_return_if_fail (data != NULL);
00752
00753 ((DBusGTypeSpecializedCollectionVtable *) data->klass->vtable)->iterator (gtype,
00754 g_value_get_boxed (value),
00755 iterator, user_data);
00756 }
00757
00758 typedef struct {
00759 GValue *val;
00760 GType specialization_type;
00761 DBusGTypeSpecializedData *specdata;
00762 } DBusGTypeSpecializedAppendContextReal;
00763
00773 void
00774 dbus_g_type_specialized_init_append (GValue *value, DBusGTypeSpecializedAppendContext *ctx)
00775 {
00776 DBusGTypeSpecializedAppendContextReal *realctx = (DBusGTypeSpecializedAppendContextReal *) ctx;
00777 GType gtype;
00778 DBusGTypeSpecializedData *specdata;
00779
00780 g_return_if_fail (specialized_types_is_initialized ());
00781 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
00782 gtype = G_VALUE_TYPE (value);
00783 specdata = lookup_specialization_data (gtype);
00784 g_return_if_fail (specdata != NULL);
00785 g_return_if_fail (specdata->num_types != 0);
00786
00787 realctx->val = value;
00788 realctx->specialization_type = specdata->types[0];
00789 realctx->specdata = specdata;
00790 }
00791
00800 void
00801 dbus_g_type_specialized_collection_append (DBusGTypeSpecializedAppendContext *ctx,
00802 GValue *elt)
00803 {
00804 DBusGTypeSpecializedAppendContextReal *realctx = (DBusGTypeSpecializedAppendContextReal *) ctx;
00805 ((DBusGTypeSpecializedCollectionVtable *) realctx->specdata->klass->vtable)->append_func (ctx, elt);
00806 }
00807
00815 void
00816 dbus_g_type_specialized_collection_end_append (DBusGTypeSpecializedAppendContext *ctx)
00817 {
00818 DBusGTypeSpecializedAppendContextReal *realctx = (DBusGTypeSpecializedAppendContextReal *) ctx;
00819 if (((DBusGTypeSpecializedCollectionVtable *) realctx->specdata->klass->vtable)->end_append_func != NULL)
00820 ((DBusGTypeSpecializedCollectionVtable *) realctx->specdata->klass->vtable)->end_append_func (ctx);
00821 }
00822
00832 void
00833 dbus_g_type_specialized_map_append (DBusGTypeSpecializedAppendContext *ctx,
00834 GValue *key,
00835 GValue *val)
00836 {
00837 DBusGTypeSpecializedAppendContextReal *realctx = (DBusGTypeSpecializedAppendContextReal *) ctx;
00838 ((DBusGTypeSpecializedMapVtable *) realctx->specdata->klass->vtable)->append_func (ctx, key, val);
00839 }
00840
00841
00853 void
00854 dbus_g_type_map_value_iterate (const GValue *value,
00855 DBusGTypeSpecializedMapIterator iterator,
00856 gpointer user_data)
00857 {
00858 DBusGTypeSpecializedData *data;
00859 GType gtype;
00860
00861 g_return_if_fail (specialized_types_is_initialized ());
00862 g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
00863
00864 gtype = G_VALUE_TYPE (value);
00865 data = lookup_specialization_data (gtype);
00866 g_return_if_fail (data != NULL);
00867
00868 ((DBusGTypeSpecializedMapVtable *) data->klass->vtable)->iterator (gtype,
00869 g_value_get_boxed (value),
00870 iterator, user_data);
00871 }
00872
00885 gboolean
00886 dbus_g_type_struct_get_member (const GValue *value,
00887 guint member,
00888 GValue *dest)
00889 {
00890 DBusGTypeSpecializedData *data;
00891 GType gtype;
00892
00893 g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
00894 g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), FALSE);
00895
00896 gtype = G_VALUE_TYPE (value);
00897 data = lookup_specialization_data (gtype);
00898 g_return_val_if_fail (data != NULL, FALSE);
00899
00900 return ((DBusGTypeSpecializedStructVtable *) (data->klass->vtable))->get_member(gtype,
00901 g_value_get_boxed (value),
00902 member, dest);
00903 }
00904
00916 gboolean
00917 dbus_g_type_struct_set_member (GValue *value,
00918 guint member,
00919 const GValue *src)
00920 {
00921 DBusGTypeSpecializedData *data;
00922 GType gtype;
00923
00924 g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
00925 g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), FALSE);
00926
00927 gtype = G_VALUE_TYPE (value);
00928 data = lookup_specialization_data (gtype);
00929 g_return_val_if_fail (data != NULL, FALSE);
00930
00931 return ((DBusGTypeSpecializedStructVtable *) (data->klass->vtable))->set_member(gtype,
00932 g_value_get_boxed (value),
00933 member, src);
00934 }
00935
00950 gboolean
00951 dbus_g_type_struct_get (const GValue *value,
00952 guint first_member,
00953 ...)
00954 {
00955 va_list var_args;
00956 GType type;
00957 guint size,i;
00958 gchar *error;
00959 GValue val = {0,};
00960
00961 g_return_val_if_fail (dbus_g_type_is_struct (G_VALUE_TYPE (value)), FALSE);
00962
00963 va_start (var_args, first_member);
00964 size = dbus_g_type_get_struct_size (G_VALUE_TYPE (value));
00965 i = first_member;
00966 while (i != G_MAXUINT)
00967 {
00968 if (i >= size)
00969 goto error;
00970
00971 type = dbus_g_type_get_struct_member_type (G_VALUE_TYPE (value),i);
00972
00973 g_value_init (&val, type);
00974 dbus_g_type_struct_get_member (value, i, &val);
00975
00976 G_VALUE_LCOPY (&val, var_args, 0, &error);
00977 if (error)
00978 {
00979 g_warning ("%s, %s", G_STRFUNC, error);
00980 g_free (error);
00981 g_value_unset (&val);
00982 goto error;
00983 }
00984 g_value_unset (&val);
00985 i = va_arg (var_args, guint);
00986 }
00987 va_end (var_args);
00988 return TRUE;
00989 error:
00990 va_end (var_args);
00991 return FALSE;
00992 }
00993
01006 gboolean
01007 dbus_g_type_struct_set (GValue *value,
01008 guint first_member,
01009 ...)
01010 {
01011 va_list var_args;
01012 GType type;
01013 guint size,i;
01014 gchar *error;
01015 GValue val = {0,};
01016
01017 g_return_val_if_fail (dbus_g_type_is_struct (G_VALUE_TYPE (value)), FALSE);
01018
01019 va_start (var_args, first_member);
01020 size = dbus_g_type_get_struct_size (G_VALUE_TYPE (value));
01021 i = first_member;
01022 while (i != G_MAXUINT)
01023 {
01024 if (i >= size)
01025 goto error;
01026
01027 type = dbus_g_type_get_struct_member_type (G_VALUE_TYPE (value),i);
01028
01029 g_value_init (&val, type);
01030
01031 G_VALUE_COLLECT (&val, var_args, 0, &error);
01032 if (error)
01033 {
01034 g_warning ("%s, %s", G_STRFUNC, error);
01035 g_free (error);
01036 g_value_unset (&val);
01037 goto error;
01038 }
01039
01040 dbus_g_type_struct_set_member (value, i, &val);
01041
01042 g_value_unset (&val);
01043 i = va_arg (var_args, guint);
01044 }
01045 va_end (var_args);
01046 return TRUE;
01047 error:
01048 va_end (var_args);
01049 return FALSE;
01050 }
01051