Author: heimdall
Date: Fri Aug 19 16:58:36 2005
New Revision: 9003
Added:
branches/gmc/docs/running.pod (props changed)
- copied unchanged from r8993, branches/gmc/docs/running.pod
Modified:
branches/gmc/include/parrot/pobj.h
branches/gmc/include/parrot/smallobject.h
branches/gmc/include/parrot/stacks.h
branches/gmc/lib/Parrot/Pmc2c.pm
branches/gmc/src/dod.c
branches/gmc/src/gc_gmc.c
branches/gmc/src/headers.c
branches/gmc/src/jit_debug.c
branches/gmc/src/memory.c
branches/gmc/src/stack_common.c
branches/gmc/src/string.c
branches/gmc/t/native_pbc/integer_1.pbc
branches/gmc/t/native_pbc/integer_2.pbc
branches/gmc/t/native_pbc/integer_3.pbc
branches/gmc/t/native_pbc/integer_4.pbc
branches/gmc/t/native_pbc/number_1.pbc
branches/gmc/t/native_pbc/number_2.pbc
branches/gmc/t/native_pbc/number_3.pbc
branches/gmc/t/native_pbc/number_4.pbc
branches/gmc/t/native_pbc/number_5.pbc
branches/gmc/t/native_pbc/string_1.pbc
branches/gmc/t/native_pbc/string_2.pbc
Log:
Merge with trunk. Added sweep functions for buffers and gc_gmc_run should be
correct when related to final sweep. Also many bugs corrected but make test
still fails in some places...
Modified: branches/gmc/include/parrot/pobj.h
==============================================================================
--- branches/gmc/include/parrot/pobj.h (original)
+++ branches/gmc/include/parrot/pobj.h Fri Aug 19 16:58:36 2005
@@ -93,7 +93,6 @@ typedef struct pobj_t {
/* TODO: Change to a real pmc_body type. */
typedef struct default_body {
UnionVal u;
- Parrot_UInt flags;
#if ! DISABLE_GC_DEBUG
UINTVAL _pobj_version;
#endif
@@ -107,7 +106,6 @@ typedef struct default_body {
typedef struct pobj_body {
UnionVal u;
- Parrot_UInt flags;
#if ! DISABLE_GC_DEBUG
UINTVAL _pobj_version;
#endif
@@ -115,6 +113,11 @@ typedef struct pobj_body {
typedef struct Buffer {
+ /* Having the flags in the fixed size header makes life more simple
+ * as it allows us to test easily if there is an object or not, whereas if
+ * we have it in the body, we need it to be allocated if we want to access
+ * it. */
+ Parrot_UInt flags;
pobj_body *body;
} Buffer;
@@ -189,6 +192,7 @@ typedef enum {
struct parrot_string_t {
#if PARROT_GC_GMC
+ Parrot_UInt flags;
PMC_BODY *body;
#else
pobj_t obj;
@@ -214,6 +218,7 @@ struct parrot_string_t {
struct PMC {
#if PARROT_GC_GMC
+ Parrot_UInt flags;
PMC_BODY *body;
#else
pobj_t obj;
@@ -381,7 +386,10 @@ typedef enum PObj_enum {
/* True if the PMC is a class */
PObj_is_class_FLAG = 1 << 29,
/* True if the PMC is a parrot object */
- PObj_is_object_FLAG = 1 << 30
+ PObj_is_object_FLAG = 1 << 30,
+
+ /* True if there is a pobj at this position. */
+ PObj_exists_FLAG = 1 << 31,
} PObj_flags;
@@ -492,7 +500,7 @@ typedef enum PObj_enum {
#endif /* ARENA_DOD_FLAGS */
#if PARROT_GC_GMC
-#define PObj_get_FLAGS(o) ((o)->body->flags)
+#define PObj_get_FLAGS(o) ((o)->flags)
#else
#define PObj_get_FLAGS(o) ((o)->obj.flags)
#endif
@@ -546,6 +554,10 @@ typedef enum PObj_enum {
#define PObj_sysmem_SET(o) PObj_flag_SET(sysmem, o)
#define PObj_sysmem_CLEAR(o) PObj_flag_CLEAR(sysmem, o)
+#define PObj_exists_TEST(o) PObj_flag_TEST(exists, o)
+#define PObj_exists_SET(o) PObj_flag_SET(exists,o)
+#define PObj_exists_CLEAR(o) PObj_flag_CLEAR(exists, o)
+
#define PObj_special_SET(flag, o) do { \
PObj_flag_SET(flag, o); \
Modified: branches/gmc/include/parrot/smallobject.h
==============================================================================
--- branches/gmc/include/parrot/smallobject.h (original)
+++ branches/gmc/include/parrot/smallobject.h Fri Aug 19 16:58:36 2005
@@ -275,6 +275,7 @@ struct Small_Object_Pool {
#if PARROT_GC_GMC
Gc_gmc *gc;
Gc_gmc_area_list *areas; /* pointers to the headers areas */
+ void *limit; /* Last object to be allocated in the current area */
#endif
};
Modified: branches/gmc/include/parrot/stacks.h
==============================================================================
--- branches/gmc/include/parrot/stacks.h (original)
+++ branches/gmc/include/parrot/stacks.h Fri Aug 19 16:58:36 2005
@@ -25,7 +25,8 @@ typedef struct Stack_Entry {
typedef struct Stack_Chunk {
#if PARROT_GC_GMC
- PMC_BODY *body;
+ Parrot_UInt flags;
+ pobj_body *body;
#else
pobj_t obj;
#endif
Modified: branches/gmc/lib/Parrot/Pmc2c.pm
==============================================================================
--- branches/gmc/lib/Parrot/Pmc2c.pm (original)
+++ branches/gmc/lib/Parrot/Pmc2c.pm Fri Aug 19 16:58:36 2005
@@ -845,7 +845,7 @@ Parrot_${classname}_class_init(Parrot_In
$enum_name, /* base_type */
NULL, /* whoami */
$vtbl_flag, /* flags */
- 32, /* size of pmc_body */
+ 28, /* size of pmc_body */
NULL, /* does_str */
NULL, /* isa_str */
NULL, /* class */
Modified: branches/gmc/src/dod.c
==============================================================================
--- branches/gmc/src/dod.c (original)
+++ branches/gmc/src/dod.c Fri Aug 19 16:58:36 2005
@@ -225,7 +225,7 @@ void pobject_lives(Interp *interpreter,
if (PObj_is_PMC_TEST(obj)) {
PMC *p = (PMC*)obj;
#if PARROT_GC_GMC
- if (PMC_metadata(p))
+ if (PObj_is_PMC_EXT_TEST(p) && PMC_metadata(p))
#else
if (p->pmc_ext && PMC_metadata(p))
#endif
@@ -832,11 +832,7 @@ Parrot_dod_sweep(Interp *interpreter,
if (PObj_active_destroy_TEST(p))
VTABLE_destroy(interpreter, p);
-#if PARROT_GC_GMC
- if (PObj_is_PMC_EXT_TEST(p) && PMC_data(p) != NULL) {
- mem_sys_free(PMC_data(p));
- }
-#else
+#if ! PARROT_GC_GMC
if (PObj_is_PMC_EXT_TEST(p) && p->pmc_ext != NULL) {
/* if the PMC has a PMC_EXT structure,
* return it to the pool/arena
Modified: branches/gmc/src/gc_gmc.c
==============================================================================
--- branches/gmc/src/gc_gmc.c (original)
+++ branches/gmc/src/gc_gmc.c Fri Aug 19 16:58:36 2005
@@ -229,28 +229,99 @@ static int sweep_pmc (Interp *interprete
struct Arenas *arena_base = interpreter->arena_base;
PMC *ptr;
Gc_gmc_area_store *store;
- Gc_gmc_header_area *area;
+ Gc_gmc_header_area **area;
+
/* Go through all the headers of the pool. */
for (store = pool->areas->first; store; store = store->next)
{
- for (area = store->store[0]; (UINTVAL)area < (UINTVAL)store->ptr;
area++)
+ for (area = &store->store[0]; (UINTVAL)area < (UINTVAL)store->ptr;
area++)
{
- for (ptr = (PMC*)area->fst; (UINTVAL)ptr < (UINTVAL)area->lst;
+ for (ptr = (PMC*)(*area)->fst; (UINTVAL)ptr < (UINTVAL)(*area)->lst;
ptr = (PMC*)((char*)ptr + pool->object_size))
{
- if (PObj_live_TEST(ptr))
+ if (PObj_exists_TEST(ptr) && !PObj_live_TEST(ptr))
{
/* This shouldn't be necessary. */
if (PObj_needs_early_DOD_TEST(ptr))
--arena_base->num_early_DOD_PMCs;
- if (PObj_active_destroy_TEST(ptr))
+ if (PObj_active_destroy_TEST(ptr)) {
VTABLE_destroy(interpreter, ptr);
+ }
+ PObj_exists_CLEAR(ptr);
+ /* This is the work of the VTABLE_destroy function. */
+ /*
if ((Gmc_has_PMC_EXT_TEST(ptr) ||
PObj_is_PMC_EXT_TEST(ptr)) && PMC_data(ptr))
{
mem_sys_free(PMC_data(ptr));
PMC_data(ptr) = NULL;
+ } */
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int sweep_buf (Interp *interpreter, struct Small_Object_Pool *pool,
+ int flag, void *arg)
+{
+ struct Arenas *arena_base = interpreter->arena_base;
+ PObj *obj;
+ Gc_gmc_area_store *store;
+ Gc_gmc_header_area **area;
+ /* Go through all the headers of the pool. */
+ for (store = pool->areas->first; store; store = store->next)
+ {
+ for (area = &store->store[0]; (UINTVAL)area < (UINTVAL)store->ptr;
area++)
+ {
+ for (obj = (PObj*)(*area)->fst; (UINTVAL)obj <
(UINTVAL)(*area)->lst;
+ obj = (PObj*)((char*)obj + pool->object_size))
+ {
+
+ if (PObj_exists_TEST(obj))
+ {
+ if (PObj_sysmem_TEST(obj) && PObj_bufstart(obj)) {
+ /* has sysmem allocated, e.g. string_pin */
+ mem_sys_free(PObj_bufstart(obj));
+ PObj_bufstart(obj) = NULL;
+ PObj_buflen(obj) = 0;
+ }
+ else {
+#ifdef GC_IS_MALLOC
+ /* free allocated space at (int*)bufstart - 1,
+ * but not if it is used COW or external
+ */
+ if (PObj_bufstart(obj) &&
+ !PObj_is_external_or_free_TESTALL(obj)) {
+ if (PObj_COW_TEST(obj)) {
+ INTVAL *refcount = ((INTVAL
*)PObj_bufstart(obj) - 1);
+
+ if (!--(*refcount))
+ free(refcount); /* the actual bufstart */
+ }
+ else
+ free((INTVAL*)PObj_bufstart(obj) - 1);
+ }
+#else
+ /*
+ * XXX Jarkko did report that on irix pool->mem_pool
+ * was NULL, which really shouldn't happen
+ */
+ if (pool->mem_pool) {
+ if (!PObj_COW_TEST(obj)) {
+ ((struct Memory_Pool *)
+ pool->mem_pool)->guaranteed_reclaimable +=
+ PObj_buflen(obj);
+ }
+ ((struct Memory_Pool *)
+ pool->mem_pool)->possibly_reclaimable +=
+ PObj_buflen(obj);
+ }
+#endif
+ PObj_buflen(obj) = 0;
}
- PObj_live_CLEAR(ptr);
+ PObj_exists_CLEAR(obj);
}
}
}
@@ -258,6 +329,31 @@ static int sweep_pmc (Interp *interprete
return 0;
}
+
+/* Clear all live bits of the objects in pool. */
+ static int
+gc_gmc_clear_live(Interp *interpreter, struct Small_Object_Pool *pool,
+ int flags, void *arg)
+{
+ PObj *obj;
+ Gc_gmc_area_store *store;
+ Gc_gmc_header_area **area;
+
+ for (store = pool->areas->first; store; store = store->next)
+ {
+ for (area = &store->store[0]; (UINTVAL)area < (UINTVAL)store->ptr;
area++)
+ {
+ for (obj = (PObj*)(*area)->fst; (UINTVAL)obj <
(UINTVAL)(*area)->lst;
+ obj = (PObj*)((char*)obj + pool->object_size))
+ {
+ if (PObj_exists_TEST(obj))
+ PObj_live_CLEAR(obj);
+ }
+ }
+ }
+ return 0;
+}
+
static void gc_gmc_run(Interp *interpreter, int flags)
{
struct Arenas *arena_base = interpreter->arena_base;
@@ -269,10 +365,11 @@ static void gc_gmc_run(Interp *interpret
/* This interpreter will be destroyed, free everything. */
if (flags & DOD_finish_FLAG) {
/* First the pmc headers */
+ Parrot_forall_header_pools(interpreter, POOL_ALL, 0, gc_gmc_clear_live);
Parrot_forall_header_pools(interpreter, POOL_PMC, 0, sweep_pmc);
+ Parrot_forall_header_pools(interpreter, POOL_BUFFER, 0, sweep_buf);
/* Then the pmc_bodies. */
- /* TODO: free the PMC_data too. */
gc_gmc_pool_deinit(interpreter, arena_base->pmc_pool);
#ifdef GMC_DEBUG
@@ -301,56 +398,15 @@ void Parrot_gc_gmc_init(Interp *interpre
}
-/******************************* FAKE THINGS ********************************/
-static void *
-gc_gmc_fake_get_free_object(Interp *interpreter,
- struct Small_Object_Pool *pool)
-{
- return NULL;
-}
+/******************************* REAL THINGS ********************************/
-static void *
-gc_gmc_fake_get_free_typed_object(Interp *interpreter,
- struct Small_Object_Pool *pool, INTVAL base_type)
-{
- return NULL;
-}
-
static void
-gc_gmc_add_free_object(Interp *interpreter,
- struct Small_Object_Pool *pool, void *to_add)
+gc_gmc_alloc_objects(Interp *interpreter, struct Small_Object_Pool *pool)
{
-#ifdef GMC_DEBUG
- fprintf (stderr, "GMC: Adding object %p to the free list\n", to_add);
-#endif
}
-void
-gc_gmc_alloc_objects(Interp *interpreter,
- struct Small_Object_Pool *pool)
-{
-#ifdef GMC_DEBUG
- fprintf (stderr, "GMC: Allocating more objects\n");
-#endif
-}
-
-static void
-gc_gmc_fake_more_objects(Interp *interpreter,
- struct Small_Object_Pool *pool)
-{
-#ifndef GMC_DEBUG
- fprintf (stderr, "GMC: I want more objects !\n");
-#endif
-}
-
-
-
-
-/******************************* REAL THINGS ********************************/
-
-
static void *
gc_gmc_get_free_object_of_size(Interp *interpreter,
struct Small_Object_Pool *pool, size_t size, INTVAL aggreg)
@@ -381,11 +437,6 @@ gc_gmc_get_free_object_of_size(Interp *i
gen = (aggreg) ? gc->yng_lst : gc->old_lst;
-
-#ifdef GMC_DEBUG
- fprintf(stderr, "Considering to use gen (%p,%p) --> %p\n", gen, gen->first,
(char*)gen->fst_free + gen->remaining);
-#endif
-
/* Should we use the next generation ? */
if (size >= gen->remaining)
{
@@ -396,10 +447,6 @@ gc_gmc_get_free_object_of_size(Interp *i
gen = gen->next;
}
-#ifdef GMC_DEBUG
- fprintf(stderr, "Using gen (%p,%p) --> %p\n", gen, gen->first,
(char*)gen->fst_free + gen->remaining);
-#endif
-
pmc_body = gen->fst_free;
gen->fst_free = (void*)((char*)gen->fst_free + size);
gen->remaining -= size;
@@ -416,11 +463,12 @@ gc_gmc_get_free_object_of_size(Interp *i
/* Allocate the PMC* */
/* if we don't have any objects */
- if (!pool->free_list)
+ if ((UINTVAL)pool->free_list >= (UINTVAL)pool->limit)
(*pool->more_objects) (interpreter, pool);
pmc = (PMC*)pool->free_list;
- pool->free_list = *(void **)pmc;
+ PObj_exists_SET((PMC*)pmc);
+ pool->free_list = (void*)((char*)pmc + pool->object_size);
--pool->num_free_objects;
PMC_body((PMC*)pmc) = Gmc_PMC_hdr_get_BODY(pmc_body);
@@ -447,7 +495,7 @@ gc_gmc_get_free_object(Interp *interpret
static void
-gc_gmc_real_add_free_object(Interp *interpreter,
+gc_gmc_add_free_object(Interp *interpreter,
struct Small_Object_Pool *pool, void *to_add)
{
Gmc_PMC_flag_SET(marking,(PMC*)to_add);
@@ -586,35 +634,35 @@ gc_gmc_more_objects(Interp *interpreter,
struct Small_Object_Pool *pool)
{
#define NUM_NEW_OBJ 512
- void *fst = mem_sys_allocate_zeroed(NUM_NEW_OBJ * pool->object_size);
- int i;
- char *obj;
- Gc_gmc_area_store *store = pool->areas->last;
+ void *fst = mem_sys_allocate_zeroed(NUM_NEW_OBJ * pool->object_size);
+ int i;
+ char *obj;
+ Gc_gmc_area_store *store = pool->areas->last;
- /* If we have no more space in the store, expand it. */
- if ((UINTVAL)(*(store->ptr)) >=
(UINTVAL)&(store->store[GC_GMC_STORE_SIZE-1]))
- {
- store = mem_sys_allocate_zeroed(sizeof(Gc_gmc_area_store));
- store->ptr = &(store->store[0]);
- store->next = NULL;
- pool->areas->last->next = store;
- pool->areas->last = store;
- }
-
- /* Record the new area. */
- *store->ptr = mem_sys_allocate(sizeof(Gc_gmc_header_area));
- (*store->ptr)->fst = fst;
- (*store->ptr)->lst = (void *)((char*)fst + NUM_NEW_OBJ *
pool->object_size);
- store->ptr++;
-
- /* Set the internal state correctly. */
- for (i = 0, obj = (char*)fst; i < NUM_NEW_OBJ; i++, obj +=
pool->object_size)
- *(void**)obj = obj + pool->object_size;
- *(void**)(obj - pool->object_size) = NULL;
- pool->free_list = fst;
- pool->num_free_objects += NUM_NEW_OBJ;
+ /* If we have no more space in the store, expand it. */
+ if ((UINTVAL)store->ptr >= (UINTVAL)&(store->store[GC_GMC_STORE_SIZE-1]))
+ {
+ store = mem_sys_allocate_zeroed(sizeof(Gc_gmc_area_store));
+ store->ptr = &(store->store[0]);
+ store->next = NULL;
+ pool->areas->last->next = store;
+ pool->areas->last = store;
+ }
+
+ /* Record the new area. */
+ *store->ptr = mem_sys_allocate(sizeof(Gc_gmc_header_area));
+ (*store->ptr)->fst = fst;
+ (*store->ptr)->lst = (void *)((char*)fst + NUM_NEW_OBJ *
pool->object_size);
+ store->ptr++;
+
+ /* Set the flags correctly. */
+ for (i = 0, obj = (char*)fst; i < NUM_NEW_OBJ; i++, obj +=
pool->object_size)
+ PObj_exists_CLEAR((PObj*)obj);
+ pool->free_list = fst;
+ pool->limit = (void*)((char*)fst + NUM_NEW_OBJ * pool->object_size);
+ pool->num_free_objects += NUM_NEW_OBJ;
#ifdef GMC_DEBUG
- fprintf(stderr, "Allocating %d more objects of size %d beginning at
%p\n", NUM_NEW_OBJ, pool->object_size, fst);
+ fprintf(stderr, "Allocating %d more objects of size %d beginning at %p\n",
NUM_NEW_OBJ, pool->object_size, fst);
#endif
}
Modified: branches/gmc/src/headers.c
==============================================================================
--- branches/gmc/src/headers.c (original)
+++ branches/gmc/src/headers.c Fri Aug 19 16:58:36 2005
@@ -212,6 +212,9 @@ make_bufferlike_pool(Interp *interpreter
if (sized_pools[idx] == NULL) {
sized_pools[idx] = new_bufferlike_pool(interpreter, buffer_size);
+ char *s = mem_sys_allocate(strlen("sized_pool()") + 6);
+ sprintf (s, "sized_pool(%d)", buffer_size);
+ sized_pools[idx]->name = s;
}
return sized_pools[idx];
@@ -458,6 +461,7 @@ Creates and returns a new C<Buffer>.
Buffer *
new_buffer_header(Interp *interpreter)
{
+ fprintf (stderr, "Getting buffer from buffer_header_pool\n");
return get_free_buffer(interpreter,
interpreter->arena_base->buffer_header_pool);
}
Modified: branches/gmc/src/jit_debug.c
==============================================================================
--- branches/gmc/src/jit_debug.c (original)
+++ branches/gmc/src/jit_debug.c Fri Aug 19 16:58:36 2005
@@ -163,7 +163,7 @@ write_types(FILE *stabs)
BIT_OFFSET(STRING, obj.u._b._buflen), BIT_SIZE(size_t),
#endif
#if PARROT_GC_GMC
- BIT_OFFSET(STRING, body->flags), BIT_SIZE(UINTVAL),
+ BIT_OFFSET(STRING, flags), BIT_SIZE(UINTVAL),
#else
BIT_OFFSET(STRING, obj.flags), BIT_SIZE(UINTVAL),
#endif
Modified: branches/gmc/src/memory.c
==============================================================================
--- branches/gmc/src/memory.c (original)
+++ branches/gmc/src/memory.c Fri Aug 19 16:58:36 2005
@@ -61,6 +61,7 @@ mem__internal_allocate(size_t size, cons
return ptr;
}
+
/*
=item C<void *
Modified: branches/gmc/src/stack_common.c
==============================================================================
--- branches/gmc/src/stack_common.c (original)
+++ branches/gmc/src/stack_common.c Fri Aug 19 16:58:36 2005
@@ -89,7 +89,7 @@ cst_new_stack_chunk(Parrot_Interp interp
struct Small_Object_Pool *pool;
pool = get_bufferlike_pool(interpreter, chunk->size);
- new_chunk = pool->get_free_object(interpreter, pool);
+ new_chunk = pool->get_free_sized_object(interpreter, pool,
sizeof(pobj_body));
PObj_bufstart(new_chunk) = NULL;
PObj_buflen (new_chunk) = 0;
Modified: branches/gmc/src/string.c
==============================================================================
--- branches/gmc/src/string.c (original)
+++ branches/gmc/src/string.c Fri Aug 19 16:58:36 2005
@@ -66,8 +66,8 @@ Parrot_unmake_COW(Interp *interpreter, S
/* COW_FLAG | constant_FLAG | external_FLAG) */
if (PObj_is_cowed_TESTALL(s)) {
#if PARROT_GC_GMC
- STRING *for_alloc_ptr = new_string_header(interpreter, 0);
- STRING for_alloc = *for_alloc_ptr;
+ STRING for_alloc;
+ PMC_body((PMC*)(&for_alloc)) =
mem_sys_allocate_zeroed(sizeof(pobj_body));
#else
STRING for_alloc;
#endif
@@ -94,6 +94,9 @@ Parrot_unmake_COW(Interp *interpreter, S
PObj_buflen(s) = PObj_buflen(&for_alloc);
/* COW_FLAG | external_FLAG | bufstart_external_FLAG immobile_FLAG */
PObj_is_external_CLEARALL(s);
+#if PARROT_GC_GMC
+ mem_sys_free(PMC_body((PMC*)(&for_alloc)));
+#endif
}
s->hashval = 0;
Modified: branches/gmc/t/native_pbc/integer_1.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/integer_2.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/integer_3.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/integer_4.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/number_1.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/number_2.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/number_3.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/number_4.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/number_5.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/string_1.pbc
==============================================================================
Binary files. No diff available.
Modified: branches/gmc/t/native_pbc/string_2.pbc
==============================================================================
Binary files. No diff available.