import re


def size_in_bytes(size_str, default):
    """
    Read a (memory) size as string and convert it to bytes.
    :param size_str: memory size as string. Allowed formats like "1024", "1G", "1g", "1.0 G", "1.0 GB"
    :param default: a fallback value as int
    :return:
    """
    if size_str is None and isinstance(default, int):
        return default

    result = re.match(r"^(\d+(?:\.\d+)?)\ *[tT]B?$", size_str)
    if result:
        return int(float(result.group(1)) * (1 << 40))

    result = re.match(r"^(\d+(?:\.\d+)?)\ *[gG]B?$", size_str)
    if result:
        return int(float(result.group(1)) * (1 << 30))

    # spark interprets "2048" as 2 GB
    result = re.match(r"^(\d+(?:\.\d+)?)\ *[mM]?B?$", size_str)
    if result:
        return int(float(result.group(1)) * (1 << 20))

    result = re.match(r"^(\d+(?:\.\d+)?)\ *[kK]B?$", size_str)
    if result:
        return int(float(result.group(1)) * 1024)

    result = re.match(r"^(\d+) *B$", size_str)
    if result:
        return int(result.group(1))
    else:
        raise ValueError(f"Invalid size: {size_str}")