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

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

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

readstat_convert:
   39|  10.2k|readstat_error_t readstat_convert(char *dst, size_t dst_len, const char *src, size_t src_len, iconv_t converter) {
   40|  10.2k|    return readstat_convert_impl(dst, dst_len, src, src_len, converter, 1);
   41|  10.2k|}
readstat_convert.c:readstat_convert_impl:
    8|  10.2k|        iconv_t converter, int trim_spaces) {
    9|       |    /* strip off spaces from the input because the programs use ASCII space
   10|       |     * padding even with non-ASCII encoding. */
   11|  92.1k|    while (src_len && ((trim_spaces && src[src_len-1] == ' ') || src[src_len-1] == '\0')) {
  ------------------
  |  Branch (11:12): [True: 88.0k, False: 4.06k]
  |  Branch (11:25): [True: 88.0k, False: 0]
  |  Branch (11:40): [True: 9.67k, False: 78.4k]
  |  Branch (11:66): [True: 72.2k, False: 6.19k]
  ------------------
   12|  81.8k|        src_len--;
   13|  81.8k|    }
   14|  10.2k|    if (dst_len == 0) {
  ------------------
  |  Branch (14:9): [True: 0, False: 10.2k]
  ------------------
   15|      0|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   16|  10.2k|    } else if (converter) {
  ------------------
  |  Branch (16:16): [True: 8.43k, False: 1.82k]
  ------------------
   17|  8.43k|        size_t dst_left = dst_len - 1;
   18|  8.43k|        char *dst_end = dst;
   19|  8.43k|        size_t status = iconv(converter, (readstat_iconv_inbuf_t)&src, &src_len, &dst_end, &dst_left);
   20|  8.43k|        if (status == (size_t)-1) {
  ------------------
  |  Branch (20:13): [True: 356, False: 8.08k]
  ------------------
   21|    356|            if (errno == E2BIG) {
  ------------------
  |  Branch (21:17): [True: 0, False: 356]
  ------------------
   22|      0|                return READSTAT_ERROR_CONVERT_LONG_STRING;
   23|    356|            } else if (errno == EILSEQ) {
  ------------------
  |  Branch (23:24): [True: 24, False: 332]
  ------------------
   24|     24|                return READSTAT_ERROR_CONVERT_BAD_STRING;
   25|    332|            } else if (errno != EINVAL) { /* EINVAL indicates improper truncation; accept it */
  ------------------
  |  Branch (25:24): [True: 0, False: 332]
  ------------------
   26|      0|                return READSTAT_ERROR_CONVERT;
   27|      0|            }
   28|    356|        }
   29|  8.41k|        dst[dst_len - dst_left - 1] = '\0';
   30|  8.41k|    } else if (src_len + 1 > dst_len) {
  ------------------
  |  Branch (30:16): [True: 0, False: 1.82k]
  ------------------
   31|      0|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   32|  1.82k|    } else {
   33|  1.82k|        memcpy(dst, src, src_len);
   34|  1.82k|        dst[src_len] = '\0';
   35|  1.82k|    }
   36|  10.2k|    return READSTAT_OK;
   37|  10.2k|}

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

readstat_malloc:
   10|  1.37k|void *readstat_malloc(size_t len) {
   11|  1.37k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|  2.75k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (11:9): [True: 0, False: 1.37k]
  |  Branch (11:34): [True: 0, False: 1.37k]
  ------------------
   12|      0|        return NULL;
   13|      0|    }
   14|  1.37k|    return malloc(len);
   15|  1.37k|}
readstat_calloc:
   17|  2.21k|void *readstat_calloc(size_t count, size_t size) {
   18|  2.21k|    if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  4.42k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  4.30k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  2.08k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (18:9): [True: 126, False: 2.08k]
  |  Branch (18:36): [True: 0, False: 2.08k]
  |  Branch (18:62): [True: 32, False: 2.05k]
  ------------------
   19|    158|        return NULL;
   20|    158|    }
   21|  2.05k|    if (count == 0 || size == 0) {
  ------------------
  |  Branch (21:9): [True: 6, False: 2.05k]
  |  Branch (21:23): [True: 0, False: 2.05k]
  ------------------
   22|      6|        return NULL;
   23|      6|    }
   24|  2.05k|    return calloc(count, size);
   25|  2.05k|}
readstat_realloc:
   27|  5.55k|void *readstat_realloc(void *ptr, size_t len) {
   28|  5.55k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|  11.1k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (28:9): [True: 4, False: 5.55k]
  |  Branch (28:34): [True: 0, False: 5.55k]
  ------------------
   29|      4|        if (ptr)
  ------------------
  |  Branch (29:13): [True: 1, False: 3]
  ------------------
   30|      1|            free(ptr);
   31|      4|        return NULL;
   32|      4|    }
   33|  5.55k|    return realloc(ptr, len);
   34|  5.55k|}

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

sas_read8:
  148|  5.17k|uint64_t sas_read8(const char *data, int bswap) {
  149|  5.17k|    uint64_t tmp;
  150|  5.17k|    memcpy(&tmp, data, 8);
  151|  5.17k|    return bswap ? byteswap8(tmp) : tmp;
  ------------------
  |  Branch (151:12): [True: 2.06k, False: 3.10k]
  ------------------
  152|  5.17k|}
sas_read4:
  154|   170k|uint32_t sas_read4(const char *data, int bswap) {
  155|   170k|    uint32_t tmp;
  156|   170k|    memcpy(&tmp, data, 4);
  157|   170k|    return bswap ? byteswap4(tmp) : tmp;
  ------------------
  |  Branch (157:12): [True: 6.17k, False: 164k]
  ------------------
  158|   170k|}
sas_read2:
  160|  2.49M|uint16_t sas_read2(const char *data, int bswap) {
  161|  2.49M|    uint16_t tmp;
  162|  2.49M|    memcpy(&tmp, data, 2);
  163|  2.49M|    return bswap ? byteswap2(tmp) : tmp;
  ------------------
  |  Branch (163:12): [True: 12.4k, False: 2.48M]
  ------------------
  164|  2.49M|}
