fuzzer_parser_init:
   33|  1.86k|readstat_parser_t *fuzzer_parser_init(const uint8_t *Data, size_t Size) {
   34|  1.86k|    readstat_parser_t *parser = readstat_parser_init();
   35|  1.86k|    readstat_set_open_handler(parser, rt_open_handler);
   36|  1.86k|    readstat_set_close_handler(parser, rt_close_handler);
   37|  1.86k|    readstat_set_seek_handler(parser, rt_seek_handler);
   38|  1.86k|    readstat_set_read_handler(parser, rt_read_handler);
   39|  1.86k|    readstat_set_update_handler(parser, rt_update_handler);
   40|       |
   41|  1.86k|    readstat_set_metadata_handler(parser, &handle_metadata);
   42|  1.86k|    readstat_set_note_handler(parser, &handle_note);
   43|  1.86k|    readstat_set_variable_handler(parser, &handle_variable);
   44|  1.86k|    readstat_set_fweight_handler(parser, &handle_fweight);
   45|  1.86k|    readstat_set_value_handler(parser, &handle_value);
   46|  1.86k|    readstat_set_value_label_handler(parser, &handle_value_label);
   47|       |
   48|  1.86k|    return parser;
   49|  1.86k|}
fuzz_format.c:handle_metadata:
    8|  1.28k|static int handle_metadata(readstat_metadata_t *metadata, void *ctx) {
    9|  1.28k|    return READSTAT_HANDLER_OK;
   10|  1.28k|}
fuzz_format.c:handle_value_label:
   29|  24.5k|static int handle_value_label(const char *val_labels, readstat_value_t value, const char *label, void *ctx) {
   30|  24.5k|    return READSTAT_HANDLER_OK;
   31|  24.5k|}

LLVMFuzzerTestOneInput:
   10|  1.86k|int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
   11|  1.86k|    rt_buffer_t buffer = { .bytes = (char *)Data, .size = Size, .used = Size };
   12|  1.86k|    rt_buffer_ctx_t buffer_ctx = { .buffer = &buffer };
   13|       |
   14|  1.86k|    readstat_parser_t *parser = fuzzer_parser_init(Data, Size);
   15|  1.86k|    readstat_set_io_ctx(parser, &buffer_ctx);
   16|       |
   17|  1.86k|    readstat_parse_sas7bcat(parser, NULL, NULL);
   18|  1.86k|    readstat_parser_free(parser);
   19|  1.86k|    return 0;
   20|  1.86k|}

machine_is_little_endian:
   11|  4.59k|int machine_is_little_endian(void) {
   12|  4.59k|    int test_byte_order = 1;
   13|  4.59k|    return ((char *)&test_byte_order)[0];
   14|  4.59k|}
byteswap2:
   40|  13.3k|uint16_t byteswap2(uint16_t num) {
   41|  13.3k|    return ((num & 0xFF00) >> 8) | ((num & 0x00FF) << 8);
   42|  13.3k|}
byteswap4:
   44|  6.77k|uint32_t byteswap4(uint32_t num) {
   45|  6.77k|    num = ((num & 0xFFFF0000) >> 16) | ((num & 0x0000FFFF) << 16);
   46|  6.77k|    return ((num & 0xFF00FF00) >> 8) | ((num & 0x00FF00FF) << 8);
   47|  6.77k|}
byteswap8:
   49|  25.6k|uint64_t byteswap8(uint64_t num) {
   50|  25.6k|    num = ((num & 0xFFFFFFFF00000000) >> 32) | ((num & 0x00000000FFFFFFFF) << 32);
   51|  25.6k|    num = ((num & 0xFFFF0000FFFF0000) >> 16) | ((num & 0x0000FFFF0000FFFF) << 16);
   52|  25.6k|    return ((num & 0xFF00FF00FF00FF00) >> 8) | ((num & 0x00FF00FF00FF00FF) << 8);
   53|  25.6k|}
byteswap_double:
   63|  1.67k|double byteswap_double(double num) {
   64|  1.67k|    uint64_t answer = 0;
   65|  1.67k|    memcpy(&answer, &num, 8);
   66|  1.67k|    answer = byteswap8(answer);
   67|  1.67k|    memcpy(&num, &answer, 8);
   68|  1.67k|    return num;
   69|  1.67k|}

readstat_convert:
    7|  30.5k|readstat_error_t readstat_convert(char *dst, size_t dst_len, const char *src, size_t src_len, iconv_t converter) {
    8|       |    /* strip off spaces from the input because the programs use ASCII space
    9|       |     * padding even with non-ASCII encoding. */
   10|   174k|    while (src_len && (src[src_len-1] == ' ' || src[src_len-1] == '\0')) {
  ------------------
  |  Branch (10:12): [True: 149k, False: 24.6k]
  |  Branch (10:24): [True: 1.33k, False: 148k]
  |  Branch (10:49): [True: 142k, False: 5.90k]
  ------------------
   11|   143k|        src_len--;
   12|   143k|    }
   13|  30.5k|    if (dst_len == 0) {
  ------------------
  |  Branch (13:9): [True: 0, False: 30.5k]
  ------------------
   14|      0|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   15|  30.5k|    } else if (converter) {
  ------------------
  |  Branch (15:16): [True: 7.43k, False: 23.1k]
  ------------------
   16|  7.43k|        size_t dst_left = dst_len - 1;
   17|  7.43k|        char *dst_end = dst;
   18|  7.43k|        size_t status = iconv(converter, (readstat_iconv_inbuf_t)&src, &src_len, &dst_end, &dst_left);
   19|  7.43k|        if (status == (size_t)-1) {
  ------------------
  |  Branch (19:13): [True: 348, False: 7.08k]
  ------------------
   20|    348|            if (errno == E2BIG) {
  ------------------
  |  Branch (20:17): [True: 1, False: 347]
  ------------------
   21|      1|                return READSTAT_ERROR_CONVERT_LONG_STRING;
   22|    347|            } else if (errno == EILSEQ) {
  ------------------
  |  Branch (22:24): [True: 56, False: 291]
  ------------------
   23|     56|                return READSTAT_ERROR_CONVERT_BAD_STRING;
   24|    291|            } else if (errno != EINVAL) { /* EINVAL indicates improper truncation; accept it */
  ------------------
  |  Branch (24:24): [True: 0, False: 291]
  ------------------
   25|      0|                return READSTAT_ERROR_CONVERT;
   26|      0|            }
   27|    348|        }
   28|  7.37k|        dst[dst_len - dst_left - 1] = '\0';
   29|  23.1k|    } else if (src_len + 1 > dst_len) {
  ------------------
  |  Branch (29:16): [True: 0, False: 23.1k]
  ------------------
   30|      0|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   31|  23.1k|    } else {
   32|  23.1k|        memcpy(dst, src, src_len);
   33|  23.1k|        dst[src_len] = '\0';
   34|  23.1k|    }
   35|  30.5k|    return READSTAT_OK;
   36|  30.5k|}

unistd_io_init:
  121|  1.86k|readstat_error_t unistd_io_init(readstat_parser_t *parser) {
  122|  1.86k|    readstat_error_t retval = READSTAT_OK;
  123|  1.86k|    unistd_io_ctx_t *io_ctx = NULL;
  124|       |
  125|  1.86k|    if ((retval = readstat_set_open_handler(parser, unistd_open_handler)) != READSTAT_OK)
  ------------------
  |  Branch (125:9): [True: 0, False: 1.86k]
  ------------------
  126|      0|        return retval;
  127|       |
  128|  1.86k|    if ((retval = readstat_set_close_handler(parser, unistd_close_handler)) != READSTAT_OK)
  ------------------
  |  Branch (128:9): [True: 0, False: 1.86k]
  ------------------
  129|      0|        return retval;
  130|       |
  131|  1.86k|    if ((retval = readstat_set_seek_handler(parser, unistd_seek_handler)) != READSTAT_OK)
  ------------------
  |  Branch (131:9): [True: 0, False: 1.86k]
  ------------------
  132|      0|        return retval;
  133|       |
  134|  1.86k|    if ((retval = readstat_set_read_handler(parser, unistd_read_handler)) != READSTAT_OK)
  ------------------
  |  Branch (134:9): [True: 0, False: 1.86k]
  ------------------
  135|      0|        return retval;
  136|       |
  137|  1.86k|    if ((readstat_set_update_handler(parser, unistd_update_handler)) != READSTAT_OK)
  ------------------
  |  Branch (137:9): [True: 0, False: 1.86k]
  ------------------
  138|      0|        return retval;
  139|       |
  140|  1.86k|    io_ctx = calloc(1, sizeof(unistd_io_ctx_t));
  141|  1.86k|    io_ctx->fd = -1;
  142|       |
  143|  1.86k|    retval = readstat_set_io_ctx(parser, (void*) io_ctx);
  144|  1.86k|    parser->io->io_ctx_needs_free = 1;
  145|       |
  146|  1.86k|    return retval;
  147|  1.86k|}

readstat_malloc:
   10|  1.28k|void *readstat_malloc(size_t len) {
   11|  1.28k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|  2.57k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (11:9): [True: 0, False: 1.28k]
  |  Branch (11:34): [True: 0, False: 1.28k]
  ------------------
   12|      0|        return NULL;
   13|      0|    }
   14|  1.28k|    return malloc(len);
   15|  1.28k|}
