From dbe138d5cc9ec9f7177c625111b18b253c71c5ca Mon Sep 17 00:00:00 2001 From: yui-knk Date: Sun, 16 Apr 2023 14:12:13 +0900 Subject: [PATCH] Split node management to node2 files --- common.mk | 3 +- node.c | 390 ----------------------------------------------------- node2.c | 397 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ node2.h | 0 4 files changed, 399 insertions(+), 391 deletions(-) create mode 100644 node2.c create mode 100644 node2.h diff --git a/common.mk b/common.mk index c6d0c6b50f6a1e..19f13742dc46b5 100644 --- a/common.mk +++ b/common.mk @@ -111,6 +111,7 @@ COMMONOBJS = array.$(OBJEXT) \ rjit.$(OBJEXT) \ rjit_c.$(OBJEXT) \ node.$(OBJEXT) \ + node2.$(OBJEXT) \ numeric.$(OBJEXT) \ object.$(OBJEXT) \ pack.$(OBJEXT) \ @@ -342,7 +343,7 @@ Doxyfile: $(srcdir)/template/Doxyfile.tmpl $(PREP) $(tooldir)/generic_erb.rb $(R libparser-so: $(ECHO) linking shared-library $@ - $(Q) $(LDSHARED) $(DLDFLAGS) node.o parse.o st.o $(OUTFLAG)$@ + $(Q) $(LDSHARED) $(DLDFLAGS) node2.o parse.o st.o $(OUTFLAG)$@ program: $(SHOWFLAGS) $(PROGRAM) wprogram: $(SHOWFLAGS) $(WPROGRAM) diff --git a/node.c b/node.c index 7a2e8e1db3f63b..4d57bc8f96f4c4 100644 --- a/node.c +++ b/node.c @@ -15,8 +15,6 @@ #include "ruby/ruby.h" #include "vm_core.h" -#define NODE_BUF_DEFAULT_LEN 16 - #define A(str) rb_str_cat2(buf, (str)) #define AR(str) rb_str_concat(buf, (str)) @@ -1122,391 +1120,3 @@ rb_parser_dump_tree(const NODE *node, int comment) dump_node(buf, rb_str_new_cstr("# "), comment, node); return buf; } - -/* Setup NODE structure. - * NODE is not an object managed by GC, but it imitates an object - * so that it can work with `RB_TYPE_P(obj, T_NODE)`. - * This dirty hack is needed because Ripper jumbles NODEs and other type - * objects. - */ -void -rb_node_init(NODE *n, enum node_type type, VALUE a0, VALUE a1, VALUE a2) -{ - n->flags = T_NODE; - nd_init_type(n, type); - n->u1.value = a0; - n->u2.value = a1; - n->u3.value = a2; - n->nd_loc.beg_pos.lineno = 0; - n->nd_loc.beg_pos.column = 0; - n->nd_loc.end_pos.lineno = 0; - n->nd_loc.end_pos.column = 0; - n->node_id = -1; -} - -typedef struct node_buffer_elem_struct { - struct node_buffer_elem_struct *next; - long len; - NODE buf[FLEX_ARY_LEN]; -} node_buffer_elem_t; - -typedef struct { - long idx, len; - node_buffer_elem_t *head; - node_buffer_elem_t *last; -} node_buffer_list_t; - -struct node_buffer_struct { - node_buffer_list_t unmarkable; - node_buffer_list_t markable; - struct rb_ast_local_table_link *local_tables; - VALUE mark_hash; - // - id (sequence number) - // - token_type - // - text of token - // - location info - // Array, whose entry is array - VALUE tokens; -}; - -static void -init_node_buffer_list(node_buffer_list_t * nb, node_buffer_elem_t *head) -{ - nb->idx = 0; - nb->len = NODE_BUF_DEFAULT_LEN; - nb->head = nb->last = head; - nb->head->len = nb->len; - nb->head->next = NULL; -} - -static node_buffer_t * -rb_node_buffer_new(void) -{ - const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE); - const size_t alloc_size = sizeof(node_buffer_t) + (bucket_size * 2); - STATIC_ASSERT( - integer_overflow, - offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE) - > sizeof(node_buffer_t) + 2 * sizeof(node_buffer_elem_t)); - node_buffer_t *nb = ruby_xmalloc(alloc_size); - init_node_buffer_list(&nb->unmarkable, (node_buffer_elem_t*)&nb[1]); - init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size)); - nb->local_tables = 0; - nb->mark_hash = Qnil; - nb->tokens = Qnil; - return nb; -} - -static void -node_buffer_list_free(node_buffer_list_t * nb) -{ - node_buffer_elem_t *nbe = nb->head; - - while (nbe != nb->last) { - void *buf = nbe; - nbe = nbe->next; - xfree(buf); - } -} - -struct rb_ast_local_table_link { - struct rb_ast_local_table_link *next; - // struct rb_ast_id_table { - int size; - ID ids[FLEX_ARY_LEN]; - // } -}; - -static void -rb_node_buffer_free(node_buffer_t *nb) -{ - node_buffer_list_free(&nb->unmarkable); - node_buffer_list_free(&nb->markable); - struct rb_ast_local_table_link *local_table = nb->local_tables; - while (local_table) { - struct rb_ast_local_table_link *next_table = local_table->next; - xfree(local_table); - local_table = next_table; - } - xfree(nb); -} - -static NODE * -ast_newnode_in_bucket(node_buffer_list_t *nb) -{ - if (nb->idx >= nb->len) { - long n = nb->len * 2; - node_buffer_elem_t *nbe; - nbe = rb_xmalloc_mul_add(n, sizeof(NODE), offsetof(node_buffer_elem_t, buf)); - nbe->len = n; - nb->idx = 0; - nb->len = n; - nbe->next = nb->head; - nb->head = nbe; - } - return &nb->head->buf[nb->idx++]; -} - -RBIMPL_ATTR_PURE() -static bool -nodetype_markable_p(enum node_type type) -{ - switch (type) { - case NODE_MATCH: - case NODE_LIT: - case NODE_STR: - case NODE_XSTR: - case NODE_DSTR: - case NODE_DXSTR: - case NODE_DREGX: - case NODE_DSYM: - case NODE_ARGS: - case NODE_ARYPTN: - case NODE_FNDPTN: - return true; - default: - return false; - } -} - -const char * -ruby_node_name(int node) -{ - switch (node) { -#include "node_name.inc" - default: - rb_bug("unknown node: %d", node); - return 0; - } -} - -NODE * -rb_ast_newnode(rb_ast_t *ast, enum node_type type) -{ - node_buffer_t *nb = ast->node_buffer; - node_buffer_list_t *bucket = - (nodetype_markable_p(type) ? &nb->markable : &nb->unmarkable); - return ast_newnode_in_bucket(bucket); -} - -void -rb_ast_node_type_change(NODE *n, enum node_type type) -{ - enum node_type old_type = nd_type(n); - if (nodetype_markable_p(old_type) != nodetype_markable_p(type)) { - rb_bug("node type changed: %s -> %s", - ruby_node_name(old_type), ruby_node_name(type)); - } -} - -rb_ast_id_table_t * -rb_ast_new_local_table(rb_ast_t *ast, int size) -{ - size_t alloc_size = sizeof(struct rb_ast_local_table_link) + size * sizeof(ID); - struct rb_ast_local_table_link *link = ruby_xmalloc(alloc_size); - link->next = ast->node_buffer->local_tables; - ast->node_buffer->local_tables = link; - link->size = size; - - return (rb_ast_id_table_t *) &link->size; -} - -rb_ast_id_table_t * -rb_ast_resize_latest_local_table(rb_ast_t *ast, int size) -{ - struct rb_ast_local_table_link *link = ast->node_buffer->local_tables; - size_t alloc_size = sizeof(struct rb_ast_local_table_link) + size * sizeof(ID); - link = ruby_xrealloc(link, alloc_size); - ast->node_buffer->local_tables = link; - link->size = size; - - return (rb_ast_id_table_t *) &link->size; -} - -void -rb_ast_delete_node(rb_ast_t *ast, NODE *n) -{ - (void)ast; - (void)n; - /* should we implement freelist? */ -} - -rb_ast_t * -rb_ast_new(void) -{ - node_buffer_t *nb = rb_node_buffer_new(); - rb_ast_t *ast = (rb_ast_t *)rb_imemo_new(imemo_ast, 0, 0, 0, (VALUE)nb); - return ast; -} - -typedef void node_itr_t(void *ctx, NODE * node); - -static void -iterate_buffer_elements(node_buffer_elem_t *nbe, long len, node_itr_t *func, void *ctx) -{ - long cursor; - for (cursor = 0; cursor < len; cursor++) { - func(ctx, &nbe->buf[cursor]); - } -} - -static void -iterate_node_values(node_buffer_list_t *nb, node_itr_t * func, void *ctx) -{ - node_buffer_elem_t *nbe = nb->head; - - /* iterate over the head first because it's not full */ - iterate_buffer_elements(nbe, nb->idx, func, ctx); - - nbe = nbe->next; - while (nbe) { - iterate_buffer_elements(nbe, nbe->len, func, ctx); - nbe = nbe->next; - } -} - -static void -mark_ast_value(void *ctx, NODE * node) -{ - switch (nd_type(node)) { - case NODE_ARGS: - { - struct rb_args_info *args = node->nd_ainfo; - rb_gc_mark_movable(args->imemo); - break; - } - case NODE_MATCH: - case NODE_LIT: - case NODE_STR: - case NODE_XSTR: - case NODE_DSTR: - case NODE_DXSTR: - case NODE_DREGX: - case NODE_DSYM: - rb_gc_mark_movable(node->nd_lit); - break; - case NODE_ARYPTN: - case NODE_FNDPTN: - rb_gc_mark_movable(node->nd_rval); - break; - default: - rb_bug("unreachable node %s", ruby_node_name(nd_type(node))); - } -} - -static void -update_ast_value(void *ctx, NODE * node) -{ - switch (nd_type(node)) { - case NODE_ARGS: - { - struct rb_args_info *args = node->nd_ainfo; - args->imemo = rb_gc_location(args->imemo); - break; - } - case NODE_MATCH: - case NODE_LIT: - case NODE_STR: - case NODE_XSTR: - case NODE_DSTR: - case NODE_DXSTR: - case NODE_DREGX: - case NODE_DSYM: - node->nd_lit = rb_gc_location(node->nd_lit); - break; - case NODE_ARYPTN: - case NODE_FNDPTN: - node->nd_rval = rb_gc_location(node->nd_rval); - break; - default: - rb_bug("unreachable"); - } -} - -void -rb_ast_update_references(rb_ast_t *ast) -{ - if (ast->node_buffer) { - node_buffer_t *nb = ast->node_buffer; - - iterate_node_values(&nb->markable, update_ast_value, NULL); - } -} - -void -rb_ast_mark(rb_ast_t *ast) -{ - if (ast->node_buffer) { - rb_gc_mark(ast->node_buffer->mark_hash); - rb_gc_mark(ast->node_buffer->tokens); - } - if (ast->body.compile_option) rb_gc_mark(ast->body.compile_option); - if (ast->node_buffer) { - node_buffer_t *nb = ast->node_buffer; - - iterate_node_values(&nb->markable, mark_ast_value, NULL); - } - if (ast->body.script_lines) rb_gc_mark(ast->body.script_lines); -} - -void -rb_ast_free(rb_ast_t *ast) -{ - if (ast->node_buffer) { - rb_node_buffer_free(ast->node_buffer); - ast->node_buffer = 0; - } -} - -static size_t -buffer_list_size(node_buffer_list_t *nb) -{ - size_t size = 0; - node_buffer_elem_t *nbe = nb->head; - while (nbe != nb->last) { - nbe = nbe->next; - size += offsetof(node_buffer_elem_t, buf) + nb->len * sizeof(NODE); - } - return size; -} - -size_t -rb_ast_memsize(const rb_ast_t *ast) -{ - size_t size = 0; - node_buffer_t *nb = ast->node_buffer; - - if (nb) { - size += sizeof(node_buffer_t) + offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE); - size += buffer_list_size(&nb->unmarkable); - size += buffer_list_size(&nb->markable); - } - return size; -} - -void -rb_ast_dispose(rb_ast_t *ast) -{ - rb_ast_free(ast); -} - -void -rb_ast_add_mark_object(rb_ast_t *ast, VALUE obj) -{ - if (NIL_P(ast->node_buffer->mark_hash)) { - RB_OBJ_WRITE(ast, &ast->node_buffer->mark_hash, rb_ident_hash_new()); - } - rb_hash_aset(ast->node_buffer->mark_hash, obj, Qtrue); -} - -VALUE -rb_ast_tokens(rb_ast_t *ast) -{ - return ast->node_buffer->tokens; -} - -void -rb_ast_set_tokens(rb_ast_t *ast, VALUE tokens) -{ - RB_OBJ_WRITE(ast, &ast->node_buffer->tokens, tokens); -} diff --git a/node2.c b/node2.c new file mode 100644 index 00000000000000..6e94ae1d37b95e --- /dev/null +++ b/node2.c @@ -0,0 +1,397 @@ +#include "external/node.h" +#include "internal.h" +#include "internal/hash.h" +#include "internal/variable.h" +#include "ruby/ruby.h" +#include "vm_core.h" +#include "node.h" + +#define NODE_BUF_DEFAULT_LEN 16 + +/* Setup NODE structure. + * NODE is not an object managed by GC, but it imitates an object + * so that it can work with `RB_TYPE_P(obj, T_NODE)`. + * This dirty hack is needed because Ripper jumbles NODEs and other type + * objects. + */ +void +rb_node_init(NODE *n, enum node_type type, VALUE a0, VALUE a1, VALUE a2) +{ + n->flags = T_NODE; + nd_init_type(n, type); + n->u1.value = a0; + n->u2.value = a1; + n->u3.value = a2; + n->nd_loc.beg_pos.lineno = 0; + n->nd_loc.beg_pos.column = 0; + n->nd_loc.end_pos.lineno = 0; + n->nd_loc.end_pos.column = 0; + n->node_id = -1; +} + +typedef struct node_buffer_elem_struct { + struct node_buffer_elem_struct *next; + long len; + NODE buf[FLEX_ARY_LEN]; +} node_buffer_elem_t; + +typedef struct { + long idx, len; + node_buffer_elem_t *head; + node_buffer_elem_t *last; +} node_buffer_list_t; + +struct node_buffer_struct { + node_buffer_list_t unmarkable; + node_buffer_list_t markable; + struct rb_ast_local_table_link *local_tables; + VALUE mark_hash; + // - id (sequence number) + // - token_type + // - text of token + // - location info + // Array, whose entry is array + VALUE tokens; +}; + +static void +init_node_buffer_list(node_buffer_list_t * nb, node_buffer_elem_t *head) +{ + nb->idx = 0; + nb->len = NODE_BUF_DEFAULT_LEN; + nb->head = nb->last = head; + nb->head->len = nb->len; + nb->head->next = NULL; +} + +static node_buffer_t * +rb_node_buffer_new(void) +{ + const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE); + const size_t alloc_size = sizeof(node_buffer_t) + (bucket_size * 2); + STATIC_ASSERT( + integer_overflow, + offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE) + > sizeof(node_buffer_t) + 2 * sizeof(node_buffer_elem_t)); + node_buffer_t *nb = ruby_xmalloc(alloc_size); + init_node_buffer_list(&nb->unmarkable, (node_buffer_elem_t*)&nb[1]); + init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size)); + nb->local_tables = 0; + nb->mark_hash = Qnil; + nb->tokens = Qnil; + return nb; +} + +static void +node_buffer_list_free(node_buffer_list_t * nb) +{ + node_buffer_elem_t *nbe = nb->head; + + while (nbe != nb->last) { + void *buf = nbe; + nbe = nbe->next; + xfree(buf); + } +} + +struct rb_ast_local_table_link { + struct rb_ast_local_table_link *next; + // struct rb_ast_id_table { + int size; + ID ids[FLEX_ARY_LEN]; + // } +}; + +static void +rb_node_buffer_free(node_buffer_t *nb) +{ + node_buffer_list_free(&nb->unmarkable); + node_buffer_list_free(&nb->markable); + struct rb_ast_local_table_link *local_table = nb->local_tables; + while (local_table) { + struct rb_ast_local_table_link *next_table = local_table->next; + xfree(local_table); + local_table = next_table; + } + xfree(nb); +} + +static NODE * +ast_newnode_in_bucket(node_buffer_list_t *nb) +{ + if (nb->idx >= nb->len) { + long n = nb->len * 2; + node_buffer_elem_t *nbe; + nbe = rb_xmalloc_mul_add(n, sizeof(NODE), offsetof(node_buffer_elem_t, buf)); + nbe->len = n; + nb->idx = 0; + nb->len = n; + nbe->next = nb->head; + nb->head = nbe; + } + return &nb->head->buf[nb->idx++]; +} + +RBIMPL_ATTR_PURE() +static bool +nodetype_markable_p(enum node_type type) +{ + switch (type) { + case NODE_MATCH: + case NODE_LIT: + case NODE_STR: + case NODE_XSTR: + case NODE_DSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_DSYM: + case NODE_ARGS: + case NODE_ARYPTN: + case NODE_FNDPTN: + return true; + default: + return false; + } +} + +const char * +ruby_node_name(int node) +{ + switch (node) { +#include "node_name.inc" + default: + rb_bug("unknown node: %d", node); + return 0; + } +} + +NODE * +rb_ast_newnode(rb_ast_t *ast, enum node_type type) +{ + node_buffer_t *nb = ast->node_buffer; + node_buffer_list_t *bucket = + (nodetype_markable_p(type) ? &nb->markable : &nb->unmarkable); + return ast_newnode_in_bucket(bucket); +} + +void +rb_ast_node_type_change(NODE *n, enum node_type type) +{ + enum node_type old_type = nd_type(n); + if (nodetype_markable_p(old_type) != nodetype_markable_p(type)) { + rb_bug("node type changed: %s -> %s", + ruby_node_name(old_type), ruby_node_name(type)); + } +} + +rb_ast_id_table_t * +rb_ast_new_local_table(rb_ast_t *ast, int size) +{ + size_t alloc_size = sizeof(struct rb_ast_local_table_link) + size * sizeof(ID); + struct rb_ast_local_table_link *link = ruby_xmalloc(alloc_size); + link->next = ast->node_buffer->local_tables; + ast->node_buffer->local_tables = link; + link->size = size; + + return (rb_ast_id_table_t *) &link->size; +} + +rb_ast_id_table_t * +rb_ast_resize_latest_local_table(rb_ast_t *ast, int size) +{ + struct rb_ast_local_table_link *link = ast->node_buffer->local_tables; + size_t alloc_size = sizeof(struct rb_ast_local_table_link) + size * sizeof(ID); + link = ruby_xrealloc(link, alloc_size); + ast->node_buffer->local_tables = link; + link->size = size; + + return (rb_ast_id_table_t *) &link->size; +} + +void +rb_ast_delete_node(rb_ast_t *ast, NODE *n) +{ + (void)ast; + (void)n; + /* should we implement freelist? */ +} + +rb_ast_t * +rb_ast_new(void) +{ + node_buffer_t *nb = rb_node_buffer_new(); + rb_ast_t *ast = (rb_ast_t *)rb_imemo_new(imemo_ast, 0, 0, 0, (VALUE)nb); + return ast; +} + +typedef void node_itr_t(void *ctx, NODE * node); + +static void +iterate_buffer_elements(node_buffer_elem_t *nbe, long len, node_itr_t *func, void *ctx) +{ + long cursor; + for (cursor = 0; cursor < len; cursor++) { + func(ctx, &nbe->buf[cursor]); + } +} + +static void +iterate_node_values(node_buffer_list_t *nb, node_itr_t * func, void *ctx) +{ + node_buffer_elem_t *nbe = nb->head; + + /* iterate over the head first because it's not full */ + iterate_buffer_elements(nbe, nb->idx, func, ctx); + + nbe = nbe->next; + while (nbe) { + iterate_buffer_elements(nbe, nbe->len, func, ctx); + nbe = nbe->next; + } +} + +static void +mark_ast_value(void *ctx, NODE * node) +{ + switch (nd_type(node)) { + case NODE_ARGS: + { + struct rb_args_info *args = node->nd_ainfo; + rb_gc_mark_movable(args->imemo); + break; + } + case NODE_MATCH: + case NODE_LIT: + case NODE_STR: + case NODE_XSTR: + case NODE_DSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_DSYM: + rb_gc_mark_movable(node->nd_lit); + break; + case NODE_ARYPTN: + case NODE_FNDPTN: + rb_gc_mark_movable(node->nd_rval); + break; + default: + rb_bug("unreachable node %s", ruby_node_name(nd_type(node))); + } +} + +static void +update_ast_value(void *ctx, NODE * node) +{ + switch (nd_type(node)) { + case NODE_ARGS: + { + struct rb_args_info *args = node->nd_ainfo; + args->imemo = rb_gc_location(args->imemo); + break; + } + case NODE_MATCH: + case NODE_LIT: + case NODE_STR: + case NODE_XSTR: + case NODE_DSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_DSYM: + node->nd_lit = rb_gc_location(node->nd_lit); + break; + case NODE_ARYPTN: + case NODE_FNDPTN: + node->nd_rval = rb_gc_location(node->nd_rval); + break; + default: + rb_bug("unreachable"); + } +} + +void +rb_ast_update_references(rb_ast_t *ast) +{ + if (ast->node_buffer) { + node_buffer_t *nb = ast->node_buffer; + + iterate_node_values(&nb->markable, update_ast_value, NULL); + } +} + +void +rb_ast_mark(rb_ast_t *ast) +{ + if (ast->node_buffer) { + rb_gc_mark(ast->node_buffer->mark_hash); + rb_gc_mark(ast->node_buffer->tokens); + } + if (ast->body.compile_option) rb_gc_mark(ast->body.compile_option); + if (ast->node_buffer) { + node_buffer_t *nb = ast->node_buffer; + + iterate_node_values(&nb->markable, mark_ast_value, NULL); + } + if (ast->body.script_lines) rb_gc_mark(ast->body.script_lines); +} + +void +rb_ast_free(rb_ast_t *ast) +{ + if (ast->node_buffer) { + rb_node_buffer_free(ast->node_buffer); + ast->node_buffer = 0; + } +} + +static size_t +buffer_list_size(node_buffer_list_t *nb) +{ + size_t size = 0; + node_buffer_elem_t *nbe = nb->head; + while (nbe != nb->last) { + nbe = nbe->next; + size += offsetof(node_buffer_elem_t, buf) + nb->len * sizeof(NODE); + } + return size; +} + +size_t +rb_ast_memsize(const rb_ast_t *ast) +{ + size_t size = 0; + node_buffer_t *nb = ast->node_buffer; + + if (nb) { + size += sizeof(node_buffer_t) + offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE); + size += buffer_list_size(&nb->unmarkable); + size += buffer_list_size(&nb->markable); + } + return size; +} + +void +rb_ast_dispose(rb_ast_t *ast) +{ + rb_ast_free(ast); +} + +void +rb_ast_add_mark_object(rb_ast_t *ast, VALUE obj) +{ + if (NIL_P(ast->node_buffer->mark_hash)) { + RB_OBJ_WRITE(ast, &ast->node_buffer->mark_hash, rb_ident_hash_new()); + } + rb_hash_aset(ast->node_buffer->mark_hash, obj, Qtrue); +} + +VALUE +rb_ast_tokens(rb_ast_t *ast) +{ + return ast->node_buffer->tokens; +} + +void +rb_ast_set_tokens(rb_ast_t *ast, VALUE tokens) +{ + RB_OBJ_WRITE(ast, &ast->node_buffer->tokens, tokens); +} diff --git a/node2.h b/node2.h new file mode 100644 index 00000000000000..e69de29bb2d1d6