sas_read_header:
  171|  1.94k|        readstat_error_handler error_handler, void *user_ctx) {
  172|  1.94k|    sas_header_start_t  header_start;
  173|  1.94k|    sas_header_end_t    header_end;
  174|  1.94k|    int retval = READSTAT_OK;
  175|  1.94k|    char error_buf[1024];
  176|  1.94k|    time_t epoch = sas_epoch();
  177|       |
  178|  1.94k|    if (io->read(&header_start, sizeof(sas_header_start_t), io->io_ctx) < sizeof(sas_header_start_t)) {
  ------------------
  |  Branch (178:9): [True: 20, False: 1.92k]
  ------------------
  179|     20|        retval = READSTAT_ERROR_READ;
  180|     20|        goto cleanup;
  181|     20|    }
  182|  1.92k|    if (memcmp(header_start.magic, sas7bdat_magic_number, sizeof(sas7bdat_magic_number)) != 0 &&
  ------------------
  |  Branch (182:9): [True: 1.18k, False: 736]
  ------------------
  183|  1.18k|            memcmp(header_start.magic, sas7bdat_magic_number_alt, sizeof(sas7bdat_magic_number_alt)) != 0 &&
  ------------------
  |  Branch (183:13): [True: 1.18k, False: 4]
  ------------------
  184|  1.18k|            memcmp(header_start.magic, sas7bcat_magic_number, sizeof(sas7bcat_magic_number)) != 0) {
  ------------------
  |  Branch (184:13): [True: 134, False: 1.05k]
  ------------------
  185|    134|        retval = READSTAT_ERROR_PARSE;
  186|    134|        goto cleanup;
  187|    134|    }
  188|  1.79k|    if (header_start.a1 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   88|  1.79k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (188:9): [True: 10, False: 1.78k]
  ------------------
  189|     10|        hinfo->pad1 = 4;
  190|     10|    }
  191|  1.79k|    if (header_start.a2 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   88|  1.79k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (191:9): [True: 502, False: 1.28k]
  ------------------
  192|    502|        hinfo->u64 = 1;
  193|    502|    }
  194|  1.79k|    int bswap = 0;
  195|  1.79k|    if (header_start.endian == SAS_ENDIAN_BIG) {
  ------------------
  |  |   81|  1.79k|#define SAS_ENDIAN_BIG       0x00
  ------------------
  |  Branch (195:9): [True: 412, False: 1.37k]
  ------------------
  196|    412|        bswap = machine_is_little_endian();
  197|    412|        hinfo->little_endian = 0;
  198|  1.37k|    } else if (header_start.endian == SAS_ENDIAN_LITTLE) {
  ------------------
  |  |   82|  1.37k|#define SAS_ENDIAN_LITTLE    0x01
  ------------------
  |  Branch (198:16): [True: 1.36k, False: 14]
  ------------------
  199|  1.36k|        bswap = !machine_is_little_endian();
  200|  1.36k|        hinfo->little_endian = 1;
  201|  1.36k|    } else {
  202|     14|        retval = READSTAT_ERROR_PARSE;
  203|     14|        goto cleanup;
  204|     14|    }
  205|  1.77k|    int i;
  206|  18.4k|    for (i=0; i<sizeof(_charset_table)/sizeof(_charset_table[0]); i++) {
  ------------------
  |  Branch (206:15): [True: 18.4k, False: 8]
  ------------------
  207|  18.4k|        if (header_start.encoding == _charset_table[i].code) {
  ------------------
  |  Branch (207:13): [True: 1.76k, False: 16.6k]
  ------------------
  208|  1.76k|            hinfo->encoding = _charset_table[i].name;
  209|  1.76k|            break;
  210|  1.76k|        }
  211|  18.4k|    }
  212|  1.77k|    if (hinfo->encoding == NULL) {
  ------------------
  |  Branch (212:9): [True: 8, False: 1.76k]
  ------------------
  213|      8|        if (error_handler) {
  ------------------
  |  Branch (213:13): [True: 0, False: 8]
  ------------------
  214|      0|            snprintf(error_buf, sizeof(error_buf), "Unsupported character set code: %d", header_start.encoding);
  215|      0|            error_handler(error_buf, user_ctx);
  216|      0|        }
  217|      8|        retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
  218|      8|        goto cleanup;
  219|      8|    }
  220|  1.76k|    memcpy(hinfo->table_name, header_start.table_name, sizeof(header_start.table_name));
  221|  1.76k|    if (io->seek(hinfo->pad1, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (221:9): [True: 3, False: 1.76k]
  ------------------
  222|      3|        retval = READSTAT_ERROR_SEEK;
  223|      3|        goto cleanup;
  224|      3|    }
  225|       |
  226|  1.76k|    double creation_time, modification_time, creation_time_diff, modification_time_diff;
  227|       |
  228|  1.76k|    if (io->read(&creation_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (228:9): [True: 12, False: 1.75k]
  ------------------
  229|     12|        retval = READSTAT_ERROR_READ;
  230|     12|        goto cleanup;
  231|     12|    }
  232|  1.75k|    if (bswap)
  ------------------
  |  Branch (232:9): [True: 395, False: 1.35k]
  ------------------
  233|    395|        creation_time = byteswap_double(creation_time);
  234|       |
  235|  1.75k|    if (io->read(&modification_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (235:9): [True: 7, False: 1.74k]
  ------------------
  236|      7|        retval = READSTAT_ERROR_READ;
  237|      7|        goto cleanup;
  238|      7|    }
  239|  1.74k|    if (bswap)
  ------------------
  |  Branch (239:9): [True: 391, False: 1.35k]
  ------------------
  240|    391|        modification_time = byteswap_double(modification_time);
  241|       |
  242|  1.74k|    if (io->read(&creation_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (242:9): [True: 6, False: 1.74k]
  ------------------
  243|      6|        retval = READSTAT_ERROR_READ;
  244|      6|        goto cleanup;
  245|      6|    }
  246|  1.74k|    if (bswap)
  ------------------
  |  Branch (246:9): [True: 388, False: 1.35k]
  ------------------
  247|    388|        creation_time_diff = byteswap_double(creation_time_diff);
  248|       |    
  249|  1.74k|    if (io->read(&modification_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (249:9): [True: 7, False: 1.73k]
  ------------------
  250|      7|        retval = READSTAT_ERROR_READ;
  251|      7|        goto cleanup;
  252|      7|    }
  253|  1.73k|    if (bswap)
  ------------------
  |  Branch (253:9): [True: 386, False: 1.34k]
  ------------------
  254|    386|        modification_time_diff = byteswap_double(modification_time_diff);
  255|       |    
  256|  1.73k|    hinfo->creation_time = sas_convert_time(creation_time, creation_time_diff, epoch);
  257|  1.73k|    hinfo->modification_time = sas_convert_time(modification_time, modification_time_diff, epoch);
  258|       |
  259|  1.73k|    uint32_t header_size, page_size;
  260|       |
  261|  1.73k|    if (io->read(&header_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (261:9): [True: 9, False: 1.72k]
  ------------------
  262|      9|        retval = READSTAT_ERROR_READ;
  263|      9|        goto cleanup;
  264|      9|    }
  265|  1.72k|    if (io->read(&page_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (265:9): [True: 3, False: 1.72k]
  ------------------
  266|      3|        retval = READSTAT_ERROR_READ;
  267|      3|        goto cleanup;
  268|      3|    }
  269|       |
  270|  1.72k|    hinfo->header_size = bswap ? byteswap4(header_size) : header_size;
  ------------------
  |  Branch (270:26): [True: 380, False: 1.34k]
  ------------------
  271|  1.72k|    hinfo->page_size = bswap ? byteswap4(page_size) : page_size;
  ------------------
  |  Branch (271:24): [True: 380, False: 1.34k]
  ------------------
  272|       |
  273|  1.72k|    if (hinfo->header_size < 1024 || hinfo->page_size < 1024) {
  ------------------
  |  Branch (273:9): [True: 11, False: 1.71k]
  |  Branch (273:38): [True: 12, False: 1.69k]
  ------------------
  274|     23|        retval = READSTAT_ERROR_PARSE;
  275|     23|        goto cleanup;
  276|     23|    }
  277|  1.69k|    if (hinfo->header_size > (1<<24) || hinfo->page_size > (1<<24)) {
  ------------------
  |  Branch (277:9): [True: 22, False: 1.67k]
  |  Branch (277:41): [True: 28, False: 1.64k]
  ------------------
  278|     50|        retval = READSTAT_ERROR_PARSE;
  279|     50|        goto cleanup;
  280|     50|    }
  281|       |
  282|  1.64k|    if (hinfo->u64) {
  ------------------
  |  Branch (282:9): [True: 466, False: 1.18k]
  ------------------
  283|    466|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_64BIT;
  ------------------
  |  |  123|    466|#define SAS_PAGE_HEADER_SIZE_64BIT  40
  ------------------
  284|    466|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_64BIT;
  ------------------
  |  |  120|    466|#define SAS_SUBHEADER_POINTER_SIZE_64BIT    24
  ------------------
  285|  1.18k|    } else {
  286|  1.18k|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_32BIT;
  ------------------
  |  |  122|  1.18k|#define SAS_PAGE_HEADER_SIZE_32BIT  24
  ------------------
  287|  1.18k|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_32BIT;
  ------------------
  |  |  119|  1.18k|#define SAS_SUBHEADER_POINTER_SIZE_32BIT    12
  ------------------
  288|  1.18k|    }
  289|       |
  290|  1.64k|    if (hinfo->u64) {
  ------------------
  |  Branch (290:9): [True: 466, False: 1.18k]
  ------------------
  291|    466|        uint64_t page_count;
  292|    466|        if (io->read(&page_count, sizeof(uint64_t), io->io_ctx) < sizeof(uint64_t)) {
  ------------------
  |  Branch (292:13): [True: 19, False: 447]
  ------------------
  293|     19|            retval = READSTAT_ERROR_READ;
  294|     19|            goto cleanup;
  295|     19|        }
  296|    447|        hinfo->page_count = bswap ? byteswap8(page_count) : page_count;
  ------------------
  |  Branch (296:29): [True: 26, False: 421]
  ------------------
  297|  1.18k|    } else {
  298|  1.18k|        uint32_t page_count;
  299|  1.18k|        if (io->read(&page_count, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (299:13): [True: 26, False: 1.15k]
  ------------------
  300|     26|            retval = READSTAT_ERROR_READ;
  301|     26|            goto cleanup;
  302|     26|        }
  303|  1.15k|        hinfo->page_count = bswap ? byteswap4(page_count) : page_count;
  ------------------
  |  Branch (303:29): [True: 291, False: 865]
  ------------------
  304|  1.15k|    }
  305|  1.60k|    if (hinfo->page_count > (1<<24)) {
  ------------------
  |  Branch (305:9): [True: 11, False: 1.59k]
  ------------------
  306|     11|        retval = READSTAT_ERROR_PARSE;
  307|     11|        goto cleanup;
  308|     11|    }
  309|       |    
  310|  1.59k|    if (io->seek(8, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (310:9): [True: 74, False: 1.51k]
  ------------------
  311|     74|        retval = READSTAT_ERROR_SEEK;
  312|     74|        if (error_handler) {
  ------------------
  |  Branch (312:13): [True: 0, False: 74]
  ------------------
  313|      0|            snprintf(error_buf, sizeof(error_buf), "ReadStat: Failed to seek forward by %d", 8);
  314|      0|            error_handler(error_buf, user_ctx);
  315|      0|        }
  316|     74|        goto cleanup;
  317|     74|    }
  318|  1.51k|    if (io->read(&header_end, sizeof(sas_header_end_t), io->io_ctx) < sizeof(sas_header_end_t)) {
  ------------------
  |  Branch (318:9): [True: 17, False: 1.50k]
  ------------------
  319|     17|        retval = READSTAT_ERROR_READ;
  320|     17|        goto cleanup;
  321|     17|    }
  322|  1.50k|    char major, revision_tag, revision;
  323|  1.50k|    int minor;
  324|       |    /* The release field is not NUL-terminated in the file; copy it into a
  325|       |     * terminated buffer before handing it to sscanf, which calls strlen on it. */
  326|  1.50k|    char release[sizeof(header_end.release)+1];
  327|  1.50k|    memcpy(release, header_end.release, sizeof(header_end.release));
  328|  1.50k|    release[sizeof(header_end.release)] = '\0';
  329|  1.50k|    if (sscanf(release, "%c.%04d%c%c", &major, &minor, &revision_tag, &revision) != 4) {
  ------------------
  |  Branch (329:9): [True: 3, False: 1.49k]
  ------------------
  330|      3|        retval = READSTAT_ERROR_PARSE;
  331|      3|        goto cleanup;
  332|      3|    }
  333|       |
  334|  1.49k|    if (major >= '1' && major <= '9') {
  ------------------
  |  Branch (334:9): [True: 1.48k, False: 10]
  |  Branch (334:25): [True: 1.48k, False: 8]
  ------------------
  335|  1.48k|        hinfo->major_version = major - '0';
  336|  1.48k|    } else if (major == 'V') {
  ------------------
  |  Branch (336:16): [True: 4, False: 14]
  ------------------
  337|       |        // It appears that SAS Visual Forecaster reports the major version as "V"
  338|       |        // Treat it as version 9 for all intents and purposes
  339|      4|        hinfo->major_version = 9;
  340|     14|    } else {
  341|     14|        retval = READSTAT_ERROR_PARSE;
  342|     14|        goto cleanup;
  343|     14|    }
  344|       |    // revision_tag is usually M, but J has been observed in the wild (not created with SAS?)
  345|       |    // files have also been observed with TK in place of the revision details.
  346|  1.48k|    if ((major == '8' || major == '9') && revision_tag != 'M' && revision_tag != 'J' && revision_tag != 'T') {
  ------------------
  |  Branch (346:10): [True: 145, False: 1.33k]
  |  Branch (346:26): [True: 269, False: 1.07k]
  |  Branch (346:43): [True: 42, False: 372]
  |  Branch (346:66): [True: 35, False: 7]
  |  Branch (346:89): [True: 19, False: 16]
  ------------------
  347|     19|        retval = READSTAT_ERROR_PARSE;
  348|     19|        goto cleanup;
  349|     19|    }
  350|  1.46k|    hinfo->minor_version = minor;
  351|  1.46k|    if (revision >= '0' && revision <= '9') {
  ------------------
  |  Branch (351:9): [True: 1.44k, False: 16]
  |  Branch (351:28): [True: 1.43k, False: 17]
  ------------------
  352|  1.43k|        hinfo->revision = revision - '0';
  353|  1.43k|    } else if (revision_tag == 'T' && revision == 'K') {
  ------------------
  |  Branch (353:16): [True: 17, False: 16]
  |  Branch (353:39): [True: 2, False: 15]
  ------------------
  354|       |        // files have been observed with TK in place of the revision details.
  355|      2|        hinfo->revision = 0;
  356|     31|    } else {
  357|     31|        retval = READSTAT_ERROR_PARSE;
  358|     31|        goto cleanup;
  359|     31|    }
  360|       |
  361|  1.43k|    if ((major == '8' || major == '9') && minor == 0 && hinfo->revision == 0) {
  ------------------
  |  Branch (361:10): [True: 127, False: 1.30k]
  |  Branch (361:26): [True: 255, False: 1.05k]
  |  Branch (361:43): [True: 17, False: 365]
  |  Branch (361:57): [True: 3, False: 14]
  ------------------
  362|       |        /* A bit of a hack, but most SAS installations are running a minor update */
  363|      3|        hinfo->vendor = READSTAT_VENDOR_STAT_TRANSFER;
  364|  1.43k|    } else {
  365|  1.43k|        hinfo->vendor = READSTAT_VENDOR_SAS;
  366|  1.43k|    }
  367|  1.43k|    if (io->seek(hinfo->header_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (367:9): [True: 50, False: 1.38k]
  ------------------
  368|     50|        retval = READSTAT_ERROR_SEEK;
  369|     50|        if (error_handler) {
  ------------------
  |  Branch (369:13): [True: 0, False: 50]
  ------------------
  370|      0|            snprintf(error_buf, sizeof(error_buf), 
  371|      0|                    "ReadStat: Failed to seek to position %" PRId64, hinfo->header_size);
  372|      0|            error_handler(error_buf, user_ctx);
  373|      0|        }
  374|     50|        goto cleanup;
  375|     50|    }
  376|       |
  377|  1.94k|cleanup:
  378|  1.94k|    return retval;
  379|  1.43k|}
sas_validate_tag:
  600|    965|readstat_error_t sas_validate_tag(char tag) {
  601|    965|    if (tag == '_' || (tag >= 'A' && tag <= 'Z'))
  ------------------
  |  Branch (601:9): [True: 198, False: 767]
  |  Branch (601:24): [True: 417, False: 350]
  |  Branch (601:38): [True: 215, False: 202]
  ------------------
  602|    413|        return READSTAT_OK;
  603|       |
  604|    552|    return READSTAT_ERROR_TAGGED_VALUE_IS_OUT_OF_RANGE;
  605|    965|}
sas_assign_tag:
  640|    965|void sas_assign_tag(readstat_value_t *value, uint8_t tag) {
  641|       |    /* We accommodate two tag schemes. In the first, the tag is an ASCII code
  642|       |     * given by uint8_t tag above. System missing is represented by an ASCII
  643|       |     * period. In the second scheme, (tag-2) is an offset from 'A', except when
  644|       |     * tag == 0, in which case it represents an underscore, or tag == 1, in
  645|       |     * which case it represents system-missing.
  646|       |     */
  647|    965|    if (tag == 0) {
  ------------------
  |  Branch (647:9): [True: 198, False: 767]
  ------------------
  648|    198|        tag = '_';
  649|    767|    } else if (tag >= 2 && tag < 28) {
  ------------------
  |  Branch (649:16): [True: 572, False: 195]
  |  Branch (649:28): [True: 199, False: 373]
  ------------------
  650|    199|        tag = 'A' + (tag - 2);
  651|    199|    }
  652|    965|    if (sas_validate_tag(tag) == READSTAT_OK) {
  ------------------
  |  Branch (652:9): [True: 413, False: 552]
  ------------------
  653|    413|        value->tag = tag;
  654|    413|        value->is_tagged_missing = 1;
  655|    552|    } else {
  656|    552|        value->tag = 0;
  657|    552|        value->is_system_missing = 1;
  658|    552|    }
  659|    965|}
readstat_sas.c:sas_epoch:
  132|  1.94k|static time_t sas_epoch(void) {
  133|  1.94k|    return - 3653 * 86400; // seconds between 01-01-1960 and 01-01-1970
  134|  1.94k|}
readstat_sas.c:sas_convert_time:
  136|  3.46k|static time_t sas_convert_time(double time, double time_diff, time_t epoch) {
  137|  3.46k|    time -= time_diff;
  138|  3.46k|    time += epoch;
  139|  3.46k|    if (isnan(time))
  ------------------
  |  Branch (139:9): [True: 115, False: 3.35k]
  ------------------
  140|    115|        return 0;
  141|  3.35k|    if (time > (double)LONG_MAX)
  ------------------
  |  Branch (141:9): [True: 411, False: 2.94k]
  ------------------
  142|    411|        return LONG_MAX;
  143|  2.94k|    if (time < (double)LONG_MIN)
  ------------------
  |  Branch (143:9): [True: 325, False: 2.61k]
  ------------------
  144|    325|        return LONG_MIN;
  145|  2.61k|    return time;
  146|  2.94k|}

readstat_parse_sas7bcat:
  429|  1.94k|readstat_error_t readstat_parse_sas7bcat(readstat_parser_t *parser, const char *path, void *user_ctx) {
  430|  1.94k|    readstat_error_t retval = READSTAT_OK;
  431|  1.94k|    readstat_io_t *io = parser->io;
  432|  1.94k|    int64_t i;
  433|  1.94k|    char *page = NULL;
  434|  1.94k|    char *buffer = NULL;
  435|       |
  436|  1.94k|    sas7bcat_ctx_t *ctx = calloc(1, sizeof(sas7bcat_ctx_t));
  437|  1.94k|    sas_header_info_t *hinfo = calloc(1, sizeof(sas_header_info_t));
  438|       |
  439|  1.94k|    ctx->block_pointers = malloc((ctx->block_pointers_capacity = 200) * sizeof(uint64_t));
  440|       |
  441|  1.94k|    ctx->value_label_handler = parser->handlers.value_label;
  442|  1.94k|    ctx->metadata_handler = parser->handlers.metadata;
  443|  1.94k|    ctx->input_encoding = parser->input_encoding;
  444|  1.94k|    ctx->output_encoding = parser->output_encoding;
  445|  1.94k|    ctx->user_ctx = user_ctx;
  446|  1.94k|    ctx->io = io;
  447|       |
  448|  1.94k|    if (io->open(path, io->io_ctx) == -1) {
  ------------------
  |  Branch (448:9): [True: 0, False: 1.94k]
  ------------------
  449|      0|        retval = READSTAT_ERROR_OPEN;
  450|      0|        goto cleanup;
  451|      0|    }
  452|       |
  453|  1.94k|    if ((retval = sas_read_header(io, hinfo, parser->handlers.error, user_ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (453:9): [True: 560, False: 1.38k]
  ------------------
  454|    560|        goto cleanup;
  455|    560|    }
  456|       |
  457|  1.38k|    ctx->u64 = hinfo->u64;
  458|  1.38k|    ctx->pad1 = hinfo->pad1;
  459|  1.38k|    ctx->bswap = machine_is_little_endian() ^ hinfo->little_endian;
  460|  1.38k|    ctx->header_size = hinfo->header_size;
  461|  1.38k|    ctx->page_count = hinfo->page_count;
  462|  1.38k|    ctx->page_size = hinfo->page_size;
  463|  1.38k|    if (ctx->input_encoding == NULL) {
  ------------------
  |  Branch (463:9): [True: 1.38k, False: 0]
  ------------------
  464|  1.38k|        ctx->input_encoding = hinfo->encoding;
  465|  1.38k|    }
  466|       |
  467|  1.38k|    ctx->xlsr_size = 212 + ctx->pad1;
  468|  1.38k|    ctx->xlsr_offset = 856 + 2 * ctx->pad1;
  469|  1.38k|    ctx->xlsr_O_offset = 50 + ctx->pad1;
  470|  1.38k|    if (ctx->u64) {
  ------------------
  |  Branch (470:9): [True: 396, False: 988]
  ------------------
  471|    396|        ctx->xlsr_offset += 144;
  472|    396|        ctx->xlsr_size += 72;
  473|    396|        ctx->xlsr_O_offset += 24;
  474|    396|    }
  475|       |
  476|  1.38k|    if (ctx->input_encoding && ctx->output_encoding && strcmp(ctx->input_encoding, ctx->output_encoding) != 0) {
  ------------------
  |  Branch (476:9): [True: 1.38k, False: 0]
  |  Branch (476:32): [True: 1.38k, False: 0]
  |  Branch (476:56): [True: 956, False: 428]
  ------------------
  477|    956|        iconv_t converter = iconv_open(ctx->output_encoding, ctx->input_encoding);
  478|    956|        if (converter == (iconv_t)-1) {
  ------------------
  |  Branch (478:13): [True: 2, False: 954]
  ------------------
  479|      2|            retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
  480|      2|            goto cleanup;
  481|      2|        }
  482|    954|        ctx->converter = converter;
  483|    954|    }
  484|       |
  485|  1.38k|    if (ctx->metadata_handler) {
  ------------------
  |  Branch (485:9): [True: 1.38k, False: 0]
  ------------------
  486|  1.38k|        char table_name[4*32+1];
  487|  1.38k|        readstat_metadata_t metadata = { 
  488|  1.38k|            .file_encoding = ctx->input_encoding, /* orig encoding? */
  489|  1.38k|            .modified_time = hinfo->modification_time,
  490|  1.38k|            .creation_time = hinfo->creation_time,
  491|  1.38k|            .file_format_version = hinfo->major_version,
  492|  1.38k|            .endianness = hinfo->little_endian ? READSTAT_ENDIAN_LITTLE : READSTAT_ENDIAN_BIG,
  ------------------
  |  Branch (492:27): [True: 1.14k, False: 239]
  ------------------
  493|  1.38k|            .is64bit = ctx->u64
  494|  1.38k|        };
  495|  1.38k|        retval = readstat_convert(table_name, sizeof(table_name),
  496|  1.38k|                hinfo->table_name, sizeof(hinfo->table_name), ctx->converter);
  497|  1.38k|        if (retval != READSTAT_OK)
  ------------------
  |  Branch (497:13): [True: 4, False: 1.37k]
  ------------------
  498|      4|            goto cleanup;
  499|       |
  500|  1.37k|        metadata.table_name = table_name;
  501|       |
  502|  1.37k|        if (ctx->metadata_handler(&metadata, ctx->user_ctx) != READSTAT_HANDLER_OK) {
  ------------------
  |  |   22|  1.37k|#define READSTAT_HANDLER_OK             0
  ------------------
  |  Branch (502:13): [True: 0, False: 1.37k]
  ------------------
  503|      0|            retval = READSTAT_ERROR_USER_ABORT;
  504|      0|            goto cleanup;
  505|      0|        }
  506|  1.37k|    }
  507|       |
  508|  1.37k|    if ((page = readstat_malloc(ctx->page_size)) == NULL) {
  ------------------
  |  Branch (508:9): [True: 0, False: 1.37k]
  ------------------
  509|      0|        retval = READSTAT_ERROR_MALLOC;
  510|      0|        goto cleanup;
  511|      0|    }
  512|  1.37k|    if (io->seek(ctx->header_size+SAS_CATALOG_FIRST_INDEX_PAGE*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  |   11|  1.37k|#define SAS_CATALOG_FIRST_INDEX_PAGE 1
  ------------------
  |  Branch (512:9): [True: 62, False: 1.31k]
  ------------------
  513|     62|        retval = READSTAT_ERROR_SEEK;
  514|     62|        goto cleanup;
  515|     62|    }
  516|  1.31k|    if (io->read(page, ctx->page_size, io->io_ctx) < ctx->page_size) {
  ------------------
  |  Branch (516:9): [True: 40, False: 1.27k]
  ------------------
  517|     40|        retval = READSTAT_ERROR_READ;
  518|     40|        goto cleanup;
  519|     40|    }
  520|       |
  521|  1.27k|    retval = sas7bcat_augment_index(&page[ctx->xlsr_offset], ctx->page_size - ctx->xlsr_offset, ctx);
  522|  1.27k|    if (retval != READSTAT_OK)
  ------------------
  |  Branch (522:9): [True: 0, False: 1.27k]
  ------------------
  523|      0|        goto cleanup;
  524|       |
  525|       |    // Pass 1 -- find the XLSR entries
  526|  26.4k|    for (i=SAS_CATALOG_USELESS_PAGES; i<ctx->page_count; i++) {
  ------------------
  |  |   12|  1.27k|#define SAS_CATALOG_USELESS_PAGES    3
  ------------------
  |  Branch (526:39): [True: 25.2k, False: 1.18k]
  ------------------
  527|  25.2k|        if (io->seek(ctx->header_size+i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (527:13): [True: 21, False: 25.2k]
  ------------------
  528|     21|            retval = READSTAT_ERROR_SEEK;
  529|     21|            goto cleanup;
  530|     21|        }
  531|  25.2k|        if (io->read(page, ctx->page_size, io->io_ctx) < ctx->page_size) {
  ------------------
  |  Branch (531:13): [True: 70, False: 25.1k]
  ------------------
  532|     70|            retval = READSTAT_ERROR_READ;
  533|     70|            goto cleanup;
  534|     70|        }
  535|  25.1k|        int offset = ctx->u64 ? 32 : 16;
  ------------------
  |  Branch (535:22): [True: 1.26k, False: 23.9k]
  ------------------
  536|  25.1k|        if (memcmp(&page[offset], "XLSR", sizeof("XLSR")-1) == 0) {
  ------------------
  |  Branch (536:13): [True: 9.42k, False: 15.7k]
  ------------------
  537|  9.42k|            retval = sas7bcat_augment_index(&page[offset], ctx->page_size - offset, ctx);
  538|  9.42k|            if (retval != READSTAT_OK)
  ------------------
  |  Branch (538:17): [True: 0, False: 9.42k]
  ------------------
  539|      0|                goto cleanup;
  540|  9.42k|        }
  541|  25.1k|    }
  542|       |
  543|  1.18k|    sas7bcat_sort_index(ctx);
  544|  1.18k|    sas7bcat_uniq_index(ctx);
  545|       |
  546|       |    // Pass 2 -- look up the individual block pointers
  547|  18.0k|    for (i=0; i<ctx->block_pointers_used; i++) {
  ------------------
  |  Branch (547:15): [True: 17.6k, False: 406]
  ------------------
  548|  17.6k|        int start_page = ctx->block_pointers[i] >> 32;
  549|  17.6k|        int start_page_pos = (ctx->block_pointers[i]) & 0xFFFF;
  550|       |
  551|  17.6k|        int buffer_len = sas7bcat_block_size(start_page, start_page_pos, ctx, &retval);
  552|  17.6k|        if (buffer_len == -1) {
  ------------------
  |  Branch (552:13): [True: 30, False: 17.6k]
  ------------------
  553|     30|            goto cleanup;
  554|  17.6k|        } else if (buffer_len == 0) {
  ------------------
  |  Branch (554:20): [True: 12.1k, False: 5.47k]
  ------------------
  555|  12.1k|            continue;
  556|  12.1k|        }
  557|  5.47k|        if ((buffer = readstat_realloc(buffer, buffer_len)) == NULL) {
  ------------------
  |  Branch (557:13): [True: 4, False: 5.47k]
  ------------------
  558|      4|            retval = READSTAT_ERROR_MALLOC;
  559|      4|            goto cleanup;
  560|      4|        }
  561|  5.47k|        if ((retval = sas7bcat_read_block(buffer, buffer_len, start_page, start_page_pos, ctx)) != READSTAT_OK)
  ------------------
  |  Branch (561:13): [True: 50, False: 5.42k]
  ------------------
  562|     50|            goto cleanup;
  563|  5.42k|        if ((retval = sas7bcat_parse_block(buffer, buffer_len, ctx)) != READSTAT_OK)
  ------------------
  |  Branch (563:13): [True: 695, False: 4.72k]
  ------------------
  564|    695|            goto cleanup;
  565|  5.42k|    }
  566|       |
  567|  1.94k|cleanup:
  568|  1.94k|    io->close(io->io_ctx);
  569|  1.94k|    if (page)
  ------------------
  |  Branch (569:9): [True: 1.37k, False: 566]
  ------------------
  570|  1.37k|        free(page);
  571|  1.94k|    if (buffer)
  ------------------
  |  Branch (571:9): [True: 917, False: 1.02k]
  ------------------
  572|    917|        free(buffer);
  573|  1.94k|    if (ctx)
  ------------------
  |  Branch (573:9): [True: 1.94k, False: 0]
  ------------------
  574|  1.94k|        sas7bcat_ctx_free(ctx);
  575|  1.94k|    if (hinfo)
  ------------------
  |  Branch (575:9): [True: 1.94k, False: 0]
  ------------------
  576|  1.94k|        free(hinfo);
  577|       |
  578|  1.94k|    return retval;
  579|  1.18k|}
readstat_sas7bcat_read.c:sas7bcat_augment_index:
  261|  10.7k|static readstat_error_t sas7bcat_augment_index(const char *index, size_t len, sas7bcat_ctx_t *ctx) {
  262|  10.7k|    const char *xlsr = index;
  263|  10.7k|    readstat_error_t retval = READSTAT_OK;
  264|  45.6k|    while (xlsr + ctx->xlsr_size <= index + len) {
  ------------------
  |  Branch (264:12): [True: 36.7k, False: 8.90k]
  ------------------
  265|  36.7k|        if (memcmp(xlsr, "XLSR", 4) != 0) // some block pointers seem to have 8 bytes of extra padding
  ------------------
  |  Branch (265:13): [True: 17.6k, False: 19.1k]
  ------------------
  266|  17.6k|            xlsr += 8;
  267|  36.7k|        if (memcmp(xlsr, "XLSR", 4) != 0)
  ------------------
  |  Branch (267:13): [True: 1.79k, False: 34.9k]
  ------------------
  268|  1.79k|            break;
  269|       |
  270|  34.9k|        if (xlsr[ctx->xlsr_O_offset] == 'O') {
  ------------------
  |  Branch (270:13): [True: 32.6k, False: 2.26k]
  ------------------
  271|  32.6k|            uint64_t page = 0, pos = 0;
  272|  32.6k|            if (ctx->u64) {
  ------------------
  |  Branch (272:17): [True: 1.31k, False: 31.3k]
  ------------------
  273|  1.31k|                page = sas_read8(&xlsr[8], ctx->bswap);
  274|  1.31k|                pos = sas_read2(&xlsr[16], ctx->bswap);
  275|  31.3k|            } else {
  276|  31.3k|                page = sas_read4(&xlsr[4], ctx->bswap);
  277|  31.3k|                pos = sas_read2(&xlsr[8], ctx->bswap);
  278|  31.3k|            }
  279|  32.6k|            ctx->block_pointers[ctx->block_pointers_used++] = (page << 32) + pos;
  280|  32.6k|        }
  281|       |
  282|  34.9k|        if (ctx->block_pointers_used == ctx->block_pointers_capacity) {
  ------------------
  |  Branch (282:13): [True: 81, False: 34.8k]
  ------------------
  283|     81|            ctx->block_pointers = readstat_realloc(ctx->block_pointers, (ctx->block_pointers_capacity *= 2) * sizeof(uint64_t));
  284|     81|            if (ctx->block_pointers == NULL) {
  ------------------
  |  Branch (284:17): [True: 0, False: 81]
  ------------------
  285|      0|                retval = READSTAT_ERROR_MALLOC;
  286|      0|                goto cleanup;
  287|      0|            }
  288|     81|        }
  289|       |
  290|  34.9k|        xlsr += ctx->xlsr_size;
  291|  34.9k|    }
  292|  10.7k|cleanup:
  293|  10.7k|    return retval;
  294|  10.7k|}
readstat_sas7bcat_read.c:sas7bcat_sort_index:
  302|  1.18k|static void sas7bcat_sort_index(sas7bcat_ctx_t *ctx) {
  303|  1.18k|    if (ctx->block_pointers_used == 0)
  ------------------
  |  Branch (303:9): [True: 110, False: 1.07k]
  ------------------
  304|    110|        return;
  305|       |
  306|  1.07k|    int i;
  307|  1.52k|    for (i=1; i<ctx->block_pointers_used; i++) {
  ------------------
  |  Branch (307:15): [True: 719, False: 805]
  ------------------
  308|    719|        if (ctx->block_pointers[i] < ctx->block_pointers[i-1]) {
  ------------------
  |  Branch (308:13): [True: 270, False: 449]
  ------------------
  309|    270|            qsort(ctx->block_pointers, ctx->block_pointers_used, sizeof(uint64_t), &compare_block_pointers);
  310|    270|            break;
  311|    270|        }
  312|    719|    }
  313|  1.07k|}
readstat_sas7bcat_read.c:compare_block_pointers:
  296|   132k|static int compare_block_pointers(const void *elem1, const void *elem2) {
  297|   132k|    uint64_t v1 = *(const uint64_t *)elem1;
  298|   132k|    uint64_t v2 = *(const uint64_t *)elem2;
  299|   132k|    return v1 - v2;
  300|   132k|}
readstat_sas7bcat_read.c:sas7bcat_uniq_index:
  315|  1.18k|static void sas7bcat_uniq_index(sas7bcat_ctx_t *ctx) {
  316|  1.18k|    if (ctx->block_pointers_used == 0)
  ------------------
  |  Branch (316:9): [True: 110, False: 1.07k]
  ------------------
  317|    110|        return;
  318|       |
  319|  1.07k|    int i;
  320|  1.07k|    int out_i = 1;
  321|  24.0k|    for (i=1; i<ctx->block_pointers_used; i++) {
  ------------------
  |  Branch (321:15): [True: 23.0k, False: 1.07k]
  ------------------
  322|  23.0k|        if (ctx->block_pointers[i] != ctx->block_pointers[i-1]) {
  ------------------
  |  Branch (322:13): [True: 17.2k, False: 5.78k]
  ------------------
  323|  17.2k|            if (out_i != i) {
  ------------------
  |  Branch (323:17): [True: 15.1k, False: 2.03k]
  ------------------
  324|  15.1k|                ctx->block_pointers[out_i] = ctx->block_pointers[i];
  325|  15.1k|            }
  326|  17.2k|            out_i++;
  327|  17.2k|        }
  328|  23.0k|    }
  329|  1.07k|    ctx->block_pointers_used = out_i;
  330|  1.07k|}
readstat_sas7bcat_read.c:sas7bcat_block_size:
  332|  17.6k|static int sas7bcat_block_size(int start_page, int start_page_pos, sas7bcat_ctx_t *ctx, readstat_error_t *outError) {
  333|  17.6k|    readstat_error_t retval = READSTAT_OK;
  334|  17.6k|    readstat_io_t *io = ctx->io;
  335|  17.6k|    int next_page = start_page;
  336|  17.6k|    int next_page_pos = start_page_pos;
  337|  17.6k|    int link_count = 0;
  338|       |
  339|  17.6k|    int buffer_len = 0;
  340|  17.6k|    int chain_link_len = 0;
  341|       |
  342|  17.6k|    char chain_link[32];
  343|  17.6k|    int chain_link_header_len = 16;
  344|  17.6k|    if (ctx->u64) {
  ------------------
  |  Branch (344:9): [True: 1.00k, False: 16.6k]
  ------------------
  345|  1.00k|        chain_link_header_len = 32;
  346|  1.00k|    }
  347|       |
  348|       |    // calculate buffer size needed
  349|  37.7k|    while (next_page > 0 && next_page_pos > 0 && next_page <= ctx->page_count && link_count++ < ctx->page_count) {
  ------------------
  |  Branch (349:12): [True: 30.6k, False: 7.03k]
  |  Branch (349:29): [True: 29.3k, False: 1.32k]
  |  Branch (349:50): [True: 20.3k, False: 8.96k]
  |  Branch (349:82): [True: 20.1k, False: 277]
  ------------------
  350|  20.1k|        if (io->seek(ctx->header_size+(next_page-1)*ctx->page_size+next_page_pos, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (350:13): [True: 19, False: 20.0k]
  ------------------
  351|     19|            retval = READSTAT_ERROR_SEEK;
  352|     19|            goto cleanup;
  353|     19|        }
  354|  20.0k|        if (io->read(chain_link, chain_link_header_len, io->io_ctx) < chain_link_header_len) {
  ------------------
  |  Branch (354:13): [True: 11, False: 20.0k]
  ------------------
  355|     11|            retval = READSTAT_ERROR_READ;
  356|     11|            goto cleanup;
  357|     11|        }
  358|       |
  359|  20.0k|        if (ctx->u64) {
  ------------------
  |  Branch (359:13): [True: 1.54k, False: 18.5k]
  ------------------
  360|  1.54k|            next_page = sas_read4(&chain_link[0], ctx->bswap);
  361|  1.54k|            next_page_pos = sas_read2(&chain_link[8], ctx->bswap);
  362|  1.54k|            chain_link_len = sas_read2(&chain_link[10], ctx->bswap);
  363|  18.5k|        } else {
  364|  18.5k|            next_page = sas_read4(&chain_link[0], ctx->bswap);
  365|  18.5k|            next_page_pos = sas_read2(&chain_link[4], ctx->bswap);
  366|  18.5k|            chain_link_len = sas_read2(&chain_link[6], ctx->bswap);
  367|  18.5k|        }
  368|       |
  369|  20.0k|        buffer_len += chain_link_len;
  370|  20.0k|    }
  371|       |
  372|  17.6k|cleanup:
  373|  17.6k|    if (outError)
  ------------------
  |  Branch (373:9): [True: 17.6k, False: 0]
  ------------------
  374|  17.6k|        *outError = retval;
  375|       |
  376|  17.6k|    return retval == READSTAT_OK ? buffer_len : -1;
  ------------------
  |  Branch (376:12): [True: 17.6k, False: 30]
  ------------------
  377|  17.6k|}
readstat_sas7bcat_read.c:sas7bcat_read_block:
  380|  5.47k|        int start_page, int start_page_pos, sas7bcat_ctx_t *ctx) {
  381|  5.47k|    readstat_error_t retval = READSTAT_OK;
  382|  5.47k|    readstat_io_t *io = ctx->io;
  383|  5.47k|    int next_page = start_page;
  384|  5.47k|    int next_page_pos = start_page_pos;
  385|  5.47k|    int link_count = 0;
  386|       |
  387|  5.47k|    int chain_link_len = 0;
  388|  5.47k|    int buffer_offset = 0;
  389|       |
  390|  5.47k|    char chain_link[32];
  391|  5.47k|    int chain_link_header_len = 16;
  392|  5.47k|    if (ctx->u64) {
  ------------------
  |  Branch (392:9): [True: 891, False: 4.58k]
  ------------------
  393|    891|        chain_link_header_len = 32;
  394|    891|    }
  395|       |
  396|  22.0k|    while (next_page > 0 && next_page_pos > 0 && next_page <= ctx->page_count && link_count++ < ctx->page_count) {
  ------------------
  |  Branch (396:12): [True: 19.0k, False: 2.93k]
  |  Branch (396:29): [True: 17.9k, False: 1.11k]
  |  Branch (396:50): [True: 16.8k, False: 1.12k]
  |  Branch (396:82): [True: 16.6k, False: 246]
  ------------------
  397|  16.6k|        if (io->seek(ctx->header_size+(next_page-1)*ctx->page_size+next_page_pos, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (397:13): [True: 0, False: 16.6k]
  ------------------
  398|      0|            retval = READSTAT_ERROR_SEEK;
  399|      0|            goto cleanup;
  400|      0|        }
  401|  16.6k|        if (io->read(chain_link, chain_link_header_len, io->io_ctx) < chain_link_header_len) {
  ------------------
  |  Branch (401:13): [True: 0, False: 16.6k]
  ------------------
  402|      0|            retval = READSTAT_ERROR_READ;
  403|      0|            goto cleanup;
  404|      0|        }
  405|  16.6k|        if (ctx->u64) {
  ------------------
  |  Branch (405:13): [True: 1.47k, False: 15.1k]
  ------------------
  406|  1.47k|            next_page = sas_read4(&chain_link[0], ctx->bswap);
  407|  1.47k|            next_page_pos = sas_read2(&chain_link[8], ctx->bswap);
  408|  1.47k|            chain_link_len = sas_read2(&chain_link[10], ctx->bswap);
  409|  15.1k|        } else {
  410|  15.1k|            next_page = sas_read4(&chain_link[0], ctx->bswap);
  411|  15.1k|            next_page_pos = sas_read2(&chain_link[4], ctx->bswap);
  412|  15.1k|            chain_link_len = sas_read2(&chain_link[6], ctx->bswap);
  413|  15.1k|        }
  414|  16.6k|        if (buffer_offset + chain_link_len > buffer_len) {
  ------------------
  |  Branch (414:13): [True: 0, False: 16.6k]
  ------------------
  415|      0|            retval = READSTAT_ERROR_PARSE;
  416|      0|            goto cleanup;
  417|      0|        }
  418|  16.6k|        if (io->read(buffer + buffer_offset, chain_link_len, io->io_ctx) < chain_link_len) {
  ------------------
  |  Branch (418:13): [True: 50, False: 16.5k]
  ------------------
  419|     50|            retval = READSTAT_ERROR_READ;
  420|     50|            goto cleanup;
  421|     50|        }
  422|  16.5k|        buffer_offset += chain_link_len;
  423|  16.5k|    }
  424|  5.47k|cleanup:
  425|       |
  426|  5.47k|    return retval;
  427|  5.47k|}
readstat_sas7bcat_read.c:sas7bcat_parse_block:
  188|  5.42k|static readstat_error_t sas7bcat_parse_block(const char *data, size_t data_size, sas7bcat_ctx_t *ctx) {
  189|  5.42k|    readstat_error_t retval = READSTAT_OK;
  190|       |
  191|  5.42k|    size_t pad = 0;
  192|  5.42k|    uint64_t label_count_capacity = 0;
  193|  5.42k|    uint64_t label_count_used = 0;
  194|  5.42k|    uint64_t default_label_pos = 0;
  195|  5.42k|    int string_offset = 0;
  196|  5.42k|    int payload_offset = 106;
  197|  5.42k|    uint16_t flags = 0;
  198|  5.42k|    char name[4*32+1];
  199|       |
  200|  5.42k|    if (data_size < payload_offset)
  ------------------
  |  Branch (200:9): [True: 1.00k, False: 4.42k]
  ------------------
  201|  1.00k|        goto cleanup;
  202|       |
  203|  4.42k|    flags = sas_read2(&data[2], ctx->bswap);
  204|  4.42k|    pad = (flags & 0x08) ? 4 : 0; // might be 0x10, not sure
  ------------------
  |  Branch (204:11): [True: 2.07k, False: 2.35k]
  ------------------
  205|  4.42k|    if (ctx->u64) {
  ------------------
  |  Branch (205:9): [True: 810, False: 3.61k]
  ------------------
  206|    810|        if (data_size < 128 + pad)
  ------------------
  |  Branch (206:13): [True: 213, False: 597]
  ------------------
  207|    213|            goto cleanup;
  208|    597|        label_count_capacity = sas_read8(&data[42+pad], ctx->bswap);
  209|    597|        label_count_used = sas_read8(&data[50+pad], ctx->bswap);
  210|    597|        default_label_pos = sas_read8(&data[74+pad], ctx->bswap);
  211|       |        /* Nonzero when the format's values are strings longer than 16 bytes,
  212|       |         * which shifts each value one byte to the right and NUL-pads it */
  213|    597|        string_offset = sas_read4(&data[124+pad], ctx->bswap);
  214|       |
  215|    597|        payload_offset += 32;
  216|  3.61k|    } else {
  217|  3.61k|        if (data_size < 106 + pad)
  ------------------
  |  Branch (217:13): [True: 226, False: 3.38k]
  ------------------
  218|    226|            goto cleanup;
  219|  3.38k|        label_count_capacity = sas_read4(&data[38+pad], ctx->bswap);
  220|  3.38k|        label_count_used = sas_read4(&data[42+pad], ctx->bswap);
  221|  3.38k|        default_label_pos = sas_read4(&data[66+pad], ctx->bswap);
  222|  3.38k|        string_offset = sas_read2(&data[104+pad], ctx->bswap);
  223|  3.38k|    }
  224|       |
  225|  3.98k|    if (string_offset < 0 || default_label_pos > label_count_used) {
  ------------------
  |  Branch (225:9): [True: 37, False: 3.94k]
  |  Branch (225:30): [True: 70, False: 3.87k]
  ------------------
  226|    107|        retval = READSTAT_ERROR_PARSE;
  227|    107|        goto cleanup;
  228|    107|    }
  229|       |
  230|  3.87k|    if ((retval = readstat_convert(name, sizeof(name), &data[8], 8, ctx->converter)) != READSTAT_OK)
  ------------------
  |  Branch (230:9): [True: 4, False: 3.87k]
  ------------------
  231|      4|        goto cleanup;
  232|       |
  233|  3.87k|    if (pad) {
  ------------------
  |  Branch (233:9): [True: 1.79k, False: 2.08k]
  ------------------
  234|  1.79k|        pad += 16;
  235|  1.79k|    }
  236|       |
  237|  3.87k|    if (((flags & 0x80) && !ctx->u64) || ((flags & 0x20) && ctx->u64)) { // has long name
  ------------------
  |  Branch (237:10): [True: 1.95k, False: 1.91k]
  |  Branch (237:28): [True: 1.78k, False: 170]
  |  Branch (237:43): [True: 800, False: 1.28k]
  |  Branch (237:61): [True: 219, False: 581]
  ------------------
  238|  2.00k|        if (data_size < payload_offset + pad + 32)
  ------------------
  |  Branch (238:13): [True: 277, False: 1.72k]
  ------------------
  239|    277|            goto cleanup;
  240|       |
  241|  1.72k|        retval = readstat_convert(name, sizeof(name), &data[payload_offset+pad], 32, ctx->converter);
  242|  1.72k|        if (retval != READSTAT_OK)
  ------------------
  |  Branch (242:13): [True: 2, False: 1.72k]
  ------------------
  243|      2|            goto cleanup;
  244|  1.72k|        pad += 32;
  245|  1.72k|    }
  246|       |
  247|  3.59k|    if (data_size < payload_offset + pad)
  ------------------
  |  Branch (247:9): [True: 230, False: 3.36k]
  ------------------
  248|    230|        goto cleanup;
  249|       |
  250|  3.36k|    if (label_count_used == 0)
  ------------------
  |  Branch (250:9): [True: 948, False: 2.41k]
  ------------------
  251|    948|        goto cleanup;
  252|       |
  253|  2.41k|    if ((retval = sas7bcat_parse_value_labels(&data[payload_offset+pad], data_size - payload_offset - pad,
  ------------------
  |  Branch (253:9): [True: 582, False: 1.83k]
  ------------------
  254|  2.41k|            label_count_used, label_count_capacity, default_label_pos, string_offset, name, ctx)) != READSTAT_OK)
  255|    582|        goto cleanup;
  256|       |
  257|  5.42k|cleanup:
  258|  5.42k|    return retval;
  259|  2.41k|}
readstat_sas7bcat_read.c:sas7bcat_parse_value_labels:
   47|  2.41k|        const char *name, sas7bcat_ctx_t *ctx) {
   48|  2.41k|    readstat_error_t retval = READSTAT_OK;
   49|  2.41k|    int i;
   50|  2.41k|    const char *lbp1 = value_start;
   51|  2.41k|    uint32_t *value_offset = NULL;
   52|       |    /* Doubles appear to be stored as big-endian, always */
   53|  2.41k|    int bswap_doubles = machine_is_little_endian();
   54|  2.41k|    int is_string = 0;
   55|  2.41k|    char *label = NULL;
   56|  2.41k|    char *string_val = NULL;
   57|       |
   58|  2.41k|    if (name[0] == '$') {
  ------------------
  |  Branch (58:9): [True: 393, False: 2.02k]
  ------------------
   59|    393|        is_string = 1;
   60|  2.02k|    } else if (name[0] == '@') {
  ------------------
  |  Branch (60:16): [True: 202, False: 1.82k]
  ------------------
   61|    202|        goto cleanup; // Skip informats
   62|    202|    }
   63|       |
   64|  2.21k|    value_offset = readstat_calloc(label_count_used, sizeof(uint32_t));
   65|  2.21k|    if (value_offset == NULL) {
  ------------------
  |  Branch (65:9): [True: 164, False: 2.05k]
  ------------------
   66|    164|        retval = READSTAT_ERROR_MALLOC;
   67|    164|        goto cleanup;
   68|    164|    }
   69|       |    /* Mark every slot as unset so that a label index which no value entry
   70|       |     * references is detected in pass 2 instead of silently reading entry 0 */
   71|   554M|    for (i=0; i<label_count_used; i++) {
  ------------------
  |  Branch (71:15): [True: 554M, False: 2.05k]
  ------------------
   72|   554M|        value_offset[i] = UINT32_MAX;
   73|   554M|    }
   74|       |
   75|       |    /* Pass 1 -- find out the offset of the labels */
   76|  1.19M|    for (i=0; i<label_count_capacity; i++) {
  ------------------
  |  Branch (76:15): [True: 1.19M, False: 1.93k]
  ------------------
   77|  1.19M|        if (&lbp1[4] - value_start > value_labels_len || sas_read2(&lbp1[2], ctx->bswap) < 0) {
  ------------------
  |  Branch (77:13): [True: 36, False: 1.19M]
  |  Branch (77:58): [True: 0, False: 1.19M]
  ------------------
   78|     36|            retval = READSTAT_ERROR_PARSE;
   79|     36|            goto cleanup;
   80|     36|        }
   81|  1.19M|        if (i<label_count_used) {
  ------------------
  |  Branch (81:13): [True: 92.0k, False: 1.09M]
  ------------------
   82|  92.0k|            if (&lbp1[10+ctx->pad1+4] - value_start > value_labels_len) {
  ------------------
  |  Branch (82:17): [True: 6, False: 92.0k]
  ------------------
   83|      6|                retval = READSTAT_ERROR_PARSE;
   84|      6|                goto cleanup;
   85|      6|            }
   86|  92.0k|            uint32_t label_pos = sas_read4(&lbp1[10+ctx->pad1], ctx->bswap);
   87|  92.0k|            if (label_pos >= label_count_used || (default_label_pos > 0 && label_pos == default_label_pos - 1)) {
  ------------------
  |  Branch (87:17): [True: 73, False: 91.9k]
  |  Branch (87:51): [True: 7.78k, False: 84.1k]
  |  Branch (87:76): [True: 1, False: 7.78k]
  ------------------
   88|     74|                retval = READSTAT_ERROR_PARSE;
   89|     74|                goto cleanup;
   90|     74|            }
   91|  91.9k|            value_offset[label_pos] = lbp1 - value_start;
   92|  91.9k|        }
   93|  1.19M|        lbp1 += 6 + sas_read2(&lbp1[2], ctx->bswap);
   94|  1.19M|    }
   95|       |
   96|  1.93k|    const char *lbp2 = lbp1;
   97|       |
   98|       |    /* Pass 2 -- parse pairs of values & labels */
   99|  4.91k|    for (i=0; i<label_count_used; i++) {
  ------------------
  |  Branch (99:15): [True: 3.28k, False: 1.63k]
  ------------------
  100|  3.28k|        readstat_value_t value = { 0 };
  101|  3.28k|        int is_default_label = default_label_pos > 0 && i == default_label_pos - 1;
  ------------------
  |  Branch (101:32): [True: 1.52k, False: 1.76k]
  |  Branch (101:57): [True: 355, False: 1.17k]
  ------------------
  102|  3.28k|        if (!is_default_label) {
  ------------------
  |  Branch (102:13): [True: 2.93k, False: 355]
  ------------------
  103|       |            /* A non-default label with no value entry pointing at it would
  104|       |             * otherwise read from offset 0 and emit a duplicate value */
  105|  2.93k|            if (value_offset[i] == UINT32_MAX) {
  ------------------
  |  Branch (105:17): [True: 197, False: 2.73k]
  ------------------
  106|    197|                retval = READSTAT_ERROR_PARSE;
  107|    197|                goto cleanup;
  108|    197|            }
  109|  2.73k|            lbp1 = value_start + value_offset[i];
  110|       |
  111|  2.73k|            if (&lbp1[30] - value_start > value_labels_len) {
  ------------------
  |  Branch (111:17): [True: 6, False: 2.72k]
  ------------------
  112|      6|                retval = READSTAT_ERROR_PARSE;
  113|      6|                goto cleanup;
  114|      6|            }
  115|  2.72k|            value.type = is_string ? READSTAT_TYPE_STRING : READSTAT_TYPE_DOUBLE;
  ------------------
  |  Branch (115:26): [True: 668, False: 2.06k]
  ------------------
  116|  2.72k|            if (is_string) {
  ------------------
  |  Branch (116:17): [True: 668, False: 2.06k]
  ------------------
  117|    668|                size_t value_entry_len = 6 + sas_read2(&lbp1[2], ctx->bswap);
  118|    668|                size_t string_start = 22 + string_offset;
  119|    668|                if (string_start > value_entry_len ||
  ------------------
  |  Branch (119:21): [True: 7, False: 661]
  ------------------
  120|    661|                        &lbp1[value_entry_len] - value_start > value_labels_len) {
  ------------------
  |  Branch (120:25): [True: 31, False: 630]
  ------------------
  121|     38|                    retval = READSTAT_ERROR_PARSE;
  122|     38|                    goto cleanup;
  123|     38|                }
  124|    630|                size_t string_len = value_entry_len - string_start;
  125|    630|                char *new_string_val = realloc(string_val, 4 * string_len + 1);
  126|    630|                if (new_string_val == NULL) {
  ------------------
  |  Branch (126:21): [True: 0, False: 630]
  ------------------
  127|      0|                    retval = READSTAT_ERROR_MALLOC;
  128|      0|                    goto cleanup;
  129|      0|                }
  130|    630|                string_val = new_string_val;
  131|    630|                retval = readstat_convert(string_val, 4 * string_len + 1,
  132|    630|                        &lbp1[string_start], string_len, ctx->converter);
  133|    630|                if (retval != READSTAT_OK)
  ------------------
  |  Branch (133:21): [True: 2, False: 628]
  ------------------
  134|      2|                    goto cleanup;
  135|       |
  136|    628|                value.v.string_value = string_val;
  137|  2.06k|            } else {
  138|  2.06k|                uint64_t val = sas_read8(&lbp1[22], bswap_doubles);
  139|  2.06k|                double dval = NAN;
  140|  2.06k|                if ((val | 0xFF0000000000) == 0xFFFFFFFFFFFF) {
  ------------------
  |  Branch (140:21): [True: 965, False: 1.09k]
  ------------------
  141|    965|                    sas_assign_tag(&value, (val >> 40));
  142|  1.09k|                } else {
  143|  1.09k|                    memcpy(&dval, &val, 8);
  144|  1.09k|                    if (dval > 0.0) {
  ------------------
  |  Branch (144:25): [True: 645, False: 451]
  ------------------
  145|    645|                        val = ~val;
  146|    645|                        memcpy(&dval, &val, 8);
  147|    645|                    } else {
  148|    451|                        dval *= -1.0;
  149|    451|                    }
  150|  1.09k|                }
  151|       |
  152|  2.06k|                value.v.double_value = dval;
  153|  2.06k|            }
  154|  2.72k|        }
  155|  3.04k|        if (&lbp2[10] > value_start + value_labels_len) {
  ------------------
  |  Branch (155:13): [True: 47, False: 2.99k]
  ------------------
  156|     47|            retval = READSTAT_ERROR_PARSE;
  157|     47|            goto cleanup;
  158|     47|        }
  159|  2.99k|        size_t label_len = sas_read2(&lbp2[8], ctx->bswap);
  160|       |        /* Some labels seem to overflow the reported block length, truncate it */
  161|       |        /* (Observed with formats.sasbcat from GSS2021, produced with 9.0401M6X64_SR12R2 */
  162|  2.99k|        if (label_len > value_start + value_labels_len - &lbp2[10]) {
  ------------------
  |  Branch (162:13): [True: 683, False: 2.31k]
  ------------------
  163|    683|            label_len = value_start + value_labels_len - &lbp2[10];
  164|    683|        }
  165|  2.99k|        if (ctx->value_label_handler && !is_default_label) {
  ------------------
  |  Branch (165:13): [True: 2.99k, False: 0]
  |  Branch (165:41): [True: 2.64k, False: 353]
  ------------------
  166|  2.64k|            label = realloc(label, 4 * label_len + 1);
  167|  2.64k|            retval = readstat_convert(label, 4 * label_len + 1,
  168|  2.64k|                    &lbp2[10], label_len, ctx->converter);
  169|  2.64k|            if (retval != READSTAT_OK)
  ------------------
  |  Branch (169:17): [True: 12, False: 2.63k]
  ------------------
  170|     12|                goto cleanup;
  171|       |
  172|  2.63k|            if (ctx->value_label_handler(name, value, label, ctx->user_ctx) != READSTAT_HANDLER_OK) {
  ------------------
  |  |   22|  2.63k|#define READSTAT_HANDLER_OK             0
  ------------------
  |  Branch (172:17): [True: 0, False: 2.63k]
  ------------------
  173|      0|                retval = READSTAT_ERROR_USER_ABORT;
  174|      0|                goto cleanup;
  175|      0|            }
  176|  2.63k|        }
  177|       |
  178|  2.98k|        lbp2 += 8 + 2 + label_len + 1;
  179|  2.98k|    }
  180|       |
  181|  2.41k|cleanup:
  182|  2.41k|    free(label);
  183|  2.41k|    free(string_val);
  184|  2.41k|    free(value_offset);
  185|  2.41k|    return retval;
  186|  1.93k|}
readstat_sas7bcat_read.c:sas7bcat_ctx_free:
   36|  1.94k|static void sas7bcat_ctx_free(sas7bcat_ctx_t *ctx) {
   37|  1.94k|    if (ctx->converter)
  ------------------
  |  Branch (37:9): [True: 954, False: 990]
  ------------------
   38|    954|        iconv_close(ctx->converter);
   39|  1.94k|    if (ctx->block_pointers)
  ------------------
  |  Branch (39:9): [True: 1.94k, False: 0]
  ------------------
   40|  1.94k|        free(ctx->block_pointers);
   41|       |
   42|  1.94k|    free(ctx);
   43|  1.94k|}

rt_open_handler:
    8|  1.94k|int rt_open_handler(const char *path, void *io_ctx) {
    9|  1.94k|    return 0;
   10|  1.94k|}
rt_close_handler:
   12|  1.94k|int rt_close_handler(void *io_ctx) {
   13|  1.94k|    return 0;
   14|  1.94k|}
rt_seek_handler:
   17|  68.1k|        readstat_io_flags_t whence, void *io_ctx) {
   18|  68.1k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   19|  68.1k|    readstat_off_t newpos = -1;
   20|  68.1k|    if (whence == READSTAT_SEEK_SET) {
  ------------------
  |  Branch (20:9): [True: 64.7k, False: 3.36k]
  ------------------
   21|  64.7k|        newpos = offset;
   22|  64.7k|    } else if (whence == READSTAT_SEEK_CUR) {
  ------------------
  |  Branch (22:16): [True: 3.36k, False: 0]
  ------------------
   23|  3.36k|        newpos = buffer_ctx->pos + offset;
   24|  3.36k|    } 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|  68.1k|    if (newpos < 0)
  ------------------
  |  Branch (28:9): [True: 0, False: 68.1k]
  ------------------
   29|      0|        return -1;
   30|       |
   31|  68.1k|    if (newpos > buffer_ctx->buffer->used)
  ------------------
  |  Branch (31:9): [True: 229, False: 67.9k]
  ------------------
   32|    229|        return -1;
   33|       |
   34|  67.9k|    buffer_ctx->pos = newpos;
   35|  67.9k|    return newpos;
   36|  68.1k|}
rt_read_handler:
   38|  95.4k|ssize_t rt_read_handler(void *buf, size_t nbytes, void *io_ctx) {
   39|  95.4k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   40|  95.4k|    ssize_t bytes_copied = 0;
   41|  95.4k|    ssize_t bytes_left = buffer_ctx->buffer->used - buffer_ctx->pos;
   42|  95.4k|    if (nbytes <= bytes_left) {
  ------------------
  |  Branch (42:9): [True: 95.1k, False: 297]
  ------------------
   43|  95.1k|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, nbytes);
   44|  95.1k|        bytes_copied = nbytes;
   45|  95.1k|    } else if (bytes_left > 0) {
  ------------------
  |  Branch (45:16): [True: 148, False: 149]
  ------------------
   46|    148|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, bytes_left);
   47|    148|        bytes_copied = bytes_left;
   48|    148|    }
   49|  95.4k|    buffer_ctx->pos += bytes_copied;
   50|  95.4k|    return bytes_copied;
   51|  95.4k|}