readstat_calloc:
   17|  1.60k|void *readstat_calloc(size_t count, size_t size) {
   18|  1.60k|    if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  3.21k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  3.09k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  1.48k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (18:9): [True: 128, False: 1.48k]
  |  Branch (18:36): [True: 0, False: 1.48k]
  |  Branch (18:62): [True: 36, False: 1.44k]
  ------------------
   19|    164|        return NULL;
   20|    164|    }
   21|  1.44k|    if (count == 0 || size == 0) {
  ------------------
  |  Branch (21:9): [True: 5, False: 1.44k]
  |  Branch (21:23): [True: 0, False: 1.44k]
  ------------------
   22|      5|        return NULL;
   23|      5|    }
   24|  1.44k|    return calloc(count, size);
   25|  1.44k|}
readstat_realloc:
   27|  3.79k|void *readstat_realloc(void *ptr, size_t len) {
   28|  3.79k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|  7.59k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (28:9): [True: 3, False: 3.79k]
  |  Branch (28:34): [True: 0, False: 3.79k]
  ------------------
   29|      3|        if (ptr)
  ------------------
  |  Branch (29:13): [True: 1, False: 2]
  ------------------
   30|      1|            free(ptr);
   31|      3|        return NULL;
   32|      3|    }
   33|  3.79k|    return realloc(ptr, len);
   34|  3.79k|}

readstat_parser_init:
    6|  1.86k|readstat_parser_t *readstat_parser_init(void) {
    7|  1.86k|    readstat_parser_t *parser = calloc(1, sizeof(readstat_parser_t));
    8|  1.86k|    parser->io = calloc(1, sizeof(readstat_io_t));
    9|  1.86k|    if (unistd_io_init(parser) != READSTAT_OK) {
  ------------------
  |  Branch (9:9): [True: 0, False: 1.86k]
  ------------------
   10|      0|        readstat_parser_free(parser);
   11|      0|        return NULL;
   12|      0|    }
   13|  1.86k|    parser->output_encoding = "UTF-8";
   14|  1.86k|    return parser;
   15|  1.86k|}
readstat_parser_free:
   17|  1.86k|void readstat_parser_free(readstat_parser_t *parser) {
   18|  1.86k|    if (parser) {
  ------------------
  |  Branch (18:9): [True: 1.86k, False: 0]
  ------------------
   19|  1.86k|        if (parser->io) {
  ------------------
  |  Branch (19:13): [True: 1.86k, False: 0]
  ------------------
   20|       |            readstat_set_io_ctx(parser, NULL);
   21|  1.86k|            free(parser->io);
   22|  1.86k|        }
   23|  1.86k|        free(parser);
   24|  1.86k|    }
   25|  1.86k|}
readstat_set_metadata_handler:
   27|  1.86k|readstat_error_t readstat_set_metadata_handler(readstat_parser_t *parser, readstat_metadata_handler metadata_handler) {
   28|  1.86k|    parser->handlers.metadata = metadata_handler;
   29|  1.86k|    return READSTAT_OK;
   30|  1.86k|}
readstat_set_note_handler:
   32|  1.86k|readstat_error_t readstat_set_note_handler(readstat_parser_t *parser, readstat_note_handler note_handler) {
   33|  1.86k|    parser->handlers.note = note_handler;
   34|  1.86k|    return READSTAT_OK;
   35|  1.86k|}
readstat_set_variable_handler:
   37|  1.86k|readstat_error_t readstat_set_variable_handler(readstat_parser_t *parser, readstat_variable_handler variable_handler) {
   38|  1.86k|    parser->handlers.variable = variable_handler;
   39|  1.86k|    return READSTAT_OK;
   40|  1.86k|}
readstat_set_value_handler:
   42|  1.86k|readstat_error_t readstat_set_value_handler(readstat_parser_t *parser, readstat_value_handler value_handler) {
   43|  1.86k|    parser->handlers.value = value_handler;
   44|  1.86k|    return READSTAT_OK;
   45|  1.86k|}
readstat_set_value_label_handler:
   47|  1.86k|readstat_error_t readstat_set_value_label_handler(readstat_parser_t *parser, readstat_value_label_handler label_handler) {
   48|  1.86k|    parser->handlers.value_label = label_handler;
   49|  1.86k|    return READSTAT_OK;
   50|  1.86k|}
readstat_set_fweight_handler:
   62|  1.86k|readstat_error_t readstat_set_fweight_handler(readstat_parser_t *parser, readstat_fweight_handler fweight_handler) {
   63|  1.86k|    parser->handlers.fweight = fweight_handler;
   64|  1.86k|    return READSTAT_OK;
   65|  1.86k|}
readstat_set_open_handler:
   67|  3.72k|readstat_error_t readstat_set_open_handler(readstat_parser_t *parser, readstat_open_handler open_handler) {
   68|  3.72k|    parser->io->open = open_handler;
   69|  3.72k|    return READSTAT_OK;
   70|  3.72k|}
readstat_set_close_handler:
   72|  3.72k|readstat_error_t readstat_set_close_handler(readstat_parser_t *parser, readstat_close_handler close_handler) {
   73|  3.72k|    parser->io->close = close_handler;
   74|  3.72k|    return READSTAT_OK;
   75|  3.72k|}
readstat_set_seek_handler:
   77|  3.72k|readstat_error_t readstat_set_seek_handler(readstat_parser_t *parser, readstat_seek_handler seek_handler) {
   78|  3.72k|    parser->io->seek = seek_handler;
   79|  3.72k|    return READSTAT_OK;
   80|  3.72k|}
readstat_set_read_handler:
   82|  3.72k|readstat_error_t readstat_set_read_handler(readstat_parser_t *parser, readstat_read_handler read_handler) {
   83|  3.72k|    parser->io->read = read_handler;
   84|  3.72k|    return READSTAT_OK;
   85|  3.72k|}
readstat_set_update_handler:
   87|  3.72k|readstat_error_t readstat_set_update_handler(readstat_parser_t *parser, readstat_update_handler update_handler) {
   88|  3.72k|    parser->io->update = update_handler;
   89|  3.72k|    return READSTAT_OK;
   90|  3.72k|}
readstat_set_io_ctx:
   92|  5.58k|readstat_error_t readstat_set_io_ctx(readstat_parser_t *parser, void *io_ctx) {
   93|  5.58k|    if (parser->io->io_ctx_needs_free) {
  ------------------
  |  Branch (93:9): [True: 1.86k, False: 3.72k]
  ------------------
   94|  1.86k|        free(parser->io->io_ctx);
   95|  1.86k|    }
   96|       |
   97|  5.58k|    parser->io->io_ctx = io_ctx;
   98|  5.58k|    parser->io->io_ctx_needs_free = 0;
   99|       |
  100|  5.58k|    return READSTAT_OK;
  101|  5.58k|}

sas_read8:
  139|  25.9k|uint64_t sas_read8(const char *data, int bswap) {
  140|  25.9k|    uint64_t tmp;
  141|  25.9k|    memcpy(&tmp, data, 8);
  142|  25.9k|    return bswap ? byteswap8(tmp) : tmp;
  ------------------
  |  Branch (142:12): [True: 23.9k, False: 1.95k]
  ------------------
  143|  25.9k|}
sas_read4:
  145|   181k|uint32_t sas_read4(const char *data, int bswap) {
  146|   181k|    uint32_t tmp;
  147|   181k|    memcpy(&tmp, data, 4);
  148|   181k|    return bswap ? byteswap4(tmp) : tmp;
  ------------------
  |  Branch (148:12): [True: 5.64k, False: 175k]
  ------------------
  149|   181k|}
sas_read2:
  151|  12.2M|uint16_t sas_read2(const char *data, int bswap) {
  152|  12.2M|    uint16_t tmp;
  153|  12.2M|    memcpy(&tmp, data, 2);
  154|  12.2M|    return bswap ? byteswap2(tmp) : tmp;
  ------------------
  |  Branch (154:12): [True: 13.3k, False: 12.2M]
  ------------------
  155|  12.2M|}
