Uint size_shared()

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;
}