in erts/emulator/beam/copy.c [298:601]
Uint size_shared(Eterm obj)
{
Eterm saved_obj = obj;
Uint sum = 0;
Eterm* ptr;
DECLARE_EQUEUE(s);
DECLARE_BITSTORE(b);
for (;;) {
switch (primary_tag(obj)) {
case TAG_PRIMARY_LIST: {
Eterm head, tail;
ptr = list_val(obj);
/* we're not counting anything that's outside our heap */
if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) {
goto pop_next;
}
head = CAR(ptr);
tail = CDR(ptr);
/* if it's visited, don't count it */
if (primary_tag(tail) == TAG_PRIMARY_HEADER ||
primary_tag(head) == TAG_PRIMARY_HEADER) {
goto pop_next;
}
/* else make it visited now */
switch (primary_tag(tail)) {
case TAG_PRIMARY_LIST:
ptr[1] = (tail - TAG_PRIMARY_LIST) | TAG_PRIMARY_HEADER;
break;
case TAG_PRIMARY_IMMED1:
CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER;
CDR(ptr) = (tail - TAG_PRIMARY_IMMED1) | primary_tag(head);
break;
case TAG_PRIMARY_BOXED:
BITSTORE_PUT(b, primary_tag(head));
CAR(ptr) = (head - primary_tag(head)) | TAG_PRIMARY_HEADER;
CDR(ptr) = (tail - TAG_PRIMARY_BOXED) | TAG_PRIMARY_HEADER;
break;
}
/* and count it */
sum += 2;
if (!IS_CONST(head)) {
EQUEUE_PUT(s, head);
}
obj = tail;
break;
}
case TAG_PRIMARY_BOXED: {
Eterm hdr;
ptr = boxed_val(obj);
/* we're not counting anything that's outside our heap */
if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) {
goto pop_next;
}
hdr = *ptr;
/* if it's visited, don't count it */
if (primary_tag(hdr) != TAG_PRIMARY_HEADER) {
goto pop_next;
}
/* else make it visited now */
*ptr = (hdr - primary_tag(hdr)) + BOXED_VISITED;
/* and count it */
ASSERT(is_header(hdr));
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG: {
int arity = header_arity(hdr);
sum += arity + 1;
if (arity == 0) { /* Empty tuple -- unusual. */
goto pop_next;
}
while (arity-- > 0) {
obj = *++ptr;
if (!IS_CONST(obj)) {
EQUEUE_PUT(s, obj);
}
}
goto pop_next;
}
case FUN_SUBTAG: {
ErlFunThing* funp = (ErlFunThing *) ptr;
unsigned eterms = 1 /* creator */ + funp->num_free;
unsigned sz = thing_arityval(hdr);
sum += 1 /* header */ + sz + eterms;
ptr += 1 /* header */ + sz;
while (eterms-- > 0) {
obj = *ptr++;
if (!IS_CONST(obj)) {
EQUEUE_PUT(s, obj);
}
}
goto pop_next;
}
case SUB_BINARY_SUBTAG: {
ErlSubBin* sb = (ErlSubBin *) ptr;
Uint extra_bytes;
Eterm hdr;
ASSERT((sb->thing_word & ~BOXED_VISITED_MASK) == HEADER_SUB_BIN);
if (sb->bitsize + sb->bitoffs > 8) {
sum += ERL_SUB_BIN_SIZE;
extra_bytes = 2;
} else if (sb->bitsize + sb->bitoffs > 0) {
sum += ERL_SUB_BIN_SIZE;
extra_bytes = 1;
} else {
extra_bytes = 0;
}
ptr = binary_val(sb->orig);
hdr = (*ptr) & ~BOXED_VISITED_MASK;
if (thing_subtag(hdr) == REFC_BINARY_SUBTAG) {
sum += PROC_BIN_SIZE;
} else {
ASSERT(thing_subtag(hdr) == HEAP_BINARY_SUBTAG);
sum += heap_bin_size(binary_size(obj) + extra_bytes);
}
goto pop_next;
}
case MAP_SUBTAG:
switch (MAP_HEADER_TYPE(hdr)) {
case MAP_HEADER_TAG_FLATMAP_HEAD : {
flatmap_t *mp = (flatmap_t*)flatmap_val(obj);
Uint n = flatmap_get_size(mp) + 1;
ptr = (Eterm *)mp;
sum += n + 2;
ptr += 2; /* hdr + size words */
while (n--) {
obj = *ptr++;
if (!IS_CONST(obj)) {
EQUEUE_PUT(s, obj);
}
}
goto pop_next;
}
case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
case MAP_HEADER_TAG_HAMT_NODE_BITMAP : {
Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr));
sum += 1 + n + header_arity(hdr);
ptr += 1 + header_arity(hdr);
while (n--) {
obj = *ptr++;
if (!IS_CONST(obj)) {
EQUEUE_PUT(s, obj);
}
}
goto pop_next;
}
default:
erts_exit(ERTS_ABORT_EXIT, "size_shared: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
}
case BIN_MATCHSTATE_SUBTAG:
erts_exit(ERTS_ABORT_EXIT,
"size_shared: matchstate term not allowed");
default:
sum += thing_arityval(hdr) + 1;
goto pop_next;
}
break;
}
case TAG_PRIMARY_IMMED1:
pop_next:
if (EQUEUE_ISEMPTY(s)) {
goto cleanup;
}
obj = EQUEUE_GET(s);
break;
default:
erts_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj);
}
}
cleanup:
obj = saved_obj;
BITSTORE_CLOSE(b);
for (;;) {
switch (primary_tag(obj)) {
case TAG_PRIMARY_LIST: {
Eterm head, tail;
ptr = list_val(obj);
if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) {
goto cleanup_next;
}
head = CAR(ptr);
tail = CDR(ptr);
/* if not already clean, clean it up */
if (primary_tag(tail) == TAG_PRIMARY_HEADER) {
if (primary_tag(head) == TAG_PRIMARY_HEADER) {
Eterm saved;
BITSTORE_FETCH(b, saved);
CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | saved;
CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_BOXED;
} else {
CDR(ptr) = tail = (tail - TAG_PRIMARY_HEADER) | TAG_PRIMARY_LIST;
}
} else if (primary_tag(head) == TAG_PRIMARY_HEADER) {
CAR(ptr) = head = (head - TAG_PRIMARY_HEADER) | primary_tag(tail);
CDR(ptr) = tail = (tail - primary_tag(tail)) | TAG_PRIMARY_IMMED1;
} else {
goto cleanup_next;
}
/* and its children too */
if (!IS_CONST(head)) {
EQUEUE_PUT_UNCHECKED(s, head);
}
obj = tail;
break;
}
case TAG_PRIMARY_BOXED: {
Eterm hdr;
ptr = boxed_val(obj);
if (!COUNT_OFF_HEAP && erts_is_literal(obj,ptr)) {
goto cleanup_next;
}
hdr = *ptr;
/* if not already clean, clean it up */
if (primary_tag(hdr) == TAG_PRIMARY_HEADER) {
goto cleanup_next;
}
else {
ASSERT(primary_tag(hdr) == BOXED_VISITED);
*ptr = hdr = (hdr - BOXED_VISITED) + TAG_PRIMARY_HEADER;
}
/* and its children too */
switch (hdr & _TAG_HEADER_MASK) {
case ARITYVAL_SUBTAG: {
int arity = header_arity(hdr);
if (arity == 0) { /* Empty tuple -- unusual. */
goto cleanup_next;
}
while (arity-- > 0) {
obj = *++ptr;
if (!IS_CONST(obj)) {
EQUEUE_PUT_UNCHECKED(s, obj);
}
}
goto cleanup_next;
}
case FUN_SUBTAG: {
ErlFunThing* funp = (ErlFunThing *) ptr;
unsigned eterms = 1 /* creator */ + funp->num_free;
unsigned sz = thing_arityval(hdr);
ptr += 1 /* header */ + sz;
while (eterms-- > 0) {
obj = *ptr++;
if (!IS_CONST(obj)) {
EQUEUE_PUT_UNCHECKED(s, obj);
}
}
goto cleanup_next;
}
case MAP_SUBTAG:
switch (MAP_HEADER_TYPE(hdr)) {
case MAP_HEADER_TAG_FLATMAP_HEAD : {
flatmap_t *mp = (flatmap_t *) ptr;
Uint n = flatmap_get_size(mp) + 1;
ptr += 2; /* hdr + size words */
while (n--) {
obj = *ptr++;
if (!IS_CONST(obj)) {
EQUEUE_PUT_UNCHECKED(s, obj);
}
}
goto cleanup_next;
}
case MAP_HEADER_TAG_HAMT_HEAD_BITMAP :
case MAP_HEADER_TAG_HAMT_HEAD_ARRAY :
case MAP_HEADER_TAG_HAMT_NODE_BITMAP : {
Uint n = hashmap_bitcount(MAP_HEADER_VAL(hdr));
sum += 1 + n + header_arity(hdr);
ptr += 1 + header_arity(hdr);
while (n--) {
obj = *ptr++;
if (!IS_CONST(obj)) {
EQUEUE_PUT_UNCHECKED(s, obj);
}
}
goto cleanup_next;
}
default:
erts_exit(ERTS_ABORT_EXIT, "size_shared: bad hashmap type %d\n", MAP_HEADER_TYPE(hdr));
}
default:
goto cleanup_next;
}
break;
}
case TAG_PRIMARY_IMMED1:
cleanup_next:
if (EQUEUE_ISEMPTY(s)) {
goto all_clean;
}
obj = EQUEUE_GET(s);
break;
default:
erts_exit(ERTS_ABORT_EXIT, "size_shared: bad tag for %#x\n", obj);
}
}
all_clean:
/* Return the result */
DESTROY_EQUEUE(s);
DESTROY_BITSTORE(b);
return sum;
}