sas_read_header:
  162|  1.86k|        readstat_error_handler error_handler, void *user_ctx) {
  163|  1.86k|    sas_header_start_t  header_start;
  164|  1.86k|    sas_header_end_t    header_end;
  165|  1.86k|    int retval = READSTAT_OK;
  166|  1.86k|    char error_buf[1024];
  167|  1.86k|    time_t epoch = sas_epoch();
  168|       |
  169|  1.86k|    if (io->read(&header_start, sizeof(sas_header_start_t), io->io_ctx) < sizeof(sas_header_start_t)) {
  ------------------
  |  Branch (169:9): [True: 20, False: 1.84k]
  ------------------
  170|     20|        retval = READSTAT_ERROR_READ;
  171|     20|        goto cleanup;
  172|     20|    }
  173|  1.84k|    if (memcmp(header_start.magic, sas7bdat_magic_number, sizeof(sas7bdat_magic_number)) != 0 &&
  ------------------
  |  Branch (173:9): [True: 1.02k, False: 811]
  ------------------
  174|  1.02k|            memcmp(header_start.magic, sas7bcat_magic_number, sizeof(sas7bcat_magic_number)) != 0) {
  ------------------
  |  Branch (174:13): [True: 134, False: 895]
  ------------------
  175|    134|        retval = READSTAT_ERROR_PARSE;
  176|    134|        goto cleanup;
  177|    134|    }
  178|  1.70k|    if (header_start.a1 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   87|  1.70k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (178:9): [True: 9, False: 1.69k]
  ------------------
  179|      9|        hinfo->pad1 = 4;
  180|      9|    }
  181|  1.70k|    if (header_start.a2 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   87|  1.70k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (181:9): [True: 413, False: 1.29k]
  ------------------
  182|    413|        hinfo->u64 = 1;
  183|    413|    }
  184|  1.70k|    int bswap = 0;
  185|  1.70k|    if (header_start.endian == SAS_ENDIAN_BIG) {
  ------------------
  |  |   80|  1.70k|#define SAS_ENDIAN_BIG       0x00
  ------------------
  |  Branch (185:9): [True: 435, False: 1.27k]
  ------------------
  186|    435|        bswap = machine_is_little_endian();
  187|    435|        hinfo->little_endian = 0;
  188|  1.27k|    } else if (header_start.endian == SAS_ENDIAN_LITTLE) {
  ------------------
  |  |   81|  1.27k|#define SAS_ENDIAN_LITTLE    0x01
  ------------------
  |  Branch (188:16): [True: 1.25k, False: 14]
  ------------------
  189|  1.25k|        bswap = !machine_is_little_endian();
  190|  1.25k|        hinfo->little_endian = 1;
  191|  1.25k|    } else {
  192|     14|        retval = READSTAT_ERROR_PARSE;
  193|     14|        goto cleanup;
  194|     14|    }
  195|  1.69k|    int i;
  196|  19.8k|    for (i=0; i<sizeof(_charset_table)/sizeof(_charset_table[0]); i++) {
  ------------------
  |  Branch (196:15): [True: 19.8k, False: 3]
  ------------------
  197|  19.8k|        if (header_start.encoding == _charset_table[i].code) {
  ------------------
  |  Branch (197:13): [True: 1.68k, False: 18.1k]
  ------------------
  198|  1.68k|            hinfo->encoding = _charset_table[i].name;
  199|  1.68k|            break;
  200|  1.68k|        }
  201|  19.8k|    }
  202|  1.69k|    if (hinfo->encoding == NULL) {
  ------------------
  |  Branch (202:9): [True: 3, False: 1.68k]
  ------------------
  203|      3|        if (error_handler) {
  ------------------
  |  Branch (203:13): [True: 0, False: 3]
  ------------------
  204|      0|            snprintf(error_buf, sizeof(error_buf), "Unsupported character set code: %d", header_start.encoding);
  205|      0|            error_handler(error_buf, user_ctx);
  206|      0|        }
  207|      3|        retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
  208|      3|        goto cleanup;
  209|      3|    }
  210|  1.68k|    memcpy(hinfo->table_name, header_start.table_name, sizeof(header_start.table_name));
  211|  1.68k|    if (io->seek(hinfo->pad1, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (211:9): [True: 4, False: 1.68k]
  ------------------
  212|      4|        retval = READSTAT_ERROR_SEEK;
  213|      4|        goto cleanup;
  214|      4|    }
  215|       |
  216|  1.68k|    double creation_time, modification_time, creation_time_diff, modification_time_diff;
  217|       |
  218|  1.68k|    if (io->read(&creation_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (218:9): [True: 14, False: 1.67k]
  ------------------
  219|     14|        retval = READSTAT_ERROR_READ;
  220|     14|        goto cleanup;
  221|     14|    }
  222|  1.67k|    if (bswap)
  ------------------
  |  Branch (222:9): [True: 426, False: 1.24k]
  ------------------
  223|    426|        creation_time = byteswap_double(creation_time);
  224|       |
  225|  1.67k|    if (io->read(&modification_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (225:9): [True: 7, False: 1.66k]
  ------------------
  226|      7|        retval = READSTAT_ERROR_READ;
  227|      7|        goto cleanup;
  228|      7|    }
  229|  1.66k|    if (bswap)
  ------------------
  |  Branch (229:9): [True: 420, False: 1.24k]
  ------------------
  230|    420|        modification_time = byteswap_double(modification_time);
  231|       |
  232|  1.66k|    if (io->read(&creation_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (232:9): [True: 6, False: 1.65k]
  ------------------
  233|      6|        retval = READSTAT_ERROR_READ;
  234|      6|        goto cleanup;
  235|      6|    }
  236|  1.65k|    if (bswap)
  ------------------
  |  Branch (236:9): [True: 417, False: 1.24k]
  ------------------
  237|    417|        creation_time_diff = byteswap_double(creation_time_diff);
  238|       |    
  239|  1.65k|    if (io->read(&modification_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (239:9): [True: 7, False: 1.65k]
  ------------------
  240|      7|        retval = READSTAT_ERROR_READ;
  241|      7|        goto cleanup;
  242|      7|    }
  243|  1.65k|    if (bswap)
  ------------------
  |  Branch (243:9): [True: 414, False: 1.23k]
  ------------------
  244|    414|        modification_time_diff = byteswap_double(modification_time_diff);
  245|       |    
  246|  1.65k|    hinfo->creation_time = sas_convert_time(creation_time, creation_time_diff, epoch);
  247|  1.65k|    hinfo->modification_time = sas_convert_time(modification_time, modification_time_diff, epoch);
  248|       |
  249|  1.65k|    uint32_t header_size, page_size;
  250|       |
  251|  1.65k|    if (io->read(&header_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (251:9): [True: 8, False: 1.64k]
  ------------------
  252|      8|        retval = READSTAT_ERROR_READ;
  253|      8|        goto cleanup;
  254|      8|    }
  255|  1.64k|    if (io->read(&page_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (255:9): [True: 3, False: 1.64k]
  ------------------
  256|      3|        retval = READSTAT_ERROR_READ;
  257|      3|        goto cleanup;
  258|      3|    }
  259|       |
  260|  1.64k|    hinfo->header_size = bswap ? byteswap4(header_size) : header_size;
  ------------------
  |  Branch (260:26): [True: 410, False: 1.23k]
  ------------------
  261|  1.64k|    hinfo->page_size = bswap ? byteswap4(page_size) : page_size;
  ------------------
  |  Branch (261:24): [True: 410, False: 1.23k]
  ------------------
  262|       |
  263|  1.64k|    if (hinfo->header_size < 1024 || hinfo->page_size < 1024) {
  ------------------
  |  Branch (263:9): [True: 11, False: 1.62k]
  |  Branch (263:38): [True: 11, False: 1.61k]
  ------------------
  264|     22|        retval = READSTAT_ERROR_PARSE;
  265|     22|        goto cleanup;
  266|     22|    }
  267|  1.61k|    if (hinfo->header_size > (1<<24) || hinfo->page_size > (1<<24)) {
  ------------------
  |  Branch (267:9): [True: 31, False: 1.58k]
  |  Branch (267:41): [True: 33, False: 1.55k]
  ------------------
  268|     64|        retval = READSTAT_ERROR_PARSE;
  269|     64|        goto cleanup;
  270|     64|    }
  271|       |
  272|  1.55k|    if (hinfo->u64) {
  ------------------
  |  Branch (272:9): [True: 377, False: 1.17k]
  ------------------
  273|    377|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_64BIT;
  ------------------
  |  |  121|    377|#define SAS_PAGE_HEADER_SIZE_64BIT  40
  ------------------
  274|    377|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_64BIT;
  ------------------
  |  |  118|    377|#define SAS_SUBHEADER_POINTER_SIZE_64BIT    24
  ------------------
  275|  1.17k|    } else {
  276|  1.17k|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_32BIT;
  ------------------
  |  |  120|  1.17k|#define SAS_PAGE_HEADER_SIZE_32BIT  24
  ------------------
  277|  1.17k|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_32BIT;
  ------------------
  |  |  117|  1.17k|#define SAS_SUBHEADER_POINTER_SIZE_32BIT    12
  ------------------
  278|  1.17k|    }
  279|       |
  280|  1.55k|    if (hinfo->u64) {
  ------------------
  |  Branch (280:9): [True: 377, False: 1.17k]
  ------------------
  281|    377|        uint64_t page_count;
  282|    377|        if (io->read(&page_count, sizeof(uint64_t), io->io_ctx) < sizeof(uint64_t)) {
  ------------------
  |  Branch (282:13): [True: 20, False: 357]
  ------------------
  283|     20|            retval = READSTAT_ERROR_READ;
  284|     20|            goto cleanup;
  285|     20|        }
  286|    357|        hinfo->page_count = bswap ? byteswap8(page_count) : page_count;
  ------------------
  |  Branch (286:29): [True: 34, False: 323]
  ------------------
  287|  1.17k|    } else {
  288|  1.17k|        uint32_t page_count;
  289|  1.17k|        if (io->read(&page_count, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (289:13): [True: 20, False: 1.15k]
  ------------------
  290|     20|            retval = READSTAT_ERROR_READ;
  291|     20|            goto cleanup;
  292|     20|        }
  293|  1.15k|        hinfo->page_count = bswap ? byteswap4(page_count) : page_count;
  ------------------
  |  Branch (293:29): [True: 311, False: 846]
  ------------------
  294|  1.15k|    }
  295|  1.51k|    if (hinfo->page_count > (1<<24)) {
  ------------------
  |  Branch (295:9): [True: 11, False: 1.50k]
  ------------------
  296|     11|        retval = READSTAT_ERROR_PARSE;
  297|     11|        goto cleanup;
  298|     11|    }
  299|       |    
  300|  1.50k|    if (io->seek(8, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (300:9): [True: 85, False: 1.41k]
  ------------------
  301|     85|        retval = READSTAT_ERROR_SEEK;
  302|     85|        if (error_handler) {
  ------------------
  |  Branch (302:13): [True: 0, False: 85]
  ------------------
  303|      0|            snprintf(error_buf, sizeof(error_buf), "ReadStat: Failed to seek forward by %d", 8);
  304|      0|            error_handler(error_buf, user_ctx);
  305|      0|        }
  306|     85|        goto cleanup;
  307|     85|    }
  308|  1.41k|    if (io->read(&header_end, sizeof(sas_header_end_t), io->io_ctx) < sizeof(sas_header_end_t)) {
  ------------------
  |  Branch (308:9): [True: 19, False: 1.39k]
  ------------------
  309|     19|        retval = READSTAT_ERROR_READ;
  310|     19|        goto cleanup;
  311|     19|    }
  312|  1.39k|    char major, revision_tag;
  313|  1.39k|    int minor, revision;
  314|  1.39k|    if (sscanf(header_end.release, "%c.%04d%c%1d", &major, &minor, &revision_tag, &revision) != 4) {
  ------------------
  |  Branch (314:9): [True: 4, False: 1.39k]
  ------------------
  315|      4|        retval = READSTAT_ERROR_PARSE;
  316|      4|        goto cleanup;
  317|      4|    }
  318|       |
  319|  1.39k|    if (major >= '1' && major <= '9') {
  ------------------
  |  Branch (319:9): [True: 1.38k, False: 13]
  |  Branch (319:25): [True: 1.36k, False: 18]
  ------------------
  320|  1.36k|        hinfo->major_version = major - '0';
  321|  1.36k|    } else if (major == 'V') {
  ------------------
  |  Branch (321:16): [True: 8, False: 23]
  ------------------
  322|       |        // It appears that SAS Visual Forecaster reports the major version as "V"
  323|       |        // Treat it as version 9 for all intents and purposes
  324|      8|        hinfo->major_version = 9;
  325|     23|    } else {
  326|     23|        retval = READSTAT_ERROR_PARSE;
  327|     23|        goto cleanup;
  328|     23|    }
  329|       |    // revision_tag is usually M, but J has been observed in the wild (not created with SAS?)
  330|  1.37k|    if (revision_tag != 'M' && revision_tag != 'J') {
  ------------------
  |  Branch (330:9): [True: 24, False: 1.34k]
  |  Branch (330:32): [True: 14, False: 10]
  ------------------
  331|     14|        retval = READSTAT_ERROR_PARSE;
  332|     14|        goto cleanup;
  333|     14|    }
  334|  1.35k|    hinfo->minor_version = minor;
  335|  1.35k|    hinfo->revision = revision;
  336|       |
  337|  1.35k|    if ((major == '8' || major == '9') && minor == 0 && revision == 0) {
  ------------------
  |  Branch (337:10): [True: 251, False: 1.10k]
  |  Branch (337:26): [True: 193, False: 914]
  |  Branch (337:43): [True: 12, False: 432]
  |  Branch (337:57): [True: 5, False: 7]
  ------------------
  338|       |        /* A bit of a hack, but most SAS installations are running a minor update */
  339|      5|        hinfo->vendor = READSTAT_VENDOR_STAT_TRANSFER;
  340|  1.35k|    } else {
  341|  1.35k|        hinfo->vendor = READSTAT_VENDOR_SAS;
  342|  1.35k|    }
  343|  1.35k|    if (io->seek(hinfo->header_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (343:9): [True: 68, False: 1.29k]
  ------------------
  344|     68|        retval = READSTAT_ERROR_SEEK;
  345|     68|        if (error_handler) {
  ------------------
  |  Branch (345:13): [True: 0, False: 68]
  ------------------
  346|      0|            snprintf(error_buf, sizeof(error_buf), 
  347|      0|                    "ReadStat: Failed to seek to position %" PRId64, hinfo->header_size);
  348|      0|            error_handler(error_buf, user_ctx);
  349|      0|        }
  350|     68|        goto cleanup;
  351|     68|    }
  352|       |
  353|  1.86k|cleanup:
  354|  1.86k|    return retval;
  355|  1.35k|}
sas_validate_tag:
  507|  2.56k|readstat_error_t sas_validate_tag(char tag) {
  508|  2.56k|    if (tag == '_' || (tag >= 'A' && tag <= 'Z'))
  ------------------
  |  Branch (508:9): [True: 523, False: 2.04k]
  |  Branch (508:24): [True: 1.12k, False: 922]
  |  Branch (508:38): [True: 661, False: 462]
  ------------------
  509|  1.18k|        return READSTAT_OK;
  510|       |
  511|  1.38k|    return READSTAT_ERROR_TAGGED_VALUE_IS_OUT_OF_RANGE;
  512|  2.56k|}
sas_assign_tag:
  514|  2.56k|void sas_assign_tag(readstat_value_t *value, uint8_t tag) {
  515|       |    /* We accommodate two tag schemes. In the first, the tag is an ASCII code
  516|       |     * given by uint8_t tag above. System missing is represented by an ASCII
  517|       |     * period. In the second scheme, (tag-2) is an offset from 'A', except when
  518|       |     * tag == 0, in which case it represents an underscore, or tag == 1, in
  519|       |     * which case it represents system-missing.
  520|       |     */
  521|  2.56k|    if (tag == 0) {
  ------------------
  |  Branch (521:9): [True: 493, False: 2.07k]
  ------------------
  522|    493|        tag = '_';
  523|  2.07k|    } else if (tag >= 2 && tag < 28) {
  ------------------
  |  Branch (523:16): [True: 1.47k, False: 599]
  |  Branch (523:28): [True: 638, False: 838]
  ------------------
  524|    638|        tag = 'A' + (tag - 2);
  525|    638|    }
  526|  2.56k|    if (sas_validate_tag(tag) == READSTAT_OK) {
  ------------------
  |  Branch (526:9): [True: 1.18k, False: 1.38k]
  ------------------
  527|  1.18k|        value->tag = tag;
  528|  1.18k|        value->is_tagged_missing = 1;
  529|  1.38k|    } else {
  530|  1.38k|        value->tag = 0;
  531|  1.38k|        value->is_system_missing = 1;
  532|  1.38k|    }
  533|  2.56k|}
readstat_sas.c:sas_epoch:
  123|  1.86k|static time_t sas_epoch(void) {
  124|  1.86k|    return - 3653 * 86400; // seconds between 01-01-1960 and 01-01-1970
  125|  1.86k|}
readstat_sas.c:sas_convert_time:
  127|  3.30k|static time_t sas_convert_time(double time, double time_diff, time_t epoch) {
  128|  3.30k|    time -= time_diff;
  129|  3.30k|    time += epoch;
  130|  3.30k|    if (isnan(time))
  ------------------
  |  Branch (130:9): [True: 102, False: 3.20k]
  ------------------
  131|    102|        return 0;
  132|  3.20k|    if (time > (double)LONG_MAX)
  ------------------
  |  Branch (132:9): [True: 297, False: 2.90k]
  ------------------
  133|    297|        return LONG_MAX;
  134|  2.90k|    if (time < (double)LONG_MIN)
  ------------------
  |  Branch (134:9): [True: 334, False: 2.56k]
  ------------------
  135|    334|        return LONG_MIN;
  136|  2.56k|    return time;
  137|  2.90k|}

readstat_parse_sas7bcat:
  376|  1.86k|readstat_error_t readstat_parse_sas7bcat(readstat_parser_t *parser, const char *path, void *user_ctx) {
  377|  1.86k|    readstat_error_t retval = READSTAT_OK;
  378|  1.86k|    readstat_io_t *io = parser->io;
  379|  1.86k|    int64_t i;
  380|  1.86k|    char *page = NULL;
  381|  1.86k|    char *buffer = NULL;
  382|       |
  383|  1.86k|    sas7bcat_ctx_t *ctx = calloc(1, sizeof(sas7bcat_ctx_t));
  384|  1.86k|    sas_header_info_t *hinfo = calloc(1, sizeof(sas_header_info_t));
  385|       |
  386|  1.86k|    ctx->block_pointers = malloc((ctx->block_pointers_capacity = 200) * sizeof(uint64_t));
  387|       |
  388|  1.86k|    ctx->value_label_handler = parser->handlers.value_label;
  389|  1.86k|    ctx->metadata_handler = parser->handlers.metadata;
  390|  1.86k|    ctx->input_encoding = parser->input_encoding;
  391|  1.86k|    ctx->output_encoding = parser->output_encoding;
  392|  1.86k|    ctx->user_ctx = user_ctx;
  393|  1.86k|    ctx->io = io;
  394|       |
  395|  1.86k|    if (io->open(path, io->io_ctx) == -1) {
  ------------------
  |  Branch (395:9): [True: 0, False: 1.86k]
  ------------------
  396|      0|        retval = READSTAT_ERROR_OPEN;
  397|      0|        goto cleanup;
  398|      0|    }
  399|       |
  400|  1.86k|    if ((retval = sas_read_header(io, hinfo, parser->handlers.error, user_ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (400:9): [True: 570, False: 1.29k]
  ------------------
  401|    570|        goto cleanup;
  402|    570|    }
  403|       |
  404|  1.29k|    ctx->u64 = hinfo->u64;
  405|  1.29k|    ctx->pad1 = hinfo->pad1;
  406|  1.29k|    ctx->bswap = machine_is_little_endian() ^ hinfo->little_endian;
  407|  1.29k|    ctx->header_size = hinfo->header_size;
  408|  1.29k|    ctx->page_count = hinfo->page_count;
  409|  1.29k|    ctx->page_size = hinfo->page_size;
  410|  1.29k|    if (ctx->input_encoding == NULL) {
  ------------------
  |  Branch (410:9): [True: 1.29k, False: 0]
  ------------------
  411|  1.29k|        ctx->input_encoding = hinfo->encoding;
  412|  1.29k|    }
  413|       |
  414|  1.29k|    ctx->xlsr_size = 212 + ctx->pad1;
  415|  1.29k|    ctx->xlsr_offset = 856 + 2 * ctx->pad1;
  416|  1.29k|    ctx->xlsr_O_offset = 50 + ctx->pad1;
  417|  1.29k|    if (ctx->u64) {
  ------------------
  |  Branch (417:9): [True: 296, False: 994]
  ------------------
  418|    296|        ctx->xlsr_offset += 144;
  419|    296|        ctx->xlsr_size += 72;
  420|    296|        ctx->xlsr_O_offset += 24;
  421|    296|    }
  422|       |
  423|  1.29k|    if (ctx->input_encoding && ctx->output_encoding && strcmp(ctx->input_encoding, ctx->output_encoding) != 0) {
  ------------------
  |  Branch (423:9): [True: 1.29k, False: 0]
  |  Branch (423:32): [True: 1.29k, False: 0]
  |  Branch (423:56): [True: 792, False: 498]
  ------------------
  424|    792|        iconv_t converter = iconv_open(ctx->output_encoding, ctx->input_encoding);
  425|    792|        if (converter == (iconv_t)-1) {
  ------------------
  |  Branch (425:13): [True: 2, False: 790]
  ------------------
  426|      2|            retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
  427|      2|            goto cleanup;
  428|      2|        }
  429|    790|        ctx->converter = converter;
  430|    790|    }
  431|       |
  432|  1.28k|    if (ctx->metadata_handler) {
  ------------------
  |  Branch (432:9): [True: 1.28k, False: 0]
  ------------------
  433|  1.28k|        char table_name[4*32+1];
  434|  1.28k|        readstat_metadata_t metadata = { 
  435|  1.28k|            .file_encoding = ctx->input_encoding, /* orig encoding? */
  436|  1.28k|            .modified_time = hinfo->modification_time,
  437|  1.28k|            .creation_time = hinfo->creation_time,
  438|  1.28k|            .file_format_version = hinfo->major_version,
  439|  1.28k|            .endianness = hinfo->little_endian ? READSTAT_ENDIAN_LITTLE : READSTAT_ENDIAN_BIG,
  ------------------
  |  Branch (439:27): [True: 1.02k, False: 259]
  ------------------
  440|  1.28k|            .is64bit = ctx->u64
  441|  1.28k|        };
  442|  1.28k|        retval = readstat_convert(table_name, sizeof(table_name),
  443|  1.28k|                hinfo->table_name, sizeof(hinfo->table_name), ctx->converter);
  444|  1.28k|        if (retval != READSTAT_OK)
  ------------------
  |  Branch (444:13): [True: 3, False: 1.28k]
  ------------------
  445|      3|            goto cleanup;
  446|       |
  447|  1.28k|        metadata.table_name = table_name;
  448|       |
  449|  1.28k|        if (ctx->metadata_handler(&metadata, ctx->user_ctx) != READSTAT_HANDLER_OK) {
  ------------------
  |  Branch (449:13): [True: 0, False: 1.28k]
  ------------------
  450|      0|            retval = READSTAT_ERROR_USER_ABORT;
  451|      0|            goto cleanup;
  452|      0|        }
  453|  1.28k|    }
  454|       |
  455|  1.28k|    if ((page = readstat_malloc(ctx->page_size)) == NULL) {
  ------------------
  |  Branch (455:9): [True: 0, False: 1.28k]
  ------------------
  456|      0|        retval = READSTAT_ERROR_MALLOC;
  457|      0|        goto cleanup;
  458|      0|    }
  459|  1.28k|    if (io->seek(ctx->header_size+SAS_CATALOG_FIRST_INDEX_PAGE*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  |   11|  1.28k|#define SAS_CATALOG_FIRST_INDEX_PAGE 1
  ------------------
  |  Branch (459:9): [True: 68, False: 1.21k]
  ------------------
  460|     68|        retval = READSTAT_ERROR_SEEK;
  461|     68|        goto cleanup;
  462|     68|    }
  463|  1.21k|    if (io->read(page, ctx->page_size, io->io_ctx) < ctx->page_size) {
  ------------------
  |  Branch (463:9): [True: 44, False: 1.17k]
  ------------------
  464|     44|        retval = READSTAT_ERROR_READ;
  465|     44|        goto cleanup;
  466|     44|    }
  467|       |
  468|  1.17k|    retval = sas7bcat_augment_index(&page[ctx->xlsr_offset], ctx->page_size - ctx->xlsr_offset, ctx);
  469|  1.17k|    if (retval != READSTAT_OK)
  ------------------
  |  Branch (469:9): [True: 0, False: 1.17k]
  ------------------
  470|      0|        goto cleanup;
  471|       |
  472|       |    // Pass 1 -- find the XLSR entries
  473|  21.9k|    for (i=SAS_CATALOG_USELESS_PAGES; i<ctx->page_count; i++) {
  ------------------
  |  |   12|  1.17k|#define SAS_CATALOG_USELESS_PAGES    3
  ------------------
  |  Branch (473:39): [True: 20.8k, False: 1.08k]
  ------------------
  474|  20.8k|        if (io->seek(ctx->header_size+i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (474:13): [True: 20, False: 20.7k]
  ------------------
  475|     20|            retval = READSTAT_ERROR_SEEK;
  476|     20|            goto cleanup;
  477|     20|        }
  478|  20.7k|        if (io->read(page, ctx->page_size, io->io_ctx) < ctx->page_size) {
  ------------------
  |  Branch (478:13): [True: 70, False: 20.7k]
  ------------------
  479|     70|            retval = READSTAT_ERROR_READ;
  480|     70|            goto cleanup;
  481|     70|        }
  482|  20.7k|        if (memcmp(&page[16], "XLSR", sizeof("XLSR")-1) == 0) {
  ------------------
  |  Branch (482:13): [True: 7.28k, False: 13.4k]
  ------------------
  483|  7.28k|            retval = sas7bcat_augment_index(&page[16], ctx->page_size - 16, ctx);
  484|  7.28k|            if (retval != READSTAT_OK)
  ------------------
  |  Branch (484:17): [True: 0, False: 7.28k]
  ------------------
  485|      0|                goto cleanup;
  486|  7.28k|        }
  487|  20.7k|    }
  488|       |
  489|  1.08k|    sas7bcat_sort_index(ctx);
  490|  1.08k|    sas7bcat_uniq_index(ctx);
  491|       |
  492|       |    // Pass 2 -- look up the individual block pointers
  493|  15.0k|    for (i=0; i<ctx->block_pointers_used; i++) {
  ------------------
  |  Branch (493:15): [True: 14.5k, False: 494]
  ------------------
  494|  14.5k|        int start_page = ctx->block_pointers[i] >> 32;
  495|  14.5k|        int start_page_pos = (ctx->block_pointers[i]) & 0xFFFF;
  496|       |
  497|  14.5k|        int buffer_len = sas7bcat_block_size(start_page, start_page_pos, ctx, &retval);
  498|  14.5k|        if (buffer_len == -1) {
  ------------------
  |  Branch (498:13): [True: 39, False: 14.4k]
  ------------------
  499|     39|            goto cleanup;
  500|  14.4k|        } else if (buffer_len == 0) {
  ------------------
  |  Branch (500:20): [True: 10.7k, False: 3.73k]
  ------------------
  501|  10.7k|            continue;
  502|  10.7k|        }
  503|  3.73k|        if ((buffer = readstat_realloc(buffer, buffer_len)) == NULL) {
  ------------------
  |  Branch (503:13): [True: 3, False: 3.73k]
  ------------------
  504|      3|            retval = READSTAT_ERROR_MALLOC;
  505|      3|            goto cleanup;
  506|      3|        }
  507|  3.73k|        if ((retval = sas7bcat_read_block(buffer, buffer_len, start_page, start_page_pos, ctx)) != READSTAT_OK)
  ------------------
  |  Branch (507:13): [True: 60, False: 3.67k]
  ------------------
  508|     60|            goto cleanup;
  509|  3.67k|        if ((retval = sas7bcat_parse_block(buffer, buffer_len, ctx)) != READSTAT_OK)
  ------------------
  |  Branch (509:13): [True: 487, False: 3.18k]
  ------------------
  510|    487|            goto cleanup;
  511|  3.67k|    }
  512|       |
  513|  1.86k|cleanup:
  514|  1.86k|    io->close(io->io_ctx);
  515|  1.86k|    if (page)
  ------------------
  |  Branch (515:9): [True: 1.28k, False: 575]
  ------------------
  516|  1.28k|        free(page);
  517|  1.86k|    if (buffer)
  ------------------
  |  Branch (517:9): [True: 782, False: 1.07k]
  ------------------
  518|    782|        free(buffer);
  519|  1.86k|    if (ctx)
  ------------------
  |  Branch (519:9): [True: 1.86k, False: 0]
  ------------------
  520|  1.86k|        sas7bcat_ctx_free(ctx);
  521|  1.86k|    if (hinfo)
  ------------------
  |  Branch (521:9): [True: 1.86k, False: 0]
  ------------------
  522|  1.86k|        free(hinfo);
  523|       |
  524|  1.86k|    return retval;
  525|  1.08k|}
readstat_sas7bcat_read.c:sas7bcat_augment_index:
  208|  8.45k|static readstat_error_t sas7bcat_augment_index(const char *index, size_t len, sas7bcat_ctx_t *ctx) {
  209|  8.45k|    const char *xlsr = index;
  210|  8.45k|    readstat_error_t retval = READSTAT_OK;
  211|  35.3k|    while (xlsr + ctx->xlsr_size <= index + len) {
  ------------------
  |  Branch (211:12): [True: 28.2k, False: 7.11k]
  ------------------
  212|  28.2k|        if (memcmp(xlsr, "XLSR", 4) != 0) // some block pointers seem to have 8 bytes of extra padding
  ------------------
  |  Branch (212:13): [True: 13.4k, False: 14.8k]
  ------------------
  213|  13.4k|            xlsr += 8;
  214|  28.2k|        if (memcmp(xlsr, "XLSR", 4) != 0)
  ------------------
  |  Branch (214:13): [True: 1.34k, False: 26.9k]
  ------------------
  215|  1.34k|            break;
  216|       |
  217|  26.9k|        if (xlsr[ctx->xlsr_O_offset] == 'O') {
  ------------------
  |  Branch (217:13): [True: 24.7k, False: 2.21k]
  ------------------
  218|  24.7k|            uint64_t page = 0, pos = 0;
  219|  24.7k|            if (ctx->u64) {
  ------------------
  |  Branch (219:17): [True: 910, False: 23.8k]
  ------------------
  220|    910|                page = sas_read8(&xlsr[8], ctx->bswap);
  221|    910|                pos = sas_read2(&xlsr[16], ctx->bswap);
  222|  23.8k|            } else {
  223|  23.8k|                page = sas_read4(&xlsr[4], ctx->bswap);
  224|  23.8k|                pos = sas_read2(&xlsr[8], ctx->bswap);
  225|  23.8k|            }
  226|  24.7k|            ctx->block_pointers[ctx->block_pointers_used++] = (page << 32) + pos;
  227|  24.7k|        }
  228|       |
  229|  26.9k|        if (ctx->block_pointers_used == ctx->block_pointers_capacity) {
  ------------------
  |  Branch (229:13): [True: 62, False: 26.8k]
  ------------------
  230|     62|            ctx->block_pointers = readstat_realloc(ctx->block_pointers, (ctx->block_pointers_capacity *= 2) * sizeof(uint64_t));
  231|     62|            if (ctx->block_pointers == NULL) {
  ------------------
  |  Branch (231:17): [True: 0, False: 62]
  ------------------
  232|      0|                retval = READSTAT_ERROR_MALLOC;
  233|      0|                goto cleanup;
  234|      0|            }
  235|     62|        }
  236|       |
  237|  26.9k|        xlsr += ctx->xlsr_size;
  238|  26.9k|    }
  239|  8.45k|cleanup:
  240|  8.45k|    return retval;
  241|  8.45k|}
readstat_sas7bcat_read.c:sas7bcat_sort_index:
  249|  1.08k|static void sas7bcat_sort_index(sas7bcat_ctx_t *ctx) {
  250|  1.08k|    if (ctx->block_pointers_used == 0)
  ------------------
  |  Branch (250:9): [True: 116, False: 967]
  ------------------
  251|    116|        return;
  252|       |
  253|    967|    int i;
  254|  1.38k|    for (i=1; i<ctx->block_pointers_used; i++) {
  ------------------
  |  Branch (254:15): [True: 651, False: 737]
  ------------------
  255|    651|        if (ctx->block_pointers[i] < ctx->block_pointers[i-1]) {
  ------------------
  |  Branch (255:13): [True: 230, False: 421]
  ------------------
  256|    230|            qsort(ctx->block_pointers, ctx->block_pointers_used, sizeof(uint64_t), &compare_block_pointers);
  257|    230|            break;
  258|    230|        }
  259|    651|    }
  260|    967|}
readstat_sas7bcat_read.c:compare_block_pointers:
  243|   106k|static int compare_block_pointers(const void *elem1, const void *elem2) {
  244|   106k|    uint64_t v1 = *(const uint64_t *)elem1;
  245|   106k|    uint64_t v2 = *(const uint64_t *)elem2;
  246|   106k|    return v1 - v2;
  247|   106k|}
readstat_sas7bcat_read.c:sas7bcat_uniq_index:
  262|  1.08k|static void sas7bcat_uniq_index(sas7bcat_ctx_t *ctx) {
  263|  1.08k|    if (ctx->block_pointers_used == 0)
  ------------------
  |  Branch (263:9): [True: 116, False: 967]
  ------------------
  264|    116|        return;
  265|       |
  266|    967|    int i;
  267|    967|    int out_i = 1;
  268|  18.9k|    for (i=1; i<ctx->block_pointers_used; i++) {
  ------------------
  |  Branch (268:15): [True: 17.9k, False: 967]
  ------------------
  269|  17.9k|        if (ctx->block_pointers[i] != ctx->block_pointers[i-1]) {
  ------------------
  |  Branch (269:13): [True: 13.6k, False: 4.35k]
  ------------------
  270|  13.6k|            if (out_i != i) {
  ------------------
  |  Branch (270:17): [True: 12.0k, False: 1.59k]
  ------------------
  271|  12.0k|                ctx->block_pointers[out_i] = ctx->block_pointers[i];
  272|  12.0k|            }
  273|  13.6k|            out_i++;
  274|  13.6k|        }
  275|  17.9k|    }
  276|    967|    ctx->block_pointers_used = out_i;
  277|    967|}
readstat_sas7bcat_read.c:sas7bcat_block_size:
  279|  14.5k|static int sas7bcat_block_size(int start_page, int start_page_pos, sas7bcat_ctx_t *ctx, readstat_error_t *outError) {
  280|  14.5k|    readstat_error_t retval = READSTAT_OK;
  281|  14.5k|    readstat_io_t *io = ctx->io;
  282|  14.5k|    int next_page = start_page;
  283|  14.5k|    int next_page_pos = start_page_pos;
  284|  14.5k|    int link_count = 0;
  285|       |
  286|  14.5k|    int buffer_len = 0;
  287|  14.5k|    int chain_link_len = 0;
  288|       |
  289|  14.5k|    char chain_link[32];
  290|  14.5k|    int chain_link_header_len = 16;
  291|  14.5k|    if (ctx->u64) {
  ------------------
  |  Branch (291:9): [True: 691, False: 13.8k]
  ------------------
  292|    691|        chain_link_header_len = 32;
  293|    691|    }
  294|       |
  295|       |    // calculate buffer size needed
  296|  39.0k|    while (next_page > 0 && next_page_pos > 0 && next_page <= ctx->page_count && link_count++ < ctx->page_count) {
  ------------------
  |  Branch (296:12): [True: 34.0k, False: 4.92k]
  |  Branch (296:29): [True: 32.8k, False: 1.19k]
  |  Branch (296:50): [True: 24.9k, False: 7.96k]
  |  Branch (296:82): [True: 24.5k, False: 393]
  ------------------
  297|  24.5k|        if (io->seek(ctx->header_size+(next_page-1)*ctx->page_size+next_page_pos, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (297:13): [True: 19, False: 24.5k]
  ------------------
  298|     19|            retval = READSTAT_ERROR_SEEK;
  299|     19|            goto cleanup;
  300|     19|        }
  301|  24.5k|        if (io->read(chain_link, chain_link_header_len, io->io_ctx) < chain_link_header_len) {
  ------------------
  |  Branch (301:13): [True: 20, False: 24.4k]
  ------------------
  302|     20|            retval = READSTAT_ERROR_READ;
  303|     20|            goto cleanup;
  304|     20|        }
  305|       |
  306|  24.4k|        if (ctx->u64) {
  ------------------
  |  Branch (306:13): [True: 1.44k, False: 23.0k]
  ------------------
  307|  1.44k|            next_page = sas_read4(&chain_link[0], ctx->bswap);
  308|  1.44k|            next_page_pos = sas_read2(&chain_link[8], ctx->bswap);
  309|  1.44k|            chain_link_len = sas_read2(&chain_link[10], ctx->bswap);
  310|  23.0k|        } else {
  311|  23.0k|            next_page = sas_read4(&chain_link[0], ctx->bswap);
  312|  23.0k|            next_page_pos = sas_read2(&chain_link[4], ctx->bswap);
  313|  23.0k|            chain_link_len = sas_read2(&chain_link[6], ctx->bswap);
  314|  23.0k|        }
  315|       |
  316|  24.4k|        buffer_len += chain_link_len;
  317|  24.4k|    }
  318|       |
  319|  14.5k|cleanup:
  320|  14.5k|    if (outError)
  ------------------
  |  Branch (320:9): [True: 14.5k, False: 0]
  ------------------
  321|  14.5k|        *outError = retval;
  322|       |
  323|  14.5k|    return retval == READSTAT_OK ? buffer_len : -1;
  ------------------
  |  Branch (323:12): [True: 14.4k, False: 39]
  ------------------
  324|  14.5k|}
readstat_sas7bcat_read.c:sas7bcat_read_block:
  327|  3.73k|        int start_page, int start_page_pos, sas7bcat_ctx_t *ctx) {
  328|  3.73k|    readstat_error_t retval = READSTAT_OK;
  329|  3.73k|    readstat_io_t *io = ctx->io;
  330|  3.73k|    int next_page = start_page;
  331|  3.73k|    int next_page_pos = start_page_pos;
  332|  3.73k|    int link_count = 0;
  333|       |
  334|  3.73k|    int chain_link_len = 0;
  335|  3.73k|    int buffer_offset = 0;
  336|       |
  337|  3.73k|    char chain_link[32];
  338|  3.73k|    int chain_link_header_len = 16;
  339|  3.73k|    if (ctx->u64) {
  ------------------
  |  Branch (339:9): [True: 601, False: 3.13k]
  ------------------
  340|    601|        chain_link_header_len = 32;
  341|    601|    }
  342|       |
  343|  25.2k|    while (next_page > 0 && next_page_pos > 0 && next_page <= ctx->page_count && link_count++ < ctx->page_count) {
  ------------------
  |  Branch (343:12): [True: 23.9k, False: 1.28k]
  |  Branch (343:29): [True: 23.1k, False: 882]
  |  Branch (343:50): [True: 21.9k, False: 1.14k]
  |  Branch (343:82): [True: 21.5k, False: 368]
  ------------------
  344|  21.5k|        if (io->seek(ctx->header_size+(next_page-1)*ctx->page_size+next_page_pos, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (344:13): [True: 0, False: 21.5k]
  ------------------
  345|      0|            retval = READSTAT_ERROR_SEEK;
  346|      0|            goto cleanup;
  347|      0|        }
  348|  21.5k|        if (io->read(chain_link, chain_link_header_len, io->io_ctx) < chain_link_header_len) {
  ------------------
  |  Branch (348:13): [True: 0, False: 21.5k]
  ------------------
  349|      0|            retval = READSTAT_ERROR_READ;
  350|      0|            goto cleanup;
  351|      0|        }
  352|  21.5k|        if (ctx->u64) {
  ------------------
  |  Branch (352:13): [True: 1.42k, False: 20.1k]
  ------------------
  353|  1.42k|            next_page = sas_read4(&chain_link[0], ctx->bswap);
  354|  1.42k|            next_page_pos = sas_read2(&chain_link[8], ctx->bswap);
  355|  1.42k|            chain_link_len = sas_read2(&chain_link[10], ctx->bswap);
  356|  20.1k|        } else {
  357|  20.1k|            next_page = sas_read4(&chain_link[0], ctx->bswap);
  358|  20.1k|            next_page_pos = sas_read2(&chain_link[4], ctx->bswap);
  359|  20.1k|            chain_link_len = sas_read2(&chain_link[6], ctx->bswap);
  360|  20.1k|        }
  361|  21.5k|        if (buffer_offset + chain_link_len > buffer_len) {
  ------------------
  |  Branch (361:13): [True: 0, False: 21.5k]
  ------------------
  362|      0|            retval = READSTAT_ERROR_PARSE;
  363|      0|            goto cleanup;
  364|      0|        }
  365|  21.5k|        if (io->read(buffer + buffer_offset, chain_link_len, io->io_ctx) < chain_link_len) {
  ------------------
  |  Branch (365:13): [True: 60, False: 21.5k]
  ------------------
  366|     60|            retval = READSTAT_ERROR_READ;
  367|     60|            goto cleanup;
  368|     60|        }
  369|  21.5k|        buffer_offset += chain_link_len;
  370|  21.5k|    }
  371|  3.73k|cleanup:
  372|       |
  373|  3.73k|    return retval;
  374|  3.73k|}
readstat_sas7bcat_read.c:sas7bcat_parse_block:
  152|  3.67k|static readstat_error_t sas7bcat_parse_block(const char *data, size_t data_size, sas7bcat_ctx_t *ctx) {
  153|  3.67k|    readstat_error_t retval = READSTAT_OK;
  154|       |
  155|  3.67k|    size_t pad = 0;
  156|  3.67k|    uint64_t label_count_capacity = 0;
  157|  3.67k|    uint64_t label_count_used = 0;
  158|  3.67k|    int payload_offset = 106;
  159|  3.67k|    uint16_t flags = 0;
  160|  3.67k|    char name[4*32+1];
  161|       |
  162|  3.67k|    if (data_size < payload_offset)
  ------------------
  |  Branch (162:9): [True: 792, False: 2.88k]
  ------------------
  163|    792|        goto cleanup;
  164|       |
  165|  2.88k|    flags = sas_read2(&data[2], ctx->bswap);
  166|  2.88k|    pad = (flags & 0x08) ? 4 : 0; // might be 0x10, not sure
  ------------------
  |  Branch (166:11): [True: 1.20k, False: 1.68k]
  ------------------
  167|  2.88k|    if (ctx->u64) {
  ------------------
  |  Branch (167:9): [True: 522, False: 2.36k]
  ------------------
  168|    522|        label_count_capacity = sas_read8(&data[42+pad], ctx->bswap);
  169|    522|        label_count_used = sas_read8(&data[50+pad], ctx->bswap);
  170|       |
  171|    522|        payload_offset += 32;
  172|  2.36k|    } else {
  173|  2.36k|        label_count_capacity = sas_read4(&data[38+pad], ctx->bswap);
  174|  2.36k|        label_count_used = sas_read4(&data[42+pad], ctx->bswap);
  175|  2.36k|    }
  176|       |
  177|  2.88k|    if ((retval = readstat_convert(name, sizeof(name), &data[8], 8, ctx->converter)) != READSTAT_OK)
  ------------------
  |  Branch (177:9): [True: 5, False: 2.87k]
  ------------------
  178|      5|        goto cleanup;
  179|       |
  180|  2.87k|    if (pad) {
  ------------------
  |  Branch (180:9): [True: 1.20k, False: 1.67k]
  ------------------
  181|  1.20k|        pad += 16;
  182|  1.20k|    }
  183|       |
  184|  2.87k|    if (((flags & 0x80) && !ctx->u64) || ((flags & 0x20) && ctx->u64)) { // has long name
  ------------------
  |  Branch (184:10): [True: 1.51k, False: 1.36k]
  |  Branch (184:28): [True: 1.20k, False: 306]
  |  Branch (184:43): [True: 616, False: 1.05k]
  |  Branch (184:61): [True: 319, False: 297]
  ------------------
  185|  1.52k|        if (data_size < payload_offset + pad + 32)
  ------------------
  |  Branch (185:13): [True: 314, False: 1.21k]
  ------------------
  186|    314|            goto cleanup;
  187|       |
  188|  1.21k|        retval = readstat_convert(name, sizeof(name), &data[payload_offset+pad], 32, ctx->converter);
  189|  1.21k|        if (retval != READSTAT_OK)
  ------------------
  |  Branch (189:13): [True: 1, False: 1.21k]
  ------------------
  190|      1|            goto cleanup;
  191|  1.21k|        pad += 32;
  192|  1.21k|    }
  193|       |
  194|  2.56k|    if (data_size < payload_offset + pad)
  ------------------
  |  Branch (194:9): [True: 230, False: 2.33k]
  ------------------
  195|    230|        goto cleanup;
  196|       |
  197|  2.33k|    if (label_count_used == 0)
  ------------------
  |  Branch (197:9): [True: 723, False: 1.60k]
  ------------------
  198|    723|        goto cleanup;
  199|       |
  200|  1.60k|    if ((retval = sas7bcat_parse_value_labels(&data[payload_offset+pad], data_size - payload_offset - pad,
  ------------------
  |  Branch (200:9): [True: 481, False: 1.12k]
  ------------------
  201|  1.60k|                    label_count_used, label_count_capacity, name, ctx)) != READSTAT_OK)
  202|    481|        goto cleanup;
  203|       |
  204|  3.67k|cleanup:
  205|  3.67k|    return retval;
  206|  1.60k|}
readstat_sas7bcat_read.c:sas7bcat_parse_value_labels:
   46|  1.60k|        int label_count_used, int label_count_capacity, const char *name, sas7bcat_ctx_t *ctx) {
   47|  1.60k|    readstat_error_t retval = READSTAT_OK;
   48|  1.60k|    int i;
   49|  1.60k|    const char *lbp1 = value_start;
   50|  1.60k|    uint32_t *value_offset = readstat_calloc(label_count_used, sizeof(uint32_t));
   51|       |    /* Doubles appear to be stored as big-endian, always */
   52|  1.60k|    int bswap_doubles = machine_is_little_endian();
   53|  1.60k|    int is_string = (name[0] == '$');
   54|  1.60k|    char *label = NULL;
   55|       |
   56|  1.60k|    if (value_offset == NULL) {
  ------------------
  |  Branch (56:9): [True: 169, False: 1.44k]
  ------------------
   57|    169|        retval = READSTAT_ERROR_MALLOC;
   58|    169|        goto cleanup;
   59|    169|    }
   60|       |
   61|       |    /* Pass 1 -- find out the offset of the labels */
   62|  6.07M|    for (i=0; i<label_count_capacity; i++) {
  ------------------
  |  Branch (62:15): [True: 6.06M, False: 1.30k]
  ------------------
   63|  6.06M|        if (&lbp1[4] - value_start > value_labels_len || sas_read2(&lbp1[2], ctx->bswap) < 0) {
  ------------------
  |  Branch (63:13): [True: 55, False: 6.06M]
  |  Branch (63:58): [True: 0, False: 6.06M]
  ------------------
   64|     55|            retval = READSTAT_ERROR_PARSE;
   65|     55|            goto cleanup;
   66|     55|        }
   67|  6.06M|        if (i<label_count_used) {
  ------------------
  |  Branch (67:13): [True: 106k, False: 5.96M]
  ------------------
   68|   106k|            if (&lbp1[10+ctx->pad1+4] - value_start > value_labels_len) {
  ------------------
  |  Branch (68:17): [True: 7, False: 106k]
  ------------------
   69|      7|                retval = READSTAT_ERROR_PARSE;
   70|      7|                goto cleanup;
   71|      7|            }
   72|   106k|            uint32_t label_pos = sas_read4(&lbp1[10+ctx->pad1], ctx->bswap);
   73|   106k|            if (label_pos >= label_count_used) {
  ------------------
  |  Branch (73:17): [True: 77, False: 106k]
  ------------------
   74|     77|                retval = READSTAT_ERROR_PARSE;
   75|     77|                goto cleanup;
   76|     77|            }
   77|   106k|            value_offset[label_pos] = lbp1 - value_start;
   78|   106k|        }
   79|  6.06M|        lbp1 += 6 + sas_read2(&lbp1[2], ctx->bswap);
   80|  6.06M|    }
   81|       |
   82|  1.30k|    const char *lbp2 = lbp1;
   83|       |
   84|       |    /* Pass 2 -- parse pairs of values & labels */
   85|  25.8k|    for (i=0; i<label_count_used && i<label_count_capacity; i++) {
  ------------------
  |  Branch (85:15): [True: 25.5k, False: 291]
  |  Branch (85:37): [True: 24.6k, False: 837]
  ------------------
   86|  24.6k|        lbp1 = value_start + value_offset[i];
   87|       |
   88|  24.6k|        if (&lbp1[30] - value_start > value_labels_len ||
  ------------------
  |  Branch (88:13): [True: 6, False: 24.6k]
  ------------------
   89|  24.6k|                &lbp2[10] - value_start > value_labels_len) {
  ------------------
  |  Branch (89:17): [True: 119, False: 24.5k]
  ------------------
   90|    125|            retval = READSTAT_ERROR_PARSE;
   91|    125|            goto cleanup;
   92|    125|        }
   93|  24.5k|        readstat_value_t value = { .type = is_string ? READSTAT_TYPE_STRING : READSTAT_TYPE_DOUBLE };
  ------------------
  |  Branch (93:44): [True: 611, False: 23.9k]
  ------------------
   94|  24.5k|        char string_val[4*16+1];
   95|  24.5k|        if (is_string) {
  ------------------
  |  Branch (95:13): [True: 611, False: 23.9k]
  ------------------
   96|    611|            size_t value_entry_len = 6 + sas_read2(&lbp1[2], ctx->bswap);
   97|    611|            retval = readstat_convert(string_val, sizeof(string_val),
   98|    611|                    &lbp1[value_entry_len-16], 16, ctx->converter);
   99|    611|            if (retval != READSTAT_OK)
  ------------------
  |  Branch (99:17): [True: 2, False: 609]
  ------------------
  100|      2|                goto cleanup;
  101|       |
  102|    609|            value.v.string_value = string_val;
  103|  23.9k|        } else {
  104|  23.9k|            uint64_t val = sas_read8(&lbp1[22], bswap_doubles);
  105|  23.9k|            double dval = NAN;
  106|  23.9k|            if ((val | 0xFF0000000000) == 0xFFFFFFFFFFFF) {
  ------------------
  |  Branch (106:17): [True: 2.56k, False: 21.3k]
  ------------------
  107|  2.56k|                sas_assign_tag(&value, (val >> 40));
  108|  21.3k|            } else {
  109|  21.3k|                memcpy(&dval, &val, 8);
  110|  21.3k|                if (dval > 0.0) {
  ------------------
  |  Branch (110:21): [True: 18.4k, False: 2.91k]
  ------------------
  111|  18.4k|                    val = ~val;
  112|  18.4k|                    memcpy(&dval, &val, 8);
  113|  18.4k|                } else {
  114|  2.91k|                    dval *= -1.0;
  115|  2.91k|                }
  116|  21.3k|            }
  117|       |
  118|  23.9k|            value.v.double_value = dval;
  119|  23.9k|        }
  120|  24.5k|        size_t label_len = sas_read2(&lbp2[8], ctx->bswap);
  121|  24.5k|        if (&lbp2[10] > value_start + value_labels_len) {
  ------------------
  |  Branch (121:13): [True: 0, False: 24.5k]
  ------------------
  122|      0|            retval = READSTAT_ERROR_PARSE;
  123|      0|            goto cleanup;
  124|      0|        }
  125|       |        /* Some labels seem to overflow the reported block length, truncate it */
  126|       |        /* (Observed with formats.sasbcat from GSS2021, produced with 9.0401M6X64_SR12R2 */
  127|  24.5k|        if (label_len > value_start + value_labels_len - &lbp2[10]) {
  ------------------
  |  Branch (127:13): [True: 397, False: 24.1k]
  ------------------
  128|    397|            label_len = value_start + value_labels_len - &lbp2[10];
  129|    397|        }
  130|  24.5k|        if (ctx->value_label_handler) {
  ------------------
  |  Branch (130:13): [True: 24.5k, False: 0]
  ------------------
  131|  24.5k|            label = realloc(label, 4 * label_len + 1);
  132|  24.5k|            retval = readstat_convert(label, 4 * label_len + 1,
  133|  24.5k|                    &lbp2[10], label_len, ctx->converter);
  134|  24.5k|            if (retval != READSTAT_OK)
  ------------------
  |  Branch (134:17): [True: 46, False: 24.5k]
  ------------------
  135|     46|                goto cleanup;
  136|       |
  137|  24.5k|            if (ctx->value_label_handler(name, value, label, ctx->user_ctx) != READSTAT_HANDLER_OK) {
  ------------------
  |  Branch (137:17): [True: 0, False: 24.5k]
  ------------------
  138|      0|                retval = READSTAT_ERROR_USER_ABORT;
  139|      0|                goto cleanup;
  140|      0|            }
  141|  24.5k|        }
  142|       |
  143|  24.5k|        lbp2 += 8 + 2 + label_len + 1;
  144|  24.5k|    }
  145|       |
  146|  1.60k|cleanup:
  147|  1.60k|    free(label);
  148|  1.60k|    free(value_offset);
  149|  1.60k|    return retval;
  150|  1.30k|}
readstat_sas7bcat_read.c:sas7bcat_ctx_free:
   36|  1.86k|static void sas7bcat_ctx_free(sas7bcat_ctx_t *ctx) {
   37|  1.86k|    if (ctx->converter)
  ------------------
  |  Branch (37:9): [True: 790, False: 1.07k]
  ------------------
   38|    790|        iconv_close(ctx->converter);
   39|  1.86k|    if (ctx->block_pointers)
  ------------------
  |  Branch (39:9): [True: 1.86k, False: 0]
  ------------------
   40|  1.86k|        free(ctx->block_pointers);
   41|       |
   42|  1.86k|    free(ctx);
   43|  1.86k|}

rt_open_handler:
    8|  1.86k|int rt_open_handler(const char *path, void *io_ctx) {
    9|  1.86k|    return 0;
   10|  1.86k|}
rt_close_handler:
   12|  1.86k|int rt_close_handler(void *io_ctx) {
   13|  1.86k|    return 0;
   14|  1.86k|}
rt_seek_handler:
   17|  72.7k|        readstat_io_flags_t whence, void *io_ctx) {
   18|  72.7k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   19|  72.7k|    readstat_off_t newpos = -1;
   20|  72.7k|    if (whence == READSTAT_SEEK_SET) {
  ------------------
  |  Branch (20:9): [True: 69.5k, False: 3.19k]
  ------------------
   21|  69.5k|        newpos = offset;
   22|  69.5k|    } else if (whence == READSTAT_SEEK_CUR) {
  ------------------
  |  Branch (22:16): [True: 3.19k, False: 0]
  ------------------
   23|  3.19k|        newpos = buffer_ctx->pos + offset;
   24|  3.19k|    } else if (whence == READSTAT_SEEK_END) {
  ------------------
  |  Branch (24:16): [True: 0, False: 0]
  ------------------
   25|      0|        newpos = buffer_ctx->buffer->used + offset;
   26|      0|    }
   27|       |
   28|  72.7k|    if (newpos < 0)
  ------------------
  |  Branch (28:9): [True: 0, False: 72.7k]
  ------------------
   29|      0|        return -1;
   30|       |
   31|  72.7k|    if (newpos > buffer_ctx->buffer->used)
  ------------------
  |  Branch (31:9): [True: 264, False: 72.5k]
  ------------------
   32|    264|        return -1;
   33|       |
   34|  72.5k|    buffer_ctx->pos = newpos;
   35|  72.5k|    return newpos;
   36|  72.7k|}
rt_read_handler:
   38|   104k|ssize_t rt_read_handler(void *buf, size_t nbytes, void *io_ctx) {
   39|   104k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   40|   104k|    ssize_t bytes_copied = 0;
   41|   104k|    ssize_t bytes_left = buffer_ctx->buffer->used - buffer_ctx->pos;
   42|   104k|    if (nbytes <= bytes_left) {
  ------------------
  |  Branch (42:9): [True: 104k, False: 318]
  ------------------
   43|   104k|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, nbytes);
   44|   104k|        bytes_copied = nbytes;
   45|   104k|    } else if (bytes_left > 0) {
  ------------------
  |  Branch (45:16): [True: 164, False: 154]
  ------------------
   46|    164|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, bytes_left);
   47|    164|        bytes_copied = bytes_left;
   48|    164|    }
   49|   104k|    buffer_ctx->pos += bytes_copied;
   50|   104k|    return bytes_copied;
   51|   104k|}

