fuzzer_parser_init:
   33|  3.67k|readstat_parser_t *fuzzer_parser_init(const uint8_t *Data, size_t Size) {
   34|  3.67k|    readstat_parser_t *parser = readstat_parser_init();
   35|  3.67k|    readstat_set_open_handler(parser, rt_open_handler);
   36|  3.67k|    readstat_set_close_handler(parser, rt_close_handler);
   37|  3.67k|    readstat_set_seek_handler(parser, rt_seek_handler);
   38|  3.67k|    readstat_set_read_handler(parser, rt_read_handler);
   39|  3.67k|    readstat_set_update_handler(parser, rt_update_handler);
   40|       |
   41|  3.67k|    readstat_set_metadata_handler(parser, &handle_metadata);
   42|  3.67k|    readstat_set_note_handler(parser, &handle_note);
   43|  3.67k|    readstat_set_variable_handler(parser, &handle_variable);
   44|  3.67k|    readstat_set_fweight_handler(parser, &handle_fweight);
   45|  3.67k|    readstat_set_value_handler(parser, &handle_value);
   46|  3.67k|    readstat_set_value_label_handler(parser, &handle_value_label);
   47|       |
   48|  3.67k|    return parser;
   49|  3.67k|}
fuzz_format.c:handle_metadata:
    8|  1.13k|static int handle_metadata(readstat_metadata_t *metadata, void *ctx) {
    9|  1.13k|    return READSTAT_HANDLER_OK;
   10|  1.13k|}
fuzz_format.c:handle_variable:
   21|  3.05M|                           const char *val_labels, void *ctx) {
   22|  3.05M|    return READSTAT_HANDLER_OK;
   23|  3.05M|}
fuzz_format.c:handle_value:
   25|  19.5M|static int handle_value(int obs_index, readstat_variable_t *variable, readstat_value_t value, void *ctx) {
   26|  19.5M|    return READSTAT_HANDLER_OK;
   27|  19.5M|}

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

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

readstat_convert:
    7|  19.3M|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|  19.4M|    while (src_len && (src[src_len-1] == ' ' || src[src_len-1] == '\0')) {
  ------------------
  |  Branch (10:12): [True: 68.4k, False: 19.3M]
  |  Branch (10:24): [True: 5.11k, False: 63.3k]
  |  Branch (10:49): [True: 42.9k, False: 20.4k]
  ------------------
   11|  48.0k|        src_len--;
   12|  48.0k|    }
   13|  19.3M|    if (dst_len == 0) {
  ------------------
  |  Branch (13:9): [True: 0, False: 19.3M]
  ------------------
   14|      0|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   15|  19.3M|    } else if (converter) {
  ------------------
  |  Branch (15:16): [True: 587k, False: 18.8M]
  ------------------
   16|   587k|        size_t dst_left = dst_len - 1;
   17|   587k|        char *dst_end = dst;
   18|   587k|        size_t status = iconv(converter, (readstat_iconv_inbuf_t)&src, &src_len, &dst_end, &dst_left);
   19|   587k|        if (status == (size_t)-1) {
  ------------------
  |  Branch (19:13): [True: 1.77k, False: 585k]
  ------------------
   20|  1.77k|            if (errno == E2BIG) {
  ------------------
  |  Branch (20:17): [True: 4, False: 1.77k]
  ------------------
   21|      4|                return READSTAT_ERROR_CONVERT_LONG_STRING;
   22|  1.77k|            } else if (errno == EILSEQ) {
  ------------------
  |  Branch (22:24): [True: 26, False: 1.74k]
  ------------------
   23|     26|                return READSTAT_ERROR_CONVERT_BAD_STRING;
   24|  1.74k|            } else if (errno != EINVAL) { /* EINVAL indicates improper truncation; accept it */
  ------------------
  |  Branch (24:24): [True: 0, False: 1.74k]
  ------------------
   25|      0|                return READSTAT_ERROR_CONVERT;
   26|      0|            }
   27|  1.77k|        }
   28|   587k|        dst[dst_len - dst_left - 1] = '\0';
   29|  18.8M|    } else if (src_len + 1 > dst_len) {
  ------------------
  |  Branch (29:16): [True: 26, False: 18.8M]
  ------------------
   30|     26|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   31|  18.8M|    } else {
   32|  18.8M|        memcpy(dst, src, src_len);
   33|  18.8M|        dst[src_len] = '\0';
   34|  18.8M|    }
   35|  19.3M|    return READSTAT_OK;
   36|  19.3M|}

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

readstat_malloc:
   10|  5.77k|void *readstat_malloc(size_t len) {
   11|  5.77k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|  11.5k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (11:9): [True: 0, False: 5.77k]
  |  Branch (11:34): [True: 0, False: 5.77k]
  ------------------
   12|      0|        return NULL;
   13|      0|    }
   14|  5.77k|    return malloc(len);
   15|  5.77k|}
readstat_calloc:
   17|  3.05M|void *readstat_calloc(size_t count, size_t size) {
   18|  3.05M|    if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  6.10M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  6.10M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  3.05M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (18:9): [True: 0, False: 3.05M]
  |  Branch (18:36): [True: 0, False: 3.05M]
  |  Branch (18:62): [True: 0, False: 3.05M]
  ------------------
   19|      0|        return NULL;
   20|      0|    }
   21|  3.05M|    if (count == 0 || size == 0) {
  ------------------
  |  Branch (21:9): [True: 0, False: 3.05M]
  |  Branch (21:23): [True: 0, False: 3.05M]
  ------------------
   22|      0|        return NULL;
   23|      0|    }
   24|  3.05M|    return calloc(count, size);
   25|  3.05M|}
readstat_realloc:
   27|  53.7k|void *readstat_realloc(void *ptr, size_t len) {
   28|  53.7k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|   107k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (28:9): [True: 94, False: 53.6k]
  |  Branch (28:34): [True: 1, False: 53.6k]
  ------------------
   29|     95|        if (ptr)
  ------------------
  |  Branch (29:13): [True: 51, False: 44]
  ------------------
   30|     51|            free(ptr);
   31|     95|        return NULL;
   32|     95|    }
   33|  53.6k|    return realloc(ptr, len);
   34|  53.7k|}

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

readstat_variable_set_informat:
  441|    652|void readstat_variable_set_informat(readstat_variable_t *variable, const char *informat) {
  442|    652|    if (informat) {
  ------------------
  |  Branch (442:9): [True: 652, False: 0]
  ------------------
  443|    652|        snprintf(variable->informat, sizeof(variable->informat), "%s", informat);
  444|    652|    } else {
  445|      0|        memset(variable->informat, '\0', sizeof(variable->informat));
  446|      0|    }
  447|    652|}

sas_read8:
  139|  42.5k|uint64_t sas_read8(const char *data, int bswap) {
  140|  42.5k|    uint64_t tmp;
  141|  42.5k|    memcpy(&tmp, data, 8);
  142|  42.5k|    return bswap ? byteswap8(tmp) : tmp;
  ------------------
  |  Branch (142:12): [True: 37.5k, False: 4.94k]
  ------------------
  143|  42.5k|}
sas_read4:
  145|   245k|uint32_t sas_read4(const char *data, int bswap) {
  146|   245k|    uint32_t tmp;
  147|   245k|    memcpy(&tmp, data, 4);
  148|   245k|    return bswap ? byteswap4(tmp) : tmp;
  ------------------
  |  Branch (148:12): [True: 176k, False: 68.3k]
  ------------------
  149|   245k|}
sas_read2:
  151|   157k|uint16_t sas_read2(const char *data, int bswap) {
  152|   157k|    uint16_t tmp;
  153|   157k|    memcpy(&tmp, data, 2);
  154|   157k|    return bswap ? byteswap2(tmp) : tmp;
  ------------------
  |  Branch (154:12): [True: 67.6k, False: 90.2k]
  ------------------
  155|   157k|}
sas_subheader_remainder:
  157|  5.72k|size_t sas_subheader_remainder(size_t len, size_t signature_len) {
  158|  5.72k|    return len - (4+2*signature_len);
  159|  5.72k|}
sas_read_header:
  162|  3.67k|        readstat_error_handler error_handler, void *user_ctx) {
  163|  3.67k|    sas_header_start_t  header_start;
  164|  3.67k|    sas_header_end_t    header_end;
  165|  3.67k|    int retval = READSTAT_OK;
  166|  3.67k|    char error_buf[1024];
  167|  3.67k|    time_t epoch = sas_epoch();
  168|       |
  169|  3.67k|    if (io->read(&header_start, sizeof(sas_header_start_t), io->io_ctx) < sizeof(sas_header_start_t)) {
  ------------------
  |  Branch (169:9): [True: 20, False: 3.65k]
  ------------------
  170|     20|        retval = READSTAT_ERROR_READ;
  171|     20|        goto cleanup;
  172|     20|    }
  173|  3.65k|    if (memcmp(header_start.magic, sas7bdat_magic_number, sizeof(sas7bdat_magic_number)) != 0 &&
  ------------------
  |  Branch (173:9): [True: 1.55k, False: 2.10k]
  ------------------
  174|  1.55k|            memcmp(header_start.magic, sas7bcat_magic_number, sizeof(sas7bcat_magic_number)) != 0) {
  ------------------
  |  Branch (174:13): [True: 130, False: 1.42k]
  ------------------
  175|    130|        retval = READSTAT_ERROR_PARSE;
  176|    130|        goto cleanup;
  177|    130|    }
  178|  3.52k|    if (header_start.a1 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   87|  3.52k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (178:9): [True: 6, False: 3.51k]
  ------------------
  179|      6|        hinfo->pad1 = 4;
  180|      6|    }
  181|  3.52k|    if (header_start.a2 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   87|  3.52k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (181:9): [True: 1.71k, False: 1.80k]
  ------------------
  182|  1.71k|        hinfo->u64 = 1;
  183|  1.71k|    }
  184|  3.52k|    int bswap = 0;
  185|  3.52k|    if (header_start.endian == SAS_ENDIAN_BIG) {
  ------------------
  |  |   80|  3.52k|#define SAS_ENDIAN_BIG       0x00
  ------------------
  |  Branch (185:9): [True: 2.74k, False: 776]
  ------------------
  186|  2.74k|        bswap = machine_is_little_endian();
  187|  2.74k|        hinfo->little_endian = 0;
  188|  2.74k|    } else if (header_start.endian == SAS_ENDIAN_LITTLE) {
  ------------------
  |  |   81|    776|#define SAS_ENDIAN_LITTLE    0x01
  ------------------
  |  Branch (188:16): [True: 764, False: 12]
  ------------------
  189|    764|        bswap = !machine_is_little_endian();
  190|    764|        hinfo->little_endian = 1;
  191|    764|    } else {
  192|     12|        retval = READSTAT_ERROR_PARSE;
  193|     12|        goto cleanup;
  194|     12|    }
  195|  3.51k|    int i;
  196|  22.6k|    for (i=0; i<sizeof(_charset_table)/sizeof(_charset_table[0]); i++) {
  ------------------
  |  Branch (196:15): [True: 22.6k, False: 5]
  ------------------
  197|  22.6k|        if (header_start.encoding == _charset_table[i].code) {
  ------------------
  |  Branch (197:13): [True: 3.50k, False: 19.1k]
  ------------------
  198|  3.50k|            hinfo->encoding = _charset_table[i].name;
  199|  3.50k|            break;
  200|  3.50k|        }
  201|  22.6k|    }
  202|  3.51k|    if (hinfo->encoding == NULL) {
  ------------------
  |  Branch (202:9): [True: 5, False: 3.50k]
  ------------------
  203|      5|        if (error_handler) {
  ------------------
  |  Branch (203:13): [True: 0, False: 5]
  ------------------
  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|      5|        retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
  208|      5|        goto cleanup;
  209|      5|    }
  210|  3.50k|    memcpy(hinfo->table_name, header_start.table_name, sizeof(header_start.table_name));
  211|  3.50k|    if (io->seek(hinfo->pad1, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (211:9): [True: 3, False: 3.50k]
  ------------------
  212|      3|        retval = READSTAT_ERROR_SEEK;
  213|      3|        goto cleanup;
  214|      3|    }
  215|       |
  216|  3.50k|    double creation_time, modification_time, creation_time_diff, modification_time_diff;
  217|       |
  218|  3.50k|    if (io->read(&creation_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (218:9): [True: 12, False: 3.49k]
  ------------------
  219|     12|        retval = READSTAT_ERROR_READ;
  220|     12|        goto cleanup;
  221|     12|    }
  222|  3.49k|    if (bswap)
  ------------------
  |  Branch (222:9): [True: 2.73k, False: 759]
  ------------------
  223|  2.73k|        creation_time = byteswap_double(creation_time);
  224|       |
  225|  3.49k|    if (io->read(&modification_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (225:9): [True: 6, False: 3.48k]
  ------------------
  226|      6|        retval = READSTAT_ERROR_READ;
  227|      6|        goto cleanup;
  228|      6|    }
  229|  3.48k|    if (bswap)
  ------------------
  |  Branch (229:9): [True: 2.72k, False: 758]
  ------------------
  230|  2.72k|        modification_time = byteswap_double(modification_time);
  231|       |
  232|  3.48k|    if (io->read(&creation_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (232:9): [True: 6, False: 3.48k]
  ------------------
  233|      6|        retval = READSTAT_ERROR_READ;
  234|      6|        goto cleanup;
  235|      6|    }
  236|  3.48k|    if (bswap)
  ------------------
  |  Branch (236:9): [True: 2.72k, False: 755]
  ------------------
  237|  2.72k|        creation_time_diff = byteswap_double(creation_time_diff);
  238|       |    
  239|  3.48k|    if (io->read(&modification_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (239:9): [True: 6, False: 3.47k]
  ------------------
  240|      6|        retval = READSTAT_ERROR_READ;
  241|      6|        goto cleanup;
  242|      6|    }
  243|  3.47k|    if (bswap)
  ------------------
  |  Branch (243:9): [True: 2.72k, False: 752]
  ------------------
  244|  2.72k|        modification_time_diff = byteswap_double(modification_time_diff);
  245|       |    
  246|  3.47k|    hinfo->creation_time = sas_convert_time(creation_time, creation_time_diff, epoch);
  247|  3.47k|    hinfo->modification_time = sas_convert_time(modification_time, modification_time_diff, epoch);
  248|       |
  249|  3.47k|    uint32_t header_size, page_size;
  250|       |
  251|  3.47k|    if (io->read(&header_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (251:9): [True: 9, False: 3.46k]
  ------------------
  252|      9|        retval = READSTAT_ERROR_READ;
  253|      9|        goto cleanup;
  254|      9|    }
  255|  3.46k|    if (io->read(&page_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (255:9): [True: 3, False: 3.46k]
  ------------------
  256|      3|        retval = READSTAT_ERROR_READ;
  257|      3|        goto cleanup;
  258|      3|    }
  259|       |
  260|  3.46k|    hinfo->header_size = bswap ? byteswap4(header_size) : header_size;
  ------------------
  |  Branch (260:26): [True: 2.71k, False: 749]
  ------------------
  261|  3.46k|    hinfo->page_size = bswap ? byteswap4(page_size) : page_size;
  ------------------
  |  Branch (261:24): [True: 2.71k, False: 749]
  ------------------
  262|       |
  263|  3.46k|    if (hinfo->header_size < 1024 || hinfo->page_size < 1024) {
  ------------------
  |  Branch (263:9): [True: 11, False: 3.45k]
  |  Branch (263:38): [True: 12, False: 3.44k]
  ------------------
  264|     23|        retval = READSTAT_ERROR_PARSE;
  265|     23|        goto cleanup;
  266|     23|    }
  267|  3.44k|    if (hinfo->header_size > (1<<24) || hinfo->page_size > (1<<24)) {
  ------------------
  |  Branch (267:9): [True: 20, False: 3.42k]
  |  Branch (267:41): [True: 28, False: 3.39k]
  ------------------
  268|     48|        retval = READSTAT_ERROR_PARSE;
  269|     48|        goto cleanup;
  270|     48|    }
  271|       |
  272|  3.39k|    if (hinfo->u64) {
  ------------------
  |  Branch (272:9): [True: 1.67k, False: 1.71k]
  ------------------
  273|  1.67k|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_64BIT;
  ------------------
  |  |  122|  1.67k|#define SAS_PAGE_HEADER_SIZE_64BIT  40
  ------------------
  274|  1.67k|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_64BIT;
  ------------------
  |  |  119|  1.67k|#define SAS_SUBHEADER_POINTER_SIZE_64BIT    24
  ------------------
  275|  1.71k|    } else {
  276|  1.71k|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_32BIT;
  ------------------
  |  |  121|  1.71k|#define SAS_PAGE_HEADER_SIZE_32BIT  24
  ------------------
  277|  1.71k|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_32BIT;
  ------------------
  |  |  118|  1.71k|#define SAS_SUBHEADER_POINTER_SIZE_32BIT    12
  ------------------
  278|  1.71k|    }
  279|       |
  280|  3.39k|    if (hinfo->u64) {
  ------------------
  |  Branch (280:9): [True: 1.67k, False: 1.71k]
  ------------------
  281|  1.67k|        uint64_t page_count;
  282|  1.67k|        if (io->read(&page_count, sizeof(uint64_t), io->io_ctx) < sizeof(uint64_t)) {
  ------------------
  |  Branch (282:13): [True: 18, False: 1.66k]
  ------------------
  283|     18|            retval = READSTAT_ERROR_READ;
  284|     18|            goto cleanup;
  285|     18|        }
  286|  1.66k|        hinfo->page_count = bswap ? byteswap8(page_count) : page_count;
  ------------------
  |  Branch (286:29): [True: 1.57k, False: 90]
  ------------------
  287|  1.71k|    } else {
  288|  1.71k|        uint32_t page_count;
  289|  1.71k|        if (io->read(&page_count, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (289:13): [True: 16, False: 1.69k]
  ------------------
  290|     16|            retval = READSTAT_ERROR_READ;
  291|     16|            goto cleanup;
  292|     16|        }
  293|  1.69k|        hinfo->page_count = bswap ? byteswap4(page_count) : page_count;
  ------------------
  |  Branch (293:29): [True: 1.05k, False: 644]
  ------------------
  294|  1.69k|    }
  295|  3.35k|    if (hinfo->page_count > (1<<24)) {
  ------------------
  |  Branch (295:9): [True: 13, False: 3.34k]
  ------------------
  296|     13|        retval = READSTAT_ERROR_PARSE;
  297|     13|        goto cleanup;
  298|     13|    }
  299|       |    
  300|  3.34k|    if (io->seek(8, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (300:9): [True: 77, False: 3.26k]
  ------------------
  301|     77|        retval = READSTAT_ERROR_SEEK;
  302|     77|        if (error_handler) {
  ------------------
  |  Branch (302:13): [True: 0, False: 77]
  ------------------
  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|     77|        goto cleanup;
  307|     77|    }
  308|  3.26k|    if (io->read(&header_end, sizeof(sas_header_end_t), io->io_ctx) < sizeof(sas_header_end_t)) {
  ------------------
  |  Branch (308:9): [True: 17, False: 3.25k]
  ------------------
  309|     17|        retval = READSTAT_ERROR_READ;
  310|     17|        goto cleanup;
  311|     17|    }
  312|  3.25k|    char major, revision_tag;
  313|  3.25k|    int minor, revision;
  314|  3.25k|    if (sscanf(header_end.release, "%c.%04d%c%1d", &major, &minor, &revision_tag, &revision) != 4) {
  ------------------
  |  Branch (314:9): [True: 3, False: 3.24k]
  ------------------
  315|      3|        retval = READSTAT_ERROR_PARSE;
  316|      3|        goto cleanup;
  317|      3|    }
  318|       |
  319|  3.24k|    if (major >= '1' && major <= '9') {
  ------------------
  |  Branch (319:9): [True: 3.23k, False: 12]
  |  Branch (319:25): [True: 3.22k, False: 9]
  ------------------
  320|  3.22k|        hinfo->major_version = major - '0';
  321|  3.22k|    } else if (major == 'V') {
  ------------------
  |  Branch (321:16): [True: 3, False: 18]
  ------------------
  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|      3|        hinfo->major_version = 9;
  325|     18|    } else {
  326|     18|        retval = READSTAT_ERROR_PARSE;
  327|     18|        goto cleanup;
  328|     18|    }
  329|       |    // revision_tag is usually M, but J has been observed in the wild (not created with SAS?)
  330|  3.23k|    if (revision_tag != 'M' && revision_tag != 'J') {
  ------------------
  |  Branch (330:9): [True: 25, False: 3.20k]
  |  Branch (330:32): [True: 14, False: 11]
  ------------------
  331|     14|        retval = READSTAT_ERROR_PARSE;
  332|     14|        goto cleanup;
  333|     14|    }
  334|  3.21k|    hinfo->minor_version = minor;
  335|  3.21k|    hinfo->revision = revision;
  336|       |
  337|  3.21k|    if ((major == '8' || major == '9') && minor == 0 && revision == 0) {
  ------------------
  |  Branch (337:10): [True: 274, False: 2.94k]
  |  Branch (337:26): [True: 149, False: 2.79k]
  |  Branch (337:43): [True: 92, False: 331]
  |  Branch (337:57): [True: 58, False: 34]
  ------------------
  338|       |        /* A bit of a hack, but most SAS installations are running a minor update */
  339|     58|        hinfo->vendor = READSTAT_VENDOR_STAT_TRANSFER;
  340|  3.15k|    } else {
  341|  3.15k|        hinfo->vendor = READSTAT_VENDOR_SAS;
  342|  3.15k|    }
  343|  3.21k|    if (io->seek(hinfo->header_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (343:9): [True: 54, False: 3.16k]
  ------------------
  344|     54|        retval = READSTAT_ERROR_SEEK;
  345|     54|        if (error_handler) {
  ------------------
  |  Branch (345:13): [True: 0, False: 54]
  ------------------
  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|     54|        goto cleanup;
  351|     54|    }
  352|       |
  353|  3.67k|cleanup:
  354|  3.67k|    return retval;
  355|  3.21k|}
sas_validate_tag:
  507|  8.56k|readstat_error_t sas_validate_tag(char tag) {
  508|  8.56k|    if (tag == '_' || (tag >= 'A' && tag <= 'Z'))
  ------------------
  |  Branch (508:9): [True: 4.35k, False: 4.20k]
  |  Branch (508:24): [True: 1.53k, False: 2.67k]
  |  Branch (508:38): [True: 1.19k, False: 346]
  ------------------
  509|  5.54k|        return READSTAT_OK;
  510|       |
  511|  3.01k|    return READSTAT_ERROR_TAGGED_VALUE_IS_OUT_OF_RANGE;
  512|  8.56k|}
sas_assign_tag:
  514|  8.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|  8.56k|    if (tag == 0) {
  ------------------
  |  Branch (521:9): [True: 4.35k, False: 4.20k]
  ------------------
  522|  4.35k|        tag = '_';
  523|  4.35k|    } else if (tag >= 2 && tag < 28) {
  ------------------
  |  Branch (523:16): [True: 3.68k, False: 528]
  |  Branch (523:28): [True: 1.12k, False: 2.56k]
  ------------------
  524|  1.12k|        tag = 'A' + (tag - 2);
  525|  1.12k|    }
  526|  8.56k|    if (sas_validate_tag(tag) == READSTAT_OK) {
  ------------------
  |  Branch (526:9): [True: 5.54k, False: 3.01k]
  ------------------
  527|  5.54k|        value->tag = tag;
  528|  5.54k|        value->is_tagged_missing = 1;
  529|  5.54k|    } else {
  530|  3.01k|        value->tag = 0;
  531|  3.01k|        value->is_system_missing = 1;
  532|  3.01k|    }
  533|  8.56k|}
readstat_sas.c:sas_epoch:
  123|  3.67k|static time_t sas_epoch(void) {
  124|  3.67k|    return - 3653 * 86400; // seconds between 01-01-1960 and 01-01-1970
  125|  3.67k|}
readstat_sas.c:sas_convert_time:
  127|  6.95k|static time_t sas_convert_time(double time, double time_diff, time_t epoch) {
  128|  6.95k|    time -= time_diff;
  129|  6.95k|    time += epoch;
  130|  6.95k|    if (isnan(time))
  ------------------
  |  Branch (130:9): [True: 733, False: 6.21k]
  ------------------
  131|    733|        return 0;
  132|  6.21k|    if (time > (double)LONG_MAX)
  ------------------
  |  Branch (132:9): [True: 1.69k, False: 4.52k]
  ------------------
  133|  1.69k|        return LONG_MAX;
  134|  4.52k|    if (time < (double)LONG_MIN)
  ------------------
  |  Branch (134:9): [True: 1.09k, False: 3.42k]
  ------------------
  135|  1.09k|        return LONG_MIN;
  136|  3.42k|    return time;
  137|  4.52k|}

readstat_parse_sas7bdat:
 1431|  3.67k|readstat_error_t readstat_parse_sas7bdat(readstat_parser_t *parser, const char *path, void *user_ctx) {
 1432|  3.67k|    int64_t last_examined_page_pass1 = 0;
 1433|  3.67k|    readstat_error_t retval = READSTAT_OK;
 1434|  3.67k|    readstat_io_t *io = parser->io;
 1435|       |
 1436|  3.67k|    sas7bdat_ctx_t  *ctx = calloc(1, sizeof(sas7bdat_ctx_t));
 1437|  3.67k|    sas_header_info_t  *hinfo = calloc(1, sizeof(sas_header_info_t));
 1438|       |
 1439|  3.67k|    if (ctx == NULL || hinfo == NULL) {
  ------------------
  |  Branch (1439:9): [True: 0, False: 3.67k]
  |  Branch (1439:24): [True: 0, False: 3.67k]
  ------------------
 1440|      0|        retval = READSTAT_ERROR_MALLOC;
 1441|      0|        goto cleanup;
 1442|      0|    }
 1443|       |
 1444|  3.67k|    ctx->handle = parser->handlers;
 1445|  3.67k|    ctx->input_encoding = parser->input_encoding;
 1446|  3.67k|    ctx->output_encoding = parser->output_encoding;
 1447|  3.67k|    ctx->user_ctx = user_ctx;
 1448|  3.67k|    ctx->io = parser->io;
 1449|  3.67k|    ctx->row_limit = parser->row_limit;
 1450|  3.67k|    ctx->moved_page_index = -1;
 1451|  3.67k|    if (parser->row_offset > 0)
  ------------------
  |  Branch (1451:9): [True: 0, False: 3.67k]
  ------------------
 1452|      0|        ctx->row_offset = parser->row_offset;
 1453|       |
 1454|  3.67k|    if (io->open(path, io->io_ctx) == -1) {
  ------------------
  |  Branch (1454:9): [True: 0, False: 3.67k]
  ------------------
 1455|      0|        retval = READSTAT_ERROR_OPEN;
 1456|      0|        goto cleanup;
 1457|      0|    }
 1458|       |
 1459|  3.67k|    if ((ctx->file_size = io->seek(0, READSTAT_SEEK_END, io->io_ctx)) == -1) {
  ------------------
  |  Branch (1459:9): [True: 0, False: 3.67k]
  ------------------
 1460|      0|        retval = READSTAT_ERROR_SEEK;
 1461|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1461:13): [True: 0, False: 0]
  ------------------
 1462|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to end of file");
 1463|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1464|      0|        }
 1465|      0|        goto cleanup;
 1466|      0|    }
 1467|       |
 1468|  3.67k|    if (io->seek(0, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1468:9): [True: 0, False: 3.67k]
  ------------------
 1469|      0|        retval = READSTAT_ERROR_SEEK;
 1470|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1470:13): [True: 0, False: 0]
  ------------------
 1471|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to beginning of file");
 1472|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1473|      0|        }
 1474|      0|        goto cleanup;
 1475|      0|    }
 1476|       |
 1477|  3.67k|    if ((retval = sas_read_header(io, hinfo, ctx->handle.error, user_ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1477:9): [True: 513, False: 3.16k]
  ------------------
 1478|    513|        goto cleanup;
 1479|    513|    }
 1480|       |
 1481|  3.16k|    ctx->u64 = hinfo->u64;
 1482|  3.16k|    ctx->little_endian = hinfo->little_endian;
 1483|  3.16k|    ctx->vendor = hinfo->vendor;
 1484|  3.16k|    ctx->bswap = machine_is_little_endian() ^ hinfo->little_endian;
 1485|  3.16k|    ctx->header_size = hinfo->header_size;
 1486|  3.16k|    ctx->page_count = hinfo->page_count;
 1487|  3.16k|    ctx->page_size = hinfo->page_size;
 1488|  3.16k|    ctx->page_header_size = hinfo->page_header_size;
 1489|  3.16k|    ctx->subheader_pointer_size = hinfo->subheader_pointer_size;
 1490|  3.16k|    ctx->subheader_signature_size = ctx->u64 ? 8 : 4;
  ------------------
  |  Branch (1490:37): [True: 1.59k, False: 1.56k]
  ------------------
 1491|  3.16k|    ctx->ctime = hinfo->creation_time;
 1492|  3.16k|    ctx->mtime = hinfo->modification_time;
 1493|  3.16k|    ctx->version = hinfo->major_version;
 1494|  3.16k|    if (ctx->input_encoding == NULL) {
  ------------------
  |  Branch (1494:9): [True: 3.16k, False: 0]
  ------------------
 1495|  3.16k|        ctx->input_encoding = hinfo->encoding;
 1496|  3.16k|    }
 1497|  3.16k|    if ((ctx->page = readstat_malloc(ctx->page_size)) == NULL) {
  ------------------
  |  Branch (1497:9): [True: 0, False: 3.16k]
  ------------------
 1498|      0|        retval = READSTAT_ERROR_MALLOC;
 1499|      0|        goto cleanup;
 1500|      0|    }
 1501|       |
 1502|  3.16k|    if (ctx->input_encoding && ctx->output_encoding && strcmp(ctx->input_encoding, ctx->output_encoding) != 0) {
  ------------------
  |  Branch (1502:9): [True: 3.16k, False: 0]
  |  Branch (1502:32): [True: 3.16k, False: 0]
  |  Branch (1502:56): [True: 2.10k, False: 1.05k]
  ------------------
 1503|  2.10k|        iconv_t converter = iconv_open(ctx->output_encoding, ctx->input_encoding);
 1504|  2.10k|        if (converter == (iconv_t)-1) {
  ------------------
  |  Branch (1504:13): [True: 2, False: 2.10k]
  ------------------
 1505|      2|            retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
 1506|      2|            goto cleanup;
 1507|      2|        }
 1508|  2.10k|        ctx->converter = converter;
 1509|  2.10k|    }
 1510|       |
 1511|  3.16k|    if ((retval = readstat_convert(ctx->table_name, sizeof(ctx->table_name),
  ------------------
  |  Branch (1511:9): [True: 2, False: 3.15k]
  ------------------
 1512|  3.16k|                hinfo->table_name, sizeof(hinfo->table_name), ctx->converter)) != READSTAT_OK) {
 1513|      2|        goto cleanup;
 1514|      2|    }
 1515|       |
 1516|  3.15k|    if ((retval = sas7bdat_parse_meta_pages_pass1(ctx, &last_examined_page_pass1)) != READSTAT_OK) {
  ------------------
  |  Branch (1516:9): [True: 239, False: 2.91k]
  ------------------
 1517|    239|        goto cleanup;
 1518|    239|    }
 1519|       |
 1520|  2.91k|    if ((retval = sas7bdat_parse_amd_pages_pass1(last_examined_page_pass1, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1520:9): [True: 922, False: 1.99k]
  ------------------
 1521|    922|        goto cleanup;
 1522|    922|    }
 1523|       |
 1524|  1.99k|    if (io->seek(ctx->header_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1524:9): [True: 0, False: 1.99k]
  ------------------
 1525|      0|        retval = READSTAT_ERROR_SEEK;
 1526|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1526:13): [True: 0, False: 0]
  ------------------
 1527|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64, 
 1528|      0|                    ctx->header_size);
 1529|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1530|      0|        }
 1531|      0|        goto cleanup;
 1532|      0|    }
 1533|       |
 1534|  1.99k|    if ((retval = sas7bdat_parse_all_pages_pass2(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1534:9): [True: 1.66k, False: 333]
  ------------------
 1535|  1.66k|        goto cleanup;
 1536|  1.66k|    }
 1537|       |    
 1538|    333|    if ((retval = sas7bdat_submit_columns_if_needed(ctx, 0)) != READSTAT_OK) {
  ------------------
  |  Branch (1538:9): [True: 76, False: 257]
  ------------------
 1539|     76|        goto cleanup;
 1540|     76|    }
 1541|       |
 1542|    257|    if (ctx->handle.value && ctx->parsed_row_count != ctx->row_limit) {
  ------------------
  |  Branch (1542:9): [True: 257, False: 0]
  |  Branch (1542:30): [True: 141, False: 116]
  ------------------
 1543|    141|        retval = READSTAT_ERROR_ROW_COUNT_MISMATCH;
 1544|    141|        if (ctx->handle.error) {
  ------------------
  |  Branch (1544:13): [True: 0, False: 141]
  ------------------
 1545|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Expected %d rows in file, found %d",
 1546|      0|                    ctx->row_limit, ctx->parsed_row_count);
 1547|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1548|      0|        }
 1549|    141|        goto cleanup;
 1550|    141|    }
 1551|       |
 1552|    116|    if ((retval = sas7bdat_update_progress(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1552:9): [True: 0, False: 116]
  ------------------
 1553|      0|        goto cleanup;
 1554|      0|    }
 1555|       |
 1556|  3.67k|cleanup:
 1557|  3.67k|    io->close(io->io_ctx);
 1558|       |
 1559|  3.67k|    if (retval == READSTAT_ERROR_OPEN ||
  ------------------
  |  Branch (1559:9): [True: 0, False: 3.67k]
  ------------------
 1560|  3.67k|            retval == READSTAT_ERROR_READ ||
  ------------------
  |  Branch (1560:13): [True: 419, False: 3.25k]
  ------------------
 1561|  3.25k|            retval == READSTAT_ERROR_SEEK) {
  ------------------
  |  Branch (1561:13): [True: 363, False: 2.89k]
  ------------------
 1562|    782|        if (ctx->handle.error) {
  ------------------
  |  Branch (1562:13): [True: 0, False: 782]
  ------------------
 1563|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: %s (retval = %d): %s (errno = %d)", 
 1564|      0|                    readstat_error_message(retval), retval, strerror(errno), errno);
 1565|      0|            ctx->handle.error(ctx->error_buf, user_ctx);
 1566|      0|        }
 1567|    782|    }
 1568|       |
 1569|  3.67k|    if (ctx)
  ------------------
  |  Branch (1569:9): [True: 3.67k, False: 0]
  ------------------
 1570|  3.67k|        sas7bdat_ctx_free(ctx);
 1571|  3.67k|    if (hinfo)
  ------------------
  |  Branch (1571:9): [True: 3.67k, False: 0]
  ------------------
 1572|  3.67k|        free(hinfo);
 1573|       |
 1574|  3.67k|    return retval;
 1575|    116|}
readstat_sas7bdat_read.c:sas7bdat_parse_meta_pages_pass1:
 1272|  3.15k|static readstat_error_t sas7bdat_parse_meta_pages_pass1(sas7bdat_ctx_t *ctx, int64_t *outLastExaminedPage) {
 1273|  3.15k|    readstat_error_t retval = READSTAT_OK;
 1274|  3.15k|    readstat_io_t *io = ctx->io;
 1275|  3.15k|    int64_t i;
 1276|       |
 1277|       |    /* look for META and MIX pages at beginning... */
 1278|  8.35k|    for (i=0; i<ctx->page_count; i++) {
  ------------------
  |  Branch (1278:15): [True: 6.10k, False: 2.25k]
  ------------------
 1279|  6.10k|        if (io->seek(ctx->header_size + i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1279:13): [True: 9, False: 6.09k]
  ------------------
 1280|      9|            retval = READSTAT_ERROR_SEEK;
 1281|      9|            if (ctx->handle.error) {
  ------------------
  |  Branch (1281:17): [True: 0, False: 9]
  ------------------
 1282|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64 
 1283|      0|                        " (= %" PRId64 " + %" PRId64 "*%" PRId64 ")",
 1284|      0|                        ctx->header_size + i*ctx->page_size, ctx->header_size, i, ctx->page_size);
 1285|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1286|      0|            }
 1287|      9|            goto cleanup;
 1288|      9|        }
 1289|       |
 1290|  6.09k|        readstat_off_t off = 0;
 1291|  6.09k|        if (ctx->u64)
  ------------------
  |  Branch (1291:13): [True: 3.86k, False: 2.22k]
  ------------------
 1292|  3.86k|            off = 16;
 1293|       |
 1294|  6.09k|        size_t head_len = off + 16 + 2;
 1295|  6.09k|        size_t tail_len = ctx->page_size - head_len;
 1296|       |
 1297|  6.09k|        if (io->read(ctx->page, head_len, io->io_ctx) < head_len) {
  ------------------
  |  Branch (1297:13): [True: 145, False: 5.94k]
  ------------------
 1298|    145|            retval = READSTAT_ERROR_READ;
 1299|    145|            goto cleanup;
 1300|    145|        }
 1301|       |
 1302|  5.94k|        uint16_t page_type = sas_read2(&ctx->page[off+16], ctx->bswap);
 1303|       |
 1304|  5.94k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA)
  ------------------
  |  |  112|  5.94k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA)
  ------------------
  |  |  109|  5.94k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1304:13): [True: 669, False: 5.27k]
  ------------------
 1305|    669|            break;
 1306|  5.27k|        if ((page_type & SAS_PAGE_TYPE_COMP))
  ------------------
  |  |  116|  5.27k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1306:13): [True: 1.46k, False: 3.81k]
  ------------------
 1307|  1.46k|            continue;
 1308|       |
 1309|  3.81k|        if (io->read(ctx->page + head_len, tail_len, io->io_ctx) < tail_len) {
  ------------------
  |  Branch (1309:13): [True: 33, False: 3.78k]
  ------------------
 1310|     33|            retval = READSTAT_ERROR_READ;
 1311|     33|            goto cleanup;
 1312|     33|        }
 1313|       |
 1314|  3.78k|        if ((retval = sas7bdat_parse_page_pass1(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1314:13): [True: 52, False: 3.73k]
  ------------------
 1315|     52|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1315:17): [True: 0, False: 52]
  |  Branch (1315:38): [True: 0, False: 0]
  ------------------
 1316|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1317|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1318|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1319|      0|                        i, pos - ctx->page_size, pos-1);
 1320|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1321|      0|            }
 1322|     52|            goto cleanup;
 1323|     52|        }
 1324|  3.78k|    }
 1325|       |
 1326|  3.15k|cleanup:
 1327|  3.15k|    if (outLastExaminedPage)
  ------------------
  |  Branch (1327:9): [True: 3.15k, False: 0]
  ------------------
 1328|  3.15k|        *outLastExaminedPage = i;
 1329|       |
 1330|  3.15k|    return retval;
 1331|  3.15k|}
readstat_sas7bdat_read.c:sas7bdat_parse_page_pass1:
  978|  6.31k|static readstat_error_t sas7bdat_parse_page_pass1(const char *page, size_t page_size, sas7bdat_ctx_t *ctx) {
  979|  6.31k|    readstat_error_t retval = READSTAT_OK;
  980|       |
  981|  6.31k|    uint16_t subheader_count = sas_read2(&page[ctx->page_header_size-4], ctx->bswap);
  982|       |
  983|  6.31k|    int i;
  984|  6.31k|    const char *shp = &page[ctx->page_header_size];
  985|  6.31k|    int lshp = ctx->subheader_pointer_size;
  986|       |
  987|  6.31k|    if (ctx->page_header_size + subheader_count*lshp > page_size) {
  ------------------
  |  Branch (987:9): [True: 35, False: 6.27k]
  ------------------
  988|     35|        retval = READSTAT_ERROR_PARSE;
  989|     35|        goto cleanup;
  990|     35|    }
  991|       |
  992|  70.3k|    for (i=0; i<subheader_count; i++) {
  ------------------
  |  Branch (992:15): [True: 64.7k, False: 5.58k]
  ------------------
  993|  64.7k|        subheader_pointer_t shp_info = { 0 };
  994|  64.7k|        if ((retval = sas7bdat_parse_subheader_pointer(shp, page + page_size - shp, &shp_info, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (994:13): [True: 0, False: 64.7k]
  ------------------
  995|      0|            goto cleanup;
  996|      0|        }
  997|  64.7k|        if (shp_info.len > 0 && shp_info.compression != SAS_COMPRESSION_TRUNC && shp_info.compression != SAS_COMPRESSION_REFERENCE) {
  ------------------
  |  |  125|   106k|#define SAS_COMPRESSION_TRUNC              0x01
  ------------------
                      if (shp_info.len > 0 && shp_info.compression != SAS_COMPRESSION_TRUNC && shp_info.compression != SAS_COMPRESSION_REFERENCE) {
  ------------------
  |  |  127|  35.2k|#define SAS_COMPRESSION_REFERENCE          0x03
  ------------------
  |  Branch (997:13): [True: 41.9k, False: 22.7k]
  |  Branch (997:33): [True: 35.2k, False: 6.75k]
  |  Branch (997:82): [True: 35.2k, False: 3]
  ------------------
  998|  35.2k|            if ((retval = sas7bdat_validate_subheader_pointer(&shp_info, page_size, subheader_count, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (998:17): [True: 608, False: 34.6k]
  ------------------
  999|    608|                goto cleanup;
 1000|    608|            }
 1001|  34.6k|            if (shp_info.compression == SAS_COMPRESSION_NONE) {
  ------------------
  |  |  124|  34.6k|#define SAS_COMPRESSION_NONE               0x00
  ------------------
  |  Branch (1001:17): [True: 31.0k, False: 3.58k]
  ------------------
 1002|  31.0k|                sas_subheader_type_t subheader_type = sas7bdat_parse_subheader_type(page + shp_info.offset, ctx);
 1003|  31.0k|                if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (1003:21): [True: 2.67k, False: 28.3k]
  ------------------
 1004|  2.67k|                    if ((retval = sas7bdat_parse_subheader(subheader_type, page + shp_info.offset, shp_info.len, ctx))
  ------------------
  |  Branch (1004:25): [True: 66, False: 2.61k]
  ------------------
 1005|  2.67k|                            != READSTAT_OK) {
 1006|     66|                        goto cleanup;
 1007|     66|                    }
 1008|  2.67k|                }
 1009|  31.0k|            } else if (shp_info.compression == SAS_COMPRESSION_ROW || shp_info.compression == SAS_COMPRESSION_DELETED_ROW
  ------------------
  |  |  128|  7.16k|#define SAS_COMPRESSION_ROW                0x04
  ------------------
                          } else if (shp_info.compression == SAS_COMPRESSION_ROW || shp_info.compression == SAS_COMPRESSION_DELETED_ROW
  ------------------
  |  |  129|  3.97k|#define SAS_COMPRESSION_DELETED_ROW        0x05
  ------------------
  |  Branch (1009:24): [True: 3.19k, False: 387]
  |  Branch (1009:71): [True: 367, False: 20]
  ------------------
 1010|  3.56k|                    || sas7bdat_is_moved_row(shp_info.compression)) {
  ------------------
  |  Branch (1010:24): [True: 2, False: 18]
  ------------------
 1011|       |                /* void */
 1012|  3.56k|            } else {
 1013|     18|                retval = READSTAT_ERROR_UNSUPPORTED_COMPRESSION;
 1014|     18|                goto cleanup;
 1015|     18|            }
 1016|  34.6k|        }
 1017|       |
 1018|  64.0k|        shp += lshp;
 1019|  64.0k|    }
 1020|       |
 1021|  6.31k|cleanup:
 1022|       |
 1023|  6.31k|    return retval;
 1024|  6.27k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_pointer:
  925|   101k|        subheader_pointer_t *info, sas7bdat_ctx_t *ctx) {
  926|   101k|    readstat_error_t retval = READSTAT_OK;
  927|   101k|    if (ctx->u64) {
  ------------------
  |  Branch (927:9): [True: 18.9k, False: 82.5k]
  ------------------
  928|  18.9k|        if (shp_size <= 17) {
  ------------------
  |  Branch (928:13): [True: 0, False: 18.9k]
  ------------------
  929|      0|            retval = READSTAT_ERROR_PARSE;
  930|      0|            goto cleanup;
  931|      0|        }
  932|  18.9k|        info->offset = sas_read8(&shp[0], ctx->bswap);
  933|  18.9k|        info->len = sas_read8(&shp[8], ctx->bswap);
  934|  18.9k|        info->compression = shp[16];
  935|  18.9k|        info->is_compressed_data = shp[17];
  936|  82.5k|    } else {
  937|  82.5k|        if (shp_size <= 9) {
  ------------------
  |  Branch (937:13): [True: 0, False: 82.5k]
  ------------------
  938|      0|            retval = READSTAT_ERROR_PARSE;
  939|      0|            goto cleanup;
  940|      0|        }
  941|  82.5k|        info->offset = sas_read4(&shp[0], ctx->bswap);
  942|  82.5k|        info->len = sas_read4(&shp[4], ctx->bswap);
  943|  82.5k|        info->compression = shp[8];
  944|  82.5k|        info->is_compressed_data = shp[9];
  945|  82.5k|    }
  946|   101k|cleanup:
  947|   101k|    return retval;
  948|   101k|}
readstat_sas7bdat_read.c:sas7bdat_validate_subheader_pointer:
  951|  53.4k|        uint16_t subheader_count, sas7bdat_ctx_t *ctx) {
  952|  53.4k|    if (shp_info->offset > page_size)
  ------------------
  |  Branch (952:9): [True: 370, False: 53.0k]
  ------------------
  953|    370|        return READSTAT_ERROR_PARSE;
  954|  53.0k|    if (shp_info->len > page_size)
  ------------------
  |  Branch (954:9): [True: 197, False: 52.8k]
  ------------------
  955|    197|        return READSTAT_ERROR_PARSE;
  956|  52.8k|    if (shp_info->offset + shp_info->len > page_size)
  ------------------
  |  Branch (956:9): [True: 28, False: 52.8k]
  ------------------
  957|     28|        return READSTAT_ERROR_PARSE;
  958|  52.8k|    if (shp_info->offset < ctx->page_header_size + subheader_count*ctx->subheader_pointer_size)
  ------------------
  |  Branch (958:9): [True: 11, False: 52.8k]
  ------------------
  959|     11|        return READSTAT_ERROR_PARSE;
  960|  52.8k|    if (shp_info->compression == SAS_COMPRESSION_NONE || shp_info->compression == SAS_COMPRESSION_NONE_MOVED) {
  ------------------
  |  |  124|   105k|#define SAS_COMPRESSION_NONE               0x00
  ------------------
                  if (shp_info->compression == SAS_COMPRESSION_NONE || shp_info->compression == SAS_COMPRESSION_NONE_MOVED) {
  ------------------
  |  |  126|  5.50k|#define SAS_COMPRESSION_NONE_MOVED         0x02
  ------------------
  |  Branch (960:9): [True: 47.3k, False: 5.50k]
  |  Branch (960:58): [True: 2, False: 5.50k]
  ------------------
  961|  47.3k|        if (shp_info->len < ctx->subheader_signature_size)
  ------------------
  |  Branch (961:13): [True: 3, False: 47.3k]
  ------------------
  962|      3|            return READSTAT_ERROR_PARSE;
  963|  47.3k|        if (shp_info->offset + ctx->subheader_signature_size > page_size)
  ------------------
  |  Branch (963:13): [True: 0, False: 47.3k]
  ------------------
  964|      0|            return READSTAT_ERROR_PARSE;
  965|  47.3k|    }
  966|       |    
  967|  52.8k|    return READSTAT_OK;
  968|  52.8k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_type:
  889|  47.3k|static sas_subheader_type_t sas7bdat_parse_subheader_type(const char* subheader, sas7bdat_ctx_t* ctx) {
  890|  47.3k|    if (!ctx->u64) {
  ------------------
  |  Branch (890:9): [True: 35.9k, False: 11.3k]
  ------------------
  891|  35.9k|        uint32_t signature_32 = sas_read4(subheader, ctx->bswap);
  892|  35.9k|        return sas7bdat_parse_subheader_type_32(signature_32);
  893|  35.9k|    }
  894|       |
  895|  11.3k|    if (!ctx->little_endian) {
  ------------------
  |  Branch (895:9): [True: 10.2k, False: 1.11k]
  ------------------
  896|       |        /* Big-endian 64-bit files lay out signatures differently from
  897|       |         * little-endian ones: ROW_SIZE and COLUMN_SIZE occupy only the first
  898|       |         * four bytes (the remaining four are unspecified), while the 0xFFFF...
  899|       |         * family is written as 0xFFFFFFFF followed by the 32-bit signature. */
  900|  10.2k|        uint32_t signature_32 = sas_read4(subheader, ctx->bswap);
  901|  10.2k|        if (signature_32 == SAS_SUBHEADER_SIGNATURE_ROW_SIZE) {
  ------------------
  |  |   92|  10.2k|#define SAS_SUBHEADER_SIGNATURE_ROW_SIZE       0xF7F7F7F7
  ------------------
  |  Branch (901:13): [True: 274, False: 9.95k]
  ------------------
  902|    274|            return SAS_SUBHEADER_TYPE_ROW_SIZE;
  903|  9.95k|        } else if (signature_32 == SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE) {
  ------------------
  |  |   93|  9.95k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE    0xF6F6F6F6
  ------------------
  |  Branch (903:20): [True: 68, False: 9.88k]
  ------------------
  904|     68|            return SAS_SUBHEADER_TYPE_COLUMN_SIZE;
  905|  9.88k|        } else if (signature_32 != SAS_SUBHEADER_SIGNATURE_COLUMN_NAME) {
  ------------------
  |  |  103|  9.88k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_NAME    0xFFFFFFFF
  ------------------
  |  Branch (905:20): [True: 2.48k, False: 7.40k]
  ------------------
  906|  2.48k|            return SAS_SUBHEADER_TYPE_DATA;
  907|  2.48k|        }
  908|  7.40k|        return sas7bdat_parse_subheader_type_32(sas_read4(&subheader[4], ctx->bswap));
  909|  10.2k|    }
  910|       |
  911|  1.11k|    uint64_t signature = sas_read8(subheader, ctx->bswap);
  912|  1.11k|    if (signature == SAS_SUBHEADER_SIGNATURE_ROW_SIZE) {
  ------------------
  |  |   92|  1.11k|#define SAS_SUBHEADER_SIGNATURE_ROW_SIZE       0xF7F7F7F7
  ------------------
  |  Branch (912:9): [True: 102, False: 1.01k]
  ------------------
  913|    102|        return SAS_SUBHEADER_TYPE_ROW_SIZE;
  914|  1.01k|    } else if (signature == SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE) {
  ------------------
  |  |   93|  1.01k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE    0xF6F6F6F6
  ------------------
  |  Branch (914:16): [True: 102, False: 913]
  ------------------
  915|    102|        return SAS_SUBHEADER_TYPE_COLUMN_SIZE;
  916|    913|    } else if ((signature & SAS_SUBHEADER_SIGNATURE_64BIT_MASK) != SAS_SUBHEADER_SIGNATURE_64BIT_MASK) {
  ------------------
  |  |  105|    913|#define SAS_SUBHEADER_SIGNATURE_64BIT_MASK     0xFFFFFFFF00000000
  ------------------
                  } else if ((signature & SAS_SUBHEADER_SIGNATURE_64BIT_MASK) != SAS_SUBHEADER_SIGNATURE_64BIT_MASK) {
  ------------------
  |  |  105|    913|#define SAS_SUBHEADER_SIGNATURE_64BIT_MASK     0xFFFFFFFF00000000
  ------------------
  |  Branch (916:16): [True: 28, False: 885]
  ------------------
  917|     28|        return SAS_SUBHEADER_TYPE_DATA;
  918|     28|    }
  919|       |
  920|    885|    uint32_t lower_bytes = (uint32_t)(signature & SAS_SUBHEADER_SIGNATURE_32BIT_MASK);
  ------------------
  |  |  106|    885|#define SAS_SUBHEADER_SIGNATURE_32BIT_MASK     0x00000000FFFFFFFF
  ------------------
  921|    885|    return sas7bdat_parse_subheader_type_32(lower_bytes);
  922|  1.11k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_type_32:
  863|  44.2k|static sas_subheader_type_t sas7bdat_parse_subheader_type_32(uint32_t signature) {
  864|  44.2k|    switch (signature) {
  865|  7.74k|        case SAS_SUBHEADER_SIGNATURE_ROW_SIZE:
  ------------------
  |  |   92|  7.74k|#define SAS_SUBHEADER_SIGNATURE_ROW_SIZE       0xF7F7F7F7
  ------------------
  |  Branch (865:9): [True: 7.74k, False: 36.5k]
  ------------------
  866|  7.74k|            return SAS_SUBHEADER_TYPE_ROW_SIZE;
  867|  3.12k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE:
  ------------------
  |  |   93|  3.12k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE    0xF6F6F6F6
  ------------------
  |  Branch (867:9): [True: 3.12k, False: 41.1k]
  ------------------
  868|  3.12k|            return SAS_SUBHEADER_TYPE_COLUMN_SIZE;
  869|  2.80k|        case SAS_SUBHEADER_SIGNATURE_COUNTS:
  ------------------
  |  |   94|  2.80k|#define SAS_SUBHEADER_SIGNATURE_COUNTS         0xFFFFFC00
  ------------------
  |  Branch (869:9): [True: 2.80k, False: 41.4k]
  ------------------
  870|  2.80k|            return SAS_SUBHEADER_TYPE_COUNTS;
  871|  8.19k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_FORMAT:
  ------------------
  |  |   95|  8.19k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_FORMAT  0xFFFFFBFE
  ------------------
  |  Branch (871:9): [True: 8.19k, False: 36.0k]
  ------------------
  872|  8.19k|            return SAS_SUBHEADER_TYPE_COLUMN_FORMAT;
  873|  4.78k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_ATTRS:
  ------------------
  |  |  100|  4.78k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_ATTRS   0xFFFFFFFC
  ------------------
  |  Branch (873:9): [True: 4.78k, False: 39.4k]
  ------------------
  874|  4.78k|            return SAS_SUBHEADER_TYPE_COLUMN_ATTRS;
  875|  4.43k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_TEXT:
  ------------------
  |  |  101|  4.43k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_TEXT    0xFFFFFFFD
  ------------------
  |  Branch (875:9): [True: 4.43k, False: 39.8k]
  ------------------
  876|  4.43k|            return SAS_SUBHEADER_TYPE_COLUMN_TEXT;
  877|  2.33k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_LIST:
  ------------------
  |  |  102|  2.33k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_LIST    0xFFFFFFFE
  ------------------
  |  Branch (877:9): [True: 2.33k, False: 41.9k]
  ------------------
  878|  2.33k|            return SAS_SUBHEADER_TYPE_COLUMN_LIST;
  879|  5.50k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_NAME:
  ------------------
  |  |  103|  5.50k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_NAME    0xFFFFFFFF
  ------------------
  |  Branch (879:9): [True: 5.50k, False: 38.7k]
  ------------------
  880|  5.50k|            return SAS_SUBHEADER_TYPE_COLUMN_NAME;
  881|  5.30k|        default:
  ------------------
  |  Branch (881:9): [True: 5.30k, False: 38.9k]
  ------------------
  882|  5.30k|            if ((signature & SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) == SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) {
  ------------------
  |  |   97|  5.30k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_MASK    0xFFFFFFF8
  ------------------
                          if ((signature & SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) == SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) {
  ------------------
  |  |   97|  5.30k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_MASK    0xFFFFFFF8
  ------------------
  |  Branch (882:17): [True: 1.74k, False: 3.56k]
  ------------------
  883|  1.74k|                return SAS_SUBHEADER_TYPE_UNKNOWN;
  884|  1.74k|            }
  885|  3.56k|            return SAS_SUBHEADER_TYPE_DATA;
  886|  44.2k|    }
  887|  44.2k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader:
  682|  16.2k|        size_t len, sas7bdat_ctx_t *ctx) {
  683|  16.2k|    readstat_error_t retval = READSTAT_OK;
  684|       |
  685|  16.2k|    if (len < 2 + ctx->subheader_signature_size) {
  ------------------
  |  Branch (685:9): [True: 4, False: 16.2k]
  ------------------
  686|      4|        retval = READSTAT_ERROR_PARSE;
  687|      4|        goto cleanup;
  688|      4|    }
  689|  16.2k|    if (subheader_type == SAS_SUBHEADER_TYPE_ROW_SIZE) {
  ------------------
  |  Branch (689:9): [True: 3.11k, False: 13.1k]
  ------------------
  690|  3.11k|        retval = sas7bdat_parse_row_size_subheader(subheader, len, ctx);
  691|  13.1k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_SIZE) {
  ------------------
  |  Branch (691:16): [True: 1.11k, False: 12.0k]
  ------------------
  692|  1.11k|        retval = sas7bdat_parse_column_size_subheader(subheader, len, ctx);
  693|  12.0k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COUNTS) {
  ------------------
  |  Branch (693:16): [True: 1.21k, False: 10.7k]
  ------------------
  694|       |        /* void */
  695|  10.7k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (695:16): [True: 2.67k, False: 8.11k]
  ------------------
  696|  2.67k|        retval = sas7bdat_parse_column_text_subheader(subheader, len, ctx);
  697|  8.11k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_NAME) {
  ------------------
  |  Branch (697:16): [True: 1.21k, False: 6.90k]
  ------------------
  698|  1.21k|        retval = sas7bdat_parse_column_name_subheader(subheader, len, ctx);
  699|  6.90k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_ATTRS) {
  ------------------
  |  Branch (699:16): [True: 1.83k, False: 5.07k]
  ------------------
  700|  1.83k|        retval = sas7bdat_parse_column_attributes_subheader(subheader, len, ctx);
  701|  5.07k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_FORMAT) {
  ------------------
  |  Branch (701:16): [True: 3.57k, False: 1.49k]
  ------------------
  702|  3.57k|        retval = sas7bdat_parse_column_format_subheader(subheader, len, ctx);
  703|  3.57k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_LIST) {
  ------------------
  |  Branch (703:16): [True: 693, False: 804]
  ------------------
  704|       |        /* void */
  705|    804|    } else if (subheader_type == SAS_SUBHEADER_TYPE_UNKNOWN) {
  ------------------
  |  Branch (705:16): [True: 764, False: 40]
  ------------------
  706|       |        /* void */
  707|    764|    } else {
  708|     40|        retval = READSTAT_ERROR_PARSE;
  709|     40|    }
  710|       |
  711|  16.2k|cleanup:
  712|       |
  713|  16.2k|    return retval;
  714|  16.2k|}
readstat_sas7bdat_read.c:sas7bdat_parse_row_size_subheader:
  242|  3.11k|static readstat_error_t sas7bdat_parse_row_size_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  243|  3.11k|    readstat_error_t retval = READSTAT_OK;
  244|  3.11k|    uint64_t total_row_count;
  245|  3.11k|    uint64_t row_length, deleted_row_count, page_row_count;
  246|       |
  247|  3.11k|    if (len < (ctx->u64 ? 250: 190)) {
  ------------------
  |  Branch (247:9): [True: 3, False: 3.11k]
  |  Branch (247:16): [True: 598, False: 2.51k]
  ------------------
  248|      3|        retval = READSTAT_ERROR_PARSE;
  249|      3|        goto cleanup;
  250|      3|    }
  251|       |
  252|  3.11k|    if (ctx->u64) {
  ------------------
  |  Branch (252:9): [True: 598, False: 2.51k]
  ------------------
  253|    598|        row_length = sas_read8(&subheader[40], ctx->bswap);
  254|    598|        total_row_count = sas_read8(&subheader[48], ctx->bswap);
  255|    598|        deleted_row_count = sas_read8(&subheader[56], ctx->bswap);
  256|    598|        page_row_count = sas_read8(&subheader[120], ctx->bswap);
  257|  2.51k|    } else {
  258|  2.51k|        row_length = sas_read4(&subheader[20], ctx->bswap);
  259|  2.51k|        total_row_count = sas_read4(&subheader[24], ctx->bswap);
  260|  2.51k|        deleted_row_count = sas_read4(&subheader[28], ctx->bswap);
  261|  2.51k|        page_row_count = sas_read4(&subheader[60], ctx->bswap);
  262|  2.51k|    }
  263|       |
  264|  3.11k|    if (deleted_row_count > total_row_count) {
  ------------------
  |  Branch (264:9): [True: 58, False: 3.05k]
  ------------------
  265|     58|        retval = READSTAT_ERROR_PARSE;
  266|     58|        goto cleanup;
  267|     58|    }
  268|  3.05k|    ctx->total_row_count = total_row_count;
  269|  3.05k|    ctx->deleted_row_count = deleted_row_count;
  270|       |
  271|  3.05k|    sas_text_ref_t file_label_ref = sas7bdat_parse_text_ref(&subheader[len-130], ctx);
  272|  3.05k|    if (file_label_ref.length) {
  ------------------
  |  Branch (272:9): [True: 1.03k, False: 2.01k]
  ------------------
  273|  1.03k|        if ((retval = sas7bdat_copy_text_ref(ctx->file_label, sizeof(ctx->file_label),
  ------------------
  |  Branch (273:13): [True: 45, False: 989]
  ------------------
  274|  1.03k|                        file_label_ref, ctx)) != READSTAT_OK) {
  275|     45|            goto cleanup;
  276|     45|        }
  277|  1.03k|    }
  278|       |
  279|  3.00k|    sas_text_ref_t compression_ref = sas7bdat_parse_text_ref(&subheader[len-118], ctx);
  280|  3.00k|    if (compression_ref.length) {
  ------------------
  |  Branch (280:9): [True: 1.41k, False: 1.59k]
  ------------------
  281|  1.41k|        char compression[9];
  282|  1.41k|        if ((retval = sas7bdat_copy_text_ref(compression, sizeof(compression),
  ------------------
  |  Branch (282:13): [True: 56, False: 1.35k]
  ------------------
  283|  1.41k|                        compression_ref, ctx)) != READSTAT_OK) {
  284|     56|            goto cleanup;
  285|     56|        }
  286|  1.35k|        ctx->rdc_compression = (memcmp(compression, SAS_COMPRESSION_SIGNATURE_RDC, 8) == 0);
  ------------------
  |  |  135|  1.35k|#define SAS_COMPRESSION_SIGNATURE_RDC  "SASYZCR2"
  ------------------
  287|  1.35k|    }
  288|       |
  289|  2.95k|    ctx->row_length = row_length;
  290|  2.95k|    ctx->row = readstat_realloc(ctx->row, ctx->row_length);
  291|  2.95k|    if (ctx->row == NULL) {
  ------------------
  |  Branch (291:9): [True: 9, False: 2.94k]
  ------------------
  292|      9|        retval = READSTAT_ERROR_MALLOC;
  293|      9|        goto cleanup;
  294|      9|    }
  295|       |
  296|  2.94k|    ctx->page_row_count = page_row_count;
  297|  2.94k|    uint64_t live_row_count = total_row_count - deleted_row_count;
  298|  2.94k|    uint64_t total_row_count_after_skipping = live_row_count;
  299|  2.94k|    if (live_row_count > ctx->row_offset) {
  ------------------
  |  Branch (299:9): [True: 2.63k, False: 313]
  ------------------
  300|  2.63k|        total_row_count_after_skipping -= ctx->row_offset;
  301|  2.63k|    } else {
  302|    313|        total_row_count_after_skipping = 0;
  303|    313|        ctx->row_offset = live_row_count;
  304|    313|    }
  305|  2.94k|    if (ctx->row_limit == 0 || total_row_count_after_skipping < ctx->row_limit)
  ------------------
  |  Branch (305:9): [True: 1.07k, False: 1.87k]
  |  Branch (305:32): [True: 68, False: 1.80k]
  ------------------
  306|  1.13k|        ctx->row_limit = total_row_count_after_skipping;
  307|       |
  308|  3.11k|cleanup:
  309|  3.11k|    return retval;
  310|  2.94k|}
readstat_sas7bdat_read.c:sas7bdat_parse_text_ref:
  143|  40.8k|static sas_text_ref_t sas7bdat_parse_text_ref(const char *data, sas7bdat_ctx_t *ctx) {
  144|  40.8k|    sas_text_ref_t  ref;
  145|       |
  146|  40.8k|    ref.index = sas_read2(&data[0], ctx->bswap);
  147|  40.8k|    ref.offset = sas_read2(&data[2], ctx->bswap);
  148|  40.8k|    ref.length = sas_read2(&data[4], ctx->bswap);
  149|       |
  150|  40.8k|    return ref;
  151|  40.8k|}
readstat_sas7bdat_read.c:sas7bdat_copy_text_ref:
  153|  9.15M|static readstat_error_t sas7bdat_copy_text_ref(char *out_buffer, size_t out_buffer_len, sas_text_ref_t text_ref, sas7bdat_ctx_t *ctx) {
  154|  9.15M|    if (text_ref.index >= ctx->text_blob_count)
  ------------------
  |  Branch (154:9): [True: 134, False: 9.15M]
  ------------------
  155|    134|        return READSTAT_ERROR_PARSE;
  156|       |    
  157|  9.15M|    if (text_ref.length == 0) {
  ------------------
  |  Branch (157:9): [True: 9.15M, False: 4.44k]
  ------------------
  158|  9.15M|        out_buffer[0] = '\0';
  159|  9.15M|        return READSTAT_OK;
  160|  9.15M|    }
  161|       |
  162|  4.44k|    char *blob = ctx->text_blobs[text_ref.index];
  163|       |
  164|  4.44k|    if (text_ref.offset + text_ref.length > ctx->text_blob_lengths[text_ref.index])
  ------------------
  |  Branch (164:9): [True: 78, False: 4.36k]
  ------------------
  165|     78|        return READSTAT_ERROR_PARSE;
  166|       |
  167|  4.36k|    return readstat_convert(out_buffer, out_buffer_len, &blob[text_ref.offset], text_ref.length,
  168|  4.36k|            ctx->converter);
  169|  4.44k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_size_subheader:
  214|  1.11k|static readstat_error_t sas7bdat_parse_column_size_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  215|  1.11k|    uint64_t col_count;
  216|  1.11k|    readstat_error_t retval = READSTAT_OK;
  217|       |
  218|  1.11k|    if (ctx->column_count || ctx->did_submit_columns) {
  ------------------
  |  Branch (218:9): [True: 9, False: 1.10k]
  |  Branch (218:30): [True: 3, False: 1.09k]
  ------------------
  219|     12|        retval = READSTAT_ERROR_PARSE;
  220|     12|        goto cleanup;
  221|     12|    }
  222|       |
  223|  1.09k|    if (len < (ctx->u64 ? 16 : 8)) {
  ------------------
  |  Branch (223:9): [True: 3, False: 1.09k]
  |  Branch (223:16): [True: 83, False: 1.01k]
  ------------------
  224|      3|        retval = READSTAT_ERROR_PARSE;
  225|      3|        goto cleanup;
  226|      3|    }
  227|       |
  228|  1.09k|    if (ctx->u64) {
  ------------------
  |  Branch (228:9): [True: 82, False: 1.01k]
  ------------------
  229|     82|        col_count = sas_read8(&subheader[8], ctx->bswap);
  230|  1.01k|    } else {
  231|  1.01k|        col_count = sas_read4(&subheader[4], ctx->bswap);
  232|  1.01k|    }
  233|       |
  234|  1.09k|    ctx->column_count = col_count;
  235|       |
  236|  1.09k|    retval = sas7bdat_realloc_col_info(ctx, ctx->column_count);
  237|       |
  238|  1.11k|cleanup:
  239|  1.11k|    return retval;
  240|  1.09k|}
readstat_sas7bdat_read.c:sas7bdat_realloc_col_info:
  201|  7.60k|static readstat_error_t sas7bdat_realloc_col_info(sas7bdat_ctx_t *ctx, size_t count) {
  202|  7.60k|    if (ctx->col_info_count < count) {
  ------------------
  |  Branch (202:9): [True: 3.73k, False: 3.86k]
  ------------------
  203|  3.73k|        size_t old_count = ctx->col_info_count;
  204|  3.73k|        ctx->col_info_count = count;
  205|  3.73k|        ctx->col_info = readstat_realloc(ctx->col_info, ctx->col_info_count * sizeof(col_info_t));
  206|  3.73k|        if (ctx->col_info == NULL) {
  ------------------
  |  Branch (206:13): [True: 82, False: 3.65k]
  ------------------
  207|     82|            return READSTAT_ERROR_MALLOC;
  208|     82|        }
  209|  3.65k|        memset(ctx->col_info + old_count, 0, (count - old_count) * sizeof(col_info_t));
  210|  3.65k|    }
  211|  7.52k|    return READSTAT_OK;
  212|  7.60k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_text_subheader:
  171|  2.67k|static readstat_error_t sas7bdat_parse_column_text_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  172|  2.67k|    readstat_error_t retval = READSTAT_OK;
  173|  2.67k|    size_t signature_len = ctx->subheader_signature_size;
  174|  2.67k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  175|  2.67k|    char *blob = NULL;
  176|  2.67k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (176:9): [True: 65, False: 2.61k]
  ------------------
  177|     65|        retval = READSTAT_ERROR_PARSE;
  178|     65|        goto cleanup;
  179|     65|    }
  180|  2.61k|    ctx->text_blob_count++;
  181|  2.61k|    ctx->text_blobs = readstat_realloc(ctx->text_blobs, ctx->text_blob_count * sizeof(char *));
  182|  2.61k|    ctx->text_blob_lengths = readstat_realloc(ctx->text_blob_lengths,
  183|  2.61k|            ctx->text_blob_count * sizeof(ctx->text_blob_lengths[0]));
  184|  2.61k|    if (ctx->text_blobs == NULL || ctx->text_blob_lengths == NULL) {
  ------------------
  |  Branch (184:9): [True: 0, False: 2.61k]
  |  Branch (184:36): [True: 0, False: 2.61k]
  ------------------
  185|      0|        retval = READSTAT_ERROR_MALLOC;
  186|      0|        goto cleanup;
  187|      0|    }
  188|       |
  189|  2.61k|    if ((blob = readstat_malloc(len-signature_len)) == NULL) {
  ------------------
  |  Branch (189:9): [True: 0, False: 2.61k]
  ------------------
  190|      0|        retval = READSTAT_ERROR_MALLOC;
  191|      0|        goto cleanup;
  192|      0|    }
  193|  2.61k|    memcpy(blob, subheader+signature_len, len-signature_len);
  194|  2.61k|    ctx->text_blob_lengths[ctx->text_blob_count-1] = len-signature_len;
  195|  2.61k|    ctx->text_blobs[ctx->text_blob_count-1] = blob;
  196|       |
  197|  2.67k|cleanup:
  198|  2.67k|    return retval;
  199|  2.61k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_name_subheader:
  312|  1.21k|static readstat_error_t sas7bdat_parse_column_name_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  313|  1.21k|    readstat_error_t retval = READSTAT_OK;
  314|  1.21k|    size_t signature_len = ctx->subheader_signature_size;
  315|  1.21k|    int cmax = ctx->u64 ? (len-28)/8 : (len-20)/8;
  ------------------
  |  Branch (315:16): [True: 99, False: 1.11k]
  ------------------
  316|  1.21k|    int i;
  317|  1.21k|    const char *cnp = &subheader[signature_len+8];
  318|  1.21k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  319|       |
  320|  1.21k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (320:9): [True: 56, False: 1.15k]
  ------------------
  321|     56|        retval = READSTAT_ERROR_PARSE;
  322|     56|        goto cleanup;
  323|     56|    }
  324|       |
  325|  1.15k|    ctx->col_names_count += cmax;
  326|       |
  327|  1.15k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_names_count)) != READSTAT_OK)
  ------------------
  |  Branch (327:9): [True: 14, False: 1.14k]
  ------------------
  328|     14|        goto cleanup;
  329|       |
  330|  28.8k|    for (i=ctx->col_names_count-cmax; i<ctx->col_names_count; i++) {
  ------------------
  |  Branch (330:39): [True: 27.6k, False: 1.14k]
  ------------------
  331|  27.6k|        ctx->col_info[i].name_ref = sas7bdat_parse_text_ref(cnp, ctx);
  332|  27.6k|        cnp += 8;
  333|  27.6k|    }
  334|       |
  335|  1.21k|cleanup:
  336|       |
  337|  1.21k|    return retval;
  338|  1.14k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_attributes_subheader:
  340|  1.83k|static readstat_error_t sas7bdat_parse_column_attributes_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  341|  1.83k|    readstat_error_t retval = READSTAT_OK;
  342|  1.83k|    size_t signature_len = ctx->subheader_signature_size;
  343|  1.83k|    int cmax = ctx->u64 ? (len-28)/16 : (len-20)/12;
  ------------------
  |  Branch (343:16): [True: 122, False: 1.70k]
  ------------------
  344|  1.83k|    int i;
  345|  1.83k|    const char *cap = &subheader[signature_len+8];
  346|  1.83k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  347|       |
  348|  1.83k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (348:9): [True: 54, False: 1.77k]
  ------------------
  349|     54|        retval = READSTAT_ERROR_PARSE;
  350|     54|        goto cleanup;
  351|     54|    }
  352|  1.77k|    ctx->col_attrs_count += cmax;
  353|  1.77k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_attrs_count)) != READSTAT_OK)
  ------------------
  |  Branch (353:9): [True: 11, False: 1.76k]
  ------------------
  354|     11|        goto cleanup;
  355|       |
  356|  9.73k|    for (i=ctx->col_attrs_count-cmax; i<ctx->col_attrs_count; i++) {
  ------------------
  |  Branch (356:39): [True: 7.98k, False: 1.75k]
  ------------------
  357|  7.98k|        if (ctx->u64) {
  ------------------
  |  Branch (357:13): [True: 839, False: 7.14k]
  ------------------
  358|    839|            ctx->col_info[i].offset = sas_read8(&cap[0], ctx->bswap);
  359|  7.14k|        } else {
  360|  7.14k|            ctx->col_info[i].offset = sas_read4(&cap[0], ctx->bswap);
  361|  7.14k|        }
  362|       |
  363|  7.98k|        readstat_off_t off=4;
  364|  7.98k|        if (ctx->u64)
  ------------------
  |  Branch (364:13): [True: 839, False: 7.14k]
  ------------------
  365|    839|            off=8;
  366|       |
  367|  7.98k|        ctx->col_info[i].width = sas_read4(&cap[off], ctx->bswap);
  368|  7.98k|        if (ctx->col_info[i].width > ctx->max_col_width)
  ------------------
  |  Branch (368:13): [True: 873, False: 7.11k]
  ------------------
  369|    873|            ctx->max_col_width = ctx->col_info[i].width;
  370|       |
  371|  7.98k|        if (cap[off+6] == SAS_COLUMN_TYPE_NUM) {
  ------------------
  |  |   89|  7.98k|#define SAS_COLUMN_TYPE_NUM  0x01
  ------------------
  |  Branch (371:13): [True: 4.92k, False: 3.06k]
  ------------------
  372|  4.92k|            ctx->col_info[i].type = READSTAT_TYPE_DOUBLE;
  373|  4.92k|        } else if (cap[off+6] == SAS_COLUMN_TYPE_CHR) {
  ------------------
  |  |   90|  3.06k|#define SAS_COLUMN_TYPE_CHR  0x02
  ------------------
  |  Branch (373:20): [True: 3.04k, False: 16]
  ------------------
  374|  3.04k|            ctx->col_info[i].type = READSTAT_TYPE_STRING;
  375|  3.04k|        } else {
  376|     16|            retval = READSTAT_ERROR_PARSE;
  377|     16|            goto cleanup;
  378|     16|        }
  379|  7.97k|        ctx->col_info[i].index = i;
  380|  7.97k|        cap += off+8;
  381|  7.97k|    }
  382|       |
  383|  1.83k|cleanup:
  384|       |
  385|  1.83k|    return retval;
  386|  1.76k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_format_subheader:
  388|  3.57k|static readstat_error_t sas7bdat_parse_column_format_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  389|  3.57k|    readstat_error_t retval = READSTAT_OK;
  390|       |
  391|  3.57k|    if (len < (ctx->u64 ? 58 : 46)) {
  ------------------
  |  Branch (391:9): [True: 3, False: 3.57k]
  |  Branch (391:16): [True: 344, False: 3.23k]
  ------------------
  392|      3|        retval = READSTAT_ERROR_PARSE;
  393|      3|        goto cleanup;
  394|      3|    }
  395|       |
  396|  3.57k|    ctx->col_formats_count++;
  397|  3.57k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_formats_count)) != READSTAT_OK)
  ------------------
  |  Branch (397:9): [True: 0, False: 3.57k]
  ------------------
  398|      0|        goto cleanup;
  399|       |
  400|  3.57k|    if (ctx->u64) {
  ------------------
  |  Branch (400:9): [True: 343, False: 3.23k]
  ------------------
  401|    343|        ctx->col_info[ctx->col_formats_count-1].format_width = sas_read2(&subheader[24], ctx->bswap);
  402|    343|        ctx->col_info[ctx->col_formats_count-1].format_digits = sas_read2(&subheader[26], ctx->bswap);
  403|  3.23k|    } else {
  404|  3.23k|        ctx->col_info[ctx->col_formats_count-1].format_width = sas_read2(&subheader[12], ctx->bswap);
  405|  3.23k|        ctx->col_info[ctx->col_formats_count-1].format_digits = sas_read2(&subheader[14], ctx->bswap);
  406|  3.23k|    }
  407|  3.57k|    ctx->col_info[ctx->col_formats_count-1].format_ref = sas7bdat_parse_text_ref(
  408|  3.57k|            ctx->u64 ? &subheader[46] : &subheader[34], ctx);
  ------------------
  |  Branch (408:13): [True: 343, False: 3.23k]
  ------------------
  409|  3.57k|    ctx->col_info[ctx->col_formats_count-1].label_ref = sas7bdat_parse_text_ref(
  410|  3.57k|            ctx->u64 ? &subheader[52] : &subheader[40], ctx);
  ------------------
  |  Branch (410:13): [True: 343, False: 3.23k]
  ------------------
  411|       |
  412|  3.57k|cleanup:
  413|  3.57k|    return retval;
  414|  3.57k|}
readstat_sas7bdat_read.c:sas7bdat_is_moved_row:
  970|     20|static int sas7bdat_is_moved_row(unsigned char compression) {
  971|     20|    return compression == SAS_COMPRESSION_ROW_MOVED
  ------------------
  |  |  130|     40|#define SAS_COMPRESSION_ROW_MOVED          0x06
  ------------------
  |  Branch (971:12): [True: 0, False: 20]
  ------------------
  972|     20|        || compression == SAS_COMPRESSION_NONE_MOVED
  ------------------
  |  |  126|     40|#define SAS_COMPRESSION_NONE_MOVED         0x02
  ------------------
  |  Branch (972:12): [True: 2, False: 18]
  ------------------
  973|     18|        || compression == SAS_COMPRESSION_ROW_UNREFERENCED
  ------------------
  |  |  132|     38|#define SAS_COMPRESSION_ROW_UNREFERENCED   0x0d
  ------------------
  |  Branch (973:12): [True: 0, False: 18]
  ------------------
  974|     18|        || compression == SAS_COMPRESSION_NONE_UNREFERENCED;
  ------------------
  |  |  131|     18|#define SAS_COMPRESSION_NONE_UNREFERENCED  0x09
  ------------------
  |  Branch (974:12): [True: 0, False: 18]
  ------------------
  975|     20|}
readstat_sas7bdat_read.c:sas7bdat_parse_amd_pages_pass1:
 1333|  2.91k|static readstat_error_t sas7bdat_parse_amd_pages_pass1(int64_t last_examined_page_pass1, sas7bdat_ctx_t *ctx) {
 1334|  2.91k|    readstat_error_t retval = READSTAT_OK;
 1335|  2.91k|    readstat_io_t *io = ctx->io;
 1336|  2.91k|    uint64_t i;
 1337|  2.91k|    uint64_t amd_page_count = 0;
 1338|       |
 1339|       |    /* ...then AMD pages at the end */
 1340|  6.25k|    for (i=ctx->page_count-1; i>last_examined_page_pass1; i--) {
  ------------------
  |  Branch (1340:31): [True: 4.63k, False: 1.62k]
  ------------------
 1341|  4.63k|        if (io->seek(ctx->header_size + i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1341:13): [True: 220, False: 4.41k]
  ------------------
 1342|    220|            retval = READSTAT_ERROR_SEEK;
 1343|    220|            if (ctx->handle.error) {
  ------------------
  |  Branch (1343:17): [True: 0, False: 220]
  ------------------
 1344|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64 
 1345|      0|                        " (= %" PRId64 " + %" PRId64 "*%" PRId64 ")",
 1346|      0|                        ctx->header_size + i*ctx->page_size, ctx->header_size, i, ctx->page_size);
 1347|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1348|      0|            }
 1349|    220|            goto cleanup;
 1350|    220|        }
 1351|       |
 1352|  4.41k|        readstat_off_t off = 0;
 1353|  4.41k|        if (ctx->u64)
  ------------------
  |  Branch (1353:13): [True: 3.30k, False: 1.10k]
  ------------------
 1354|  3.30k|            off = 16;
 1355|       |
 1356|  4.41k|        size_t head_len = off + 16 + 2;
 1357|  4.41k|        size_t tail_len = ctx->page_size - head_len;
 1358|       |
 1359|  4.41k|        if (io->read(ctx->page, head_len, io->io_ctx) < head_len) {
  ------------------
  |  Branch (1359:13): [True: 9, False: 4.40k]
  ------------------
 1360|      9|            retval = READSTAT_ERROR_READ;
 1361|      9|            goto cleanup;
 1362|      9|        }
 1363|       |
 1364|  4.40k|        uint16_t page_type = sas_read2(&ctx->page[off+16], ctx->bswap);
 1365|       |
 1366|  4.40k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  112|  4.40k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  109|  4.40k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1366:13): [True: 1.04k, False: 3.35k]
  ------------------
 1367|       |            /* Usually AMD pages are at the end but sometimes data pages appear after them */
 1368|  1.04k|            if (amd_page_count > 0)
  ------------------
  |  Branch (1368:17): [True: 377, False: 669]
  ------------------
 1369|    377|                break;
 1370|    669|            continue;
 1371|  1.04k|        }
 1372|  3.35k|        if ((page_type & SAS_PAGE_TYPE_COMP))
  ------------------
  |  |  116|  3.35k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1372:13): [True: 807, False: 2.54k]
  ------------------
 1373|    807|            continue;
 1374|       |
 1375|  2.54k|        if (io->read(ctx->page + head_len, tail_len, io->io_ctx) < tail_len) {
  ------------------
  |  Branch (1375:13): [True: 18, False: 2.53k]
  ------------------
 1376|     18|            retval = READSTAT_ERROR_READ;
 1377|     18|            goto cleanup;
 1378|     18|        }
 1379|       |
 1380|  2.53k|        if ((retval = sas7bdat_parse_page_pass1(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1380:13): [True: 675, False: 1.85k]
  ------------------
 1381|    675|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1381:17): [True: 0, False: 675]
  |  Branch (1381:38): [True: 0, False: 0]
  ------------------
 1382|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1383|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1384|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1385|      0|                        i, pos - ctx->page_size, pos-1);
 1386|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1387|      0|            }
 1388|    675|            goto cleanup;
 1389|    675|        }
 1390|       |
 1391|  1.85k|        amd_page_count++;
 1392|  1.85k|    }
 1393|       |
 1394|  2.91k|cleanup:
 1395|       |
 1396|  2.91k|    return retval;
 1397|  2.91k|}
readstat_sas7bdat_read.c:sas7bdat_parse_all_pages_pass2:
 1399|  1.99k|static readstat_error_t sas7bdat_parse_all_pages_pass2(sas7bdat_ctx_t *ctx) {
 1400|  1.99k|    readstat_error_t retval = READSTAT_OK;
 1401|  1.99k|    readstat_io_t *io = ctx->io;
 1402|  1.99k|    int64_t i;
 1403|       |
 1404|  3.39k|    for (i=0; i<ctx->page_count; i++) {
  ------------------
  |  Branch (1404:15): [True: 3.07k, False: 315]
  ------------------
 1405|  3.07k|        if ((retval = sas7bdat_update_progress(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1405:13): [True: 0, False: 3.07k]
  ------------------
 1406|      0|            goto cleanup;
 1407|      0|        }
 1408|  3.07k|        if (io->read(ctx->page, ctx->page_size, io->io_ctx) < ctx->page_size) {
  ------------------
  |  Branch (1408:13): [True: 101, False: 2.97k]
  ------------------
 1409|    101|            retval = READSTAT_ERROR_READ;
 1410|    101|            goto cleanup;
 1411|    101|        }
 1412|       |
 1413|  2.97k|        if ((retval = sas7bdat_parse_page_pass2(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1413:13): [True: 1.56k, False: 1.41k]
  ------------------
 1414|  1.56k|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1414:17): [True: 0, False: 1.56k]
  |  Branch (1414:38): [True: 0, False: 0]
  ------------------
 1415|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1416|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1417|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1418|      0|                        i, pos - ctx->page_size, pos-1);
 1419|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1420|      0|            }
 1421|  1.56k|            goto cleanup;
 1422|  1.56k|        }
 1423|  1.41k|        if (ctx->row_limit > 0 && ctx->parsed_row_count == ctx->row_limit)
  ------------------
  |  Branch (1423:13): [True: 589, False: 825]
  |  Branch (1423:35): [True: 18, False: 571]
  ------------------
 1424|     18|            break;
 1425|  1.41k|    }
 1426|  1.99k|cleanup:
 1427|       |
 1428|  1.99k|    return retval;
 1429|  1.99k|}
readstat_sas7bdat_read.c:sas7bdat_parse_page_pass2:
 1156|  2.97k|static readstat_error_t sas7bdat_parse_page_pass2(const char *page, size_t page_size, sas7bdat_ctx_t *ctx) {
 1157|  2.97k|    uint16_t page_type;
 1158|       |
 1159|  2.97k|    readstat_error_t retval = READSTAT_OK;
 1160|       |
 1161|  2.97k|    page_type = sas_read2(&page[ctx->page_header_size-8], ctx->bswap);
 1162|       |
 1163|  2.97k|    const char *data = NULL;
 1164|       |
 1165|  2.97k|    if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  112|  2.97k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                  if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  109|  2.97k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1165:9): [True: 336, False: 2.64k]
  ------------------
 1166|    336|        ctx->page_row_count = sas_read2(&page[ctx->page_header_size-6], ctx->bswap);
 1167|    336|        data = &page[ctx->page_header_size];
 1168|  2.64k|    } else if (!(page_type & SAS_PAGE_TYPE_COMP)) {
  ------------------
  |  |  116|  2.64k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1168:16): [True: 2.34k, False: 294]
  ------------------
 1169|  2.34k|        uint16_t subheader_count = sas_read2(&page[ctx->page_header_size-4], ctx->bswap);
 1170|       |
 1171|  2.34k|        int i;
 1172|  2.34k|        const char *shp = &page[ctx->page_header_size];
 1173|  2.34k|        int lshp = ctx->subheader_pointer_size;
 1174|       |
 1175|  2.34k|        if (ctx->page_header_size + subheader_count*lshp > page_size) {
  ------------------
  |  Branch (1175:13): [True: 3, False: 2.34k]
  ------------------
 1176|      3|            retval = READSTAT_ERROR_PARSE;
 1177|      3|            goto cleanup;
 1178|      3|        }
 1179|       |
 1180|  38.0k|        for (i=0; i<subheader_count; i++) {
  ------------------
  |  Branch (1180:19): [True: 36.8k, False: 1.20k]
  ------------------
 1181|  36.8k|            subheader_pointer_t shp_info = { 0 };
 1182|  36.8k|            if ((retval = sas7bdat_parse_subheader_pointer(shp, page + page_size - shp, &shp_info, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1182:17): [True: 0, False: 36.8k]
  ------------------
 1183|      0|                goto cleanup;
 1184|      0|            }
 1185|  36.8k|            if (shp_info.len > 0 && shp_info.compression == SAS_COMPRESSION_REFERENCE) {
  ------------------
  |  |  127|  22.7k|#define SAS_COMPRESSION_REFERENCE          0x03
  ------------------
  |  Branch (1185:17): [True: 22.7k, False: 14.1k]
  |  Branch (1185:37): [True: 0, False: 22.7k]
  ------------------
 1186|      0|                uint64_t page_index = shp_info.offset - 1;
 1187|      0|                uint64_t subheader_index = shp_info.len - 1;
 1188|      0|                if ((retval = sas7bdat_parse_moved_row(page_index, subheader_index, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1188:21): [True: 0, False: 0]
  ------------------
 1189|      0|                    goto cleanup;
 1190|      0|                }
 1191|  36.8k|            } else if (shp_info.len > 0 && shp_info.compression != SAS_COMPRESSION_TRUNC) {
  ------------------
  |  |  125|  22.7k|#define SAS_COMPRESSION_TRUNC              0x01
  ------------------
  |  Branch (1191:24): [True: 22.7k, False: 14.1k]
  |  Branch (1191:44): [True: 18.1k, False: 4.57k]
  ------------------
 1192|  18.1k|                if ((retval = sas7bdat_validate_subheader_pointer(&shp_info, page_size, subheader_count, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1192:21): [True: 1, False: 18.1k]
  ------------------
 1193|      1|                    goto cleanup;
 1194|      1|                }
 1195|  18.1k|                if (shp_info.compression == SAS_COMPRESSION_NONE) {
  ------------------
  |  |  124|  18.1k|#define SAS_COMPRESSION_NONE               0x00
  ------------------
  |  Branch (1195:21): [True: 16.2k, False: 1.92k]
  ------------------
 1196|  16.2k|                    sas_subheader_type_t subheader_type = sas7bdat_parse_subheader_type(page + shp_info.offset, ctx);
 1197|  16.2k|                    if (shp_info.is_compressed_data && subheader_type == SAS_SUBHEADER_TYPE_DATA) {
  ------------------
  |  Branch (1197:25): [True: 12.6k, False: 3.62k]
  |  Branch (1197:56): [True: 937, False: 11.7k]
  ------------------
 1198|    937|                        if (shp_info.len != ctx->row_length) {
  ------------------
  |  Branch (1198:29): [True: 425, False: 512]
  ------------------
 1199|    425|                            retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
 1200|    425|                            goto cleanup;
 1201|    425|                        }
 1202|    512|                        if ((retval = sas7bdat_submit_columns_if_needed(ctx, 1)) != READSTAT_OK) {
  ------------------
  |  Branch (1202:29): [True: 8, False: 504]
  ------------------
 1203|      8|                            goto cleanup;
 1204|      8|                        }
 1205|    504|                        if ((retval = sas7bdat_parse_single_row(page + shp_info.offset, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1205:29): [True: 8, False: 496]
  ------------------
 1206|      8|                            goto cleanup;
 1207|      8|                        }
 1208|  15.3k|                    } else {
 1209|  15.3k|                        if (subheader_type != SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (1209:29): [True: 13.5k, False: 1.76k]
  ------------------
 1210|  13.5k|                            if ((retval = sas7bdat_parse_subheader(subheader_type, page + shp_info.offset, shp_info.len, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1210:33): [True: 440, False: 13.1k]
  ------------------
 1211|    440|                                goto cleanup;
 1212|    440|                            }
 1213|  13.5k|                        }
 1214|  15.3k|                    }
 1215|  16.2k|                } else if (shp_info.compression == SAS_COMPRESSION_ROW) {
  ------------------
  |  |  128|  1.92k|#define SAS_COMPRESSION_ROW                0x04
  ------------------
  |  Branch (1215:28): [True: 1.80k, False: 116]
  ------------------
 1216|  1.80k|                    if ((retval = sas7bdat_submit_columns_if_needed(ctx, 1)) != READSTAT_OK) {
  ------------------
  |  Branch (1216:25): [True: 21, False: 1.78k]
  ------------------
 1217|     21|                        goto cleanup;
 1218|     21|                    }
 1219|  1.78k|                    if ((retval = sas7bdat_parse_subheader_compressed(page + shp_info.offset, shp_info.len, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1219:25): [True: 232, False: 1.55k]
  ------------------
 1220|    232|                        goto cleanup;
 1221|    232|                    }
 1222|  1.78k|                } else if (shp_info.compression == SAS_COMPRESSION_DELETED_ROW) {
  ------------------
  |  |  129|    116|#define SAS_COMPRESSION_DELETED_ROW        0x05
  ------------------
  |  Branch (1222:28): [True: 116, False: 0]
  ------------------
 1223|    116|                    if ((retval = sas7bdat_register_deleted_row(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1223:25): [True: 2, False: 114]
  ------------------
 1224|      2|                        goto cleanup;
 1225|      2|                    }
 1226|    116|                } else if (sas7bdat_is_moved_row(shp_info.compression)) {
  ------------------
  |  Branch (1226:28): [True: 0, False: 0]
  ------------------
 1227|       |                    /* void */
 1228|      0|                } else {
 1229|      0|                    retval = READSTAT_ERROR_UNSUPPORTED_COMPRESSION;
 1230|      0|                    goto cleanup;
 1231|      0|                }
 1232|  18.1k|            }
 1233|       |
 1234|  35.7k|            shp += lshp;
 1235|  35.7k|        }
 1236|       |
 1237|  1.20k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_MIX) {
  ------------------
  |  |  112|  1.20k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_MIX) {
  ------------------
  |  |  110|  1.20k|#define SAS_PAGE_TYPE_MIX    0x0200
  ------------------
  |  Branch (1237:13): [True: 729, False: 478]
  ------------------
 1238|       |            /* HACK - this is supposed to obey 8-byte boundaries but
 1239|       |             * some files created by Stat/Transfer don't. So verify that the
 1240|       |             * padding is { 0, 0, 0, 0 } or { ' ', ' ', ' ', ' ' } (or that
 1241|       |             * the file is not from Stat/Transfer) before skipping it */
 1242|    729|            if ((shp-page)%8 == 4 && shp + 4 <= page + page_size &&
  ------------------
  |  Branch (1242:17): [True: 426, False: 303]
  |  Branch (1242:38): [True: 423, False: 3]
  ------------------
 1243|    423|                    (*(uint32_t *)shp == 0x00000000 ||
  ------------------
  |  Branch (1243:22): [True: 100, False: 323]
  ------------------
 1244|    323|                     *(uint32_t *)shp == 0x20202020 ||
  ------------------
  |  Branch (1244:22): [True: 51, False: 272]
  ------------------
 1245|    385|                     ctx->vendor != READSTAT_VENDOR_STAT_TRANSFER)) {
  ------------------
  |  Branch (1245:22): [True: 234, False: 38]
  ------------------
 1246|    385|                data = shp + 4;
 1247|    385|            } else {
 1248|    344|                data = shp;
 1249|    344|            }
 1250|    729|        }
 1251|  1.20k|    }
 1252|  1.83k|    if (data) {
  ------------------
  |  Branch (1252:9): [True: 1.06k, False: 772]
  ------------------
 1253|  1.06k|        if ((retval = sas7bdat_submit_columns_if_needed(ctx, 0)) != READSTAT_OK) {
  ------------------
  |  Branch (1253:13): [True: 120, False: 945]
  ------------------
 1254|    120|            goto cleanup;
 1255|    120|        }
 1256|    945|        if (ctx->handle.value) {
  ------------------
  |  Branch (1256:13): [True: 945, False: 0]
  ------------------
 1257|    945|            const unsigned char *deleted_row_bitmap = NULL;
 1258|    945|            if (page_type & SAS_PAGE_TYPE_DELETED_ROWS) {
  ------------------
  |  |  114|    945|#define SAS_PAGE_TYPE_DELETED_ROWS  0x0080
  ------------------
  |  Branch (1258:17): [True: 229, False: 716]
  ------------------
 1259|    229|                if ((retval = sas7bdat_parse_deleted_row_bitmap(page, data, page_size,
  ------------------
  |  Branch (1259:21): [True: 118, False: 111]
  ------------------
 1260|    229|                        &deleted_row_bitmap, ctx)) != READSTAT_OK) {
 1261|    118|                    goto cleanup;
 1262|    118|                }
 1263|    229|            }
 1264|    827|            retval = sas7bdat_parse_rows(data, page + page_size - data, deleted_row_bitmap, ctx);
 1265|    827|        }
 1266|    945|    } 
 1267|  2.97k|cleanup:
 1268|       |
 1269|  2.97k|    return retval;
 1270|  1.83k|}
readstat_sas7bdat_read.c:sas7bdat_parse_single_row:
  488|  42.1k|static readstat_error_t sas7bdat_parse_single_row(const char *data, sas7bdat_ctx_t *ctx) {
  489|  42.1k|    if (ctx->parsed_row_count == ctx->row_limit)
  ------------------
  |  Branch (489:9): [True: 379, False: 41.7k]
  ------------------
  490|    379|        return READSTAT_OK;
  491|  41.7k|    if (ctx->row_offset) {
  ------------------
  |  Branch (491:9): [True: 0, False: 41.7k]
  ------------------
  492|      0|        ctx->row_offset--;
  493|      0|        return READSTAT_OK;
  494|      0|    }
  495|       |
  496|  41.7k|    readstat_error_t retval = READSTAT_OK;
  497|  41.7k|    int j;
  498|  41.7k|    if (ctx->handle.value) {
  ------------------
  |  Branch (498:9): [True: 41.7k, False: 0]
  ------------------
  499|  41.7k|        ctx->scratch_buffer_len = 4*ctx->max_col_width+1;
  500|  41.7k|        ctx->scratch_buffer = readstat_realloc(ctx->scratch_buffer, ctx->scratch_buffer_len);
  501|  41.7k|        if (ctx->scratch_buffer == NULL) {
  ------------------
  |  Branch (501:13): [True: 4, False: 41.7k]
  ------------------
  502|      4|            retval = READSTAT_ERROR_MALLOC;
  503|      4|            goto cleanup;
  504|      4|        }
  505|       |
  506|  19.5M|        for (j=0; j<ctx->column_count; j++) {
  ------------------
  |  Branch (506:19): [True: 19.5M, False: 41.6k]
  ------------------
  507|  19.5M|            col_info_t *col_info = &ctx->col_info[j];
  508|  19.5M|            readstat_variable_t *variable = ctx->variables[j];
  509|  19.5M|            if (variable->skip)
  ------------------
  |  Branch (509:17): [True: 0, False: 19.5M]
  ------------------
  510|      0|                continue;
  511|       |
  512|  19.5M|            if (col_info->offset > ctx->row_length || col_info->offset + col_info->width > ctx->row_length) {
  ------------------
  |  Branch (512:17): [True: 94, False: 19.5M]
  |  Branch (512:55): [True: 23, False: 19.5M]
  ------------------
  513|    117|                retval = READSTAT_ERROR_PARSE;
  514|    117|                goto cleanup;
  515|    117|            }
  516|  19.5M|            retval = sas7bdat_handle_data_value(variable, col_info, &data[col_info->offset], ctx);
  517|  19.5M|            if (retval != READSTAT_OK) {
  ------------------
  |  Branch (517:17): [True: 15, False: 19.5M]
  ------------------
  518|     15|                goto cleanup;
  519|     15|            }
  520|  19.5M|        }
  521|  41.7k|    }
  522|  41.6k|    ctx->parsed_row_count++;
  523|       |
  524|  41.7k|cleanup:
  525|  41.7k|    return retval;
  526|  41.6k|}
readstat_sas7bdat_read.c:sas7bdat_handle_data_value:
  425|  19.5M|        col_info_t *col_info, const char *col_data, sas7bdat_ctx_t *ctx) {
  426|  19.5M|    readstat_error_t retval = READSTAT_OK;
  427|  19.5M|    int cb_retval = 0;
  428|  19.5M|    readstat_value_t value;
  429|  19.5M|    memset(&value, 0, sizeof(readstat_value_t));
  430|       |
  431|  19.5M|    value.type = col_info->type;
  432|       |
  433|  19.5M|    if (col_info->type == READSTAT_TYPE_STRING) {
  ------------------
  |  Branch (433:9): [True: 19.3M, False: 134k]
  ------------------
  434|  19.3M|        retval = readstat_convert(ctx->scratch_buffer, ctx->scratch_buffer_len,
  435|  19.3M|                col_data, col_info->width, ctx->converter);
  436|  19.3M|        if (retval != READSTAT_OK) {
  ------------------
  |  Branch (436:13): [True: 13, False: 19.3M]
  ------------------
  437|     13|            if (ctx->handle.error) {
  ------------------
  |  Branch (437:17): [True: 0, False: 13]
  ------------------
  438|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf),
  439|      0|                        "ReadStat: Error converting string (row=%u, col=%u) to specified encoding: %.*s",
  440|      0|                        ctx->parsed_row_count+1, col_info->index+1, col_info->width, col_data);
  441|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  442|      0|            }
  443|     13|            goto cleanup;
  444|     13|        }
  445|       |
  446|  19.3M|        value.v.string_value = ctx->scratch_buffer;
  447|  19.3M|    } else if (col_info->type == READSTAT_TYPE_DOUBLE) {
  ------------------
  |  Branch (447:16): [True: 134k, False: 0]
  ------------------
  448|   134k|        uint64_t  val = 0;
  449|   134k|        double dval = NAN;
  450|   134k|        if (col_info->width < 1 || col_info->width > 8) {
  ------------------
  |  Branch (450:13): [True: 1, False: 134k]
  |  Branch (450:36): [True: 1, False: 134k]
  ------------------
  451|       |            /* A DOUBLE is 3-8 bytes on disk, but col_info can be mutated by a
  452|       |             * COLUMN_ATTRS subheader after the columns were validated. Guard
  453|       |             * here so a corrupt width can't produce an undefined shift below. */
  454|      2|            retval = READSTAT_ERROR_PARSE;
  455|      2|            goto cleanup;
  456|      2|        }
  457|   134k|        if (ctx->little_endian) {
  ------------------
  |  Branch (457:13): [True: 134k, False: 0]
  ------------------
  458|   134k|            int k;
  459|   922k|            for (k=0; k<col_info->width; k++) {
  ------------------
  |  Branch (459:23): [True: 787k, False: 134k]
  ------------------
  460|   787k|                val = (val << 8) | (unsigned char)col_data[col_info->width-1-k];
  461|   787k|            }
  462|   134k|        } else {
  463|      0|            int k;
  464|      0|            for (k=0; k<col_info->width; k++) {
  ------------------
  |  Branch (464:23): [True: 0, False: 0]
  ------------------
  465|      0|                val = (val << 8) | (unsigned char)col_data[k];
  466|      0|            }
  467|      0|        }
  468|   134k|        val <<= (8-col_info->width)*8;
  469|       |
  470|   134k|        memcpy(&dval, &val, 8);
  471|       |
  472|   134k|        if (isnan(dval)) {
  ------------------
  |  Branch (472:13): [True: 8.56k, False: 126k]
  ------------------
  473|  8.56k|            value.v.double_value = NAN;
  474|  8.56k|            sas_assign_tag(&value, ~((val >> 40) & 0xFF));
  475|   126k|        } else {
  476|   126k|            value.v.double_value = dval;
  477|   126k|        }
  478|   134k|    }
  479|  19.5M|    cb_retval = ctx->handle.value(ctx->parsed_row_count, variable, value, ctx->user_ctx);
  480|       |
  481|  19.5M|    if (cb_retval != READSTAT_HANDLER_OK)
  ------------------
  |  Branch (481:9): [True: 0, False: 19.5M]
  ------------------
  482|      0|        retval = READSTAT_ERROR_USER_ABORT;
  483|       |
  484|  19.5M|cleanup:
  485|  19.5M|    return retval;
  486|  19.5M|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_compressed:
  674|  1.78k|static readstat_error_t sas7bdat_parse_subheader_compressed(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  675|  1.78k|    if (ctx->rdc_compression)
  ------------------
  |  Branch (675:9): [True: 408, False: 1.37k]
  ------------------
  676|    408|        return sas7bdat_parse_subheader_rdc(subheader, len, ctx);
  677|       |
  678|  1.37k|    return sas7bdat_parse_subheader_rle(subheader, len, ctx);
  679|  1.78k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_rdc:
  560|    408|static readstat_error_t sas7bdat_parse_subheader_rdc(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  561|    408|    readstat_error_t retval = READSTAT_OK;
  562|    408|    const unsigned char *input = (const unsigned char *)subheader;
  563|    408|    char *buffer = malloc(ctx->row_length);
  564|    408|    if (buffer == NULL) return READSTAT_ERROR_MALLOC;
  ------------------
  |  Branch (564:9): [True: 0, False: 408]
  ------------------
  565|    408|    char *output = buffer;
  566|  1.63k|    while (input + 2 <= (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (566:12): [True: 1.26k, False: 367]
  ------------------
  567|  1.26k|        int i;
  568|  1.26k|        unsigned short prefix = (input[0] << 8) + input[1];
  569|  1.26k|        input += 2;
  570|  16.7k|        for (i=0; i<16; i++) {
  ------------------
  |  Branch (570:19): [True: 15.9k, False: 857]
  ------------------
  571|  15.9k|            if ((prefix & (1 << (15 - i))) == 0) {
  ------------------
  |  Branch (571:17): [True: 12.5k, False: 3.34k]
  ------------------
  572|  12.5k|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (572:21): [True: 365, False: 12.2k]
  ------------------
  573|    365|                    break;
  574|    365|                }
  575|  12.2k|                if (output + 1 > buffer + ctx->row_length) {
  ------------------
  |  Branch (575:21): [True: 4, False: 12.1k]
  ------------------
  576|      4|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  577|      4|                    goto cleanup;
  578|      4|                }
  579|  12.1k|                *output++ = *input++;
  580|  12.1k|                continue;
  581|  12.2k|            }
  582|       |
  583|  3.34k|            if (input + 2 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (583:17): [True: 1, False: 3.34k]
  ------------------
  584|      1|                retval = READSTAT_ERROR_PARSE;
  585|      1|                goto cleanup;
  586|      1|            }
  587|       |
  588|  3.34k|            unsigned char marker_byte = *input++;
  589|  3.34k|            unsigned char next_byte = *input++;
  590|  3.34k|            size_t insert_len = 0, copy_len = 0;
  591|  3.34k|            unsigned char insert_byte = 0x00;
  592|  3.34k|            size_t back_offset = 0;
  593|       |
  594|  3.34k|            if (marker_byte <= 0x0F) {
  ------------------
  |  Branch (594:17): [True: 1.54k, False: 1.80k]
  ------------------
  595|  1.54k|                insert_len = 3 + marker_byte;
  596|  1.54k|                insert_byte = next_byte;
  597|  1.80k|            } else if ((marker_byte >> 4) == 1) {
  ------------------
  |  Branch (597:24): [True: 534, False: 1.26k]
  ------------------
  598|    534|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (598:21): [True: 2, False: 532]
  ------------------
  599|      2|                    retval = READSTAT_ERROR_PARSE;
  600|      2|                    goto cleanup;
  601|      2|                }
  602|    532|                insert_len = 19 + (marker_byte & 0x0F) + next_byte * 16;
  603|    532|                insert_byte = *input++;
  604|  1.26k|            } else if ((marker_byte >> 4) == 2) {
  ------------------
  |  Branch (604:24): [True: 330, False: 938]
  ------------------
  605|    330|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (605:21): [True: 1, False: 329]
  ------------------
  606|      1|                    retval = READSTAT_ERROR_PARSE;
  607|      1|                    goto cleanup;
  608|      1|                }
  609|    329|                copy_len = 16 + (*input++);
  610|    329|                back_offset = 3 + (marker_byte & 0x0F) + next_byte * 16;
  611|    938|            } else {
  612|    938|                copy_len = (marker_byte >> 4);
  613|    938|                back_offset = 3 + (marker_byte & 0x0F) + next_byte * 16;
  614|    938|            }
  615|       |
  616|  3.34k|            if (insert_len) {
  ------------------
  |  Branch (616:17): [True: 2.07k, False: 1.26k]
  ------------------
  617|  2.07k|                if (output + insert_len > buffer + ctx->row_length) {
  ------------------
  |  Branch (617:21): [True: 2, False: 2.07k]
  ------------------
  618|      2|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  619|      2|                    goto cleanup;
  620|      2|                }
  621|  2.07k|                memset(output, insert_byte, insert_len);
  622|  2.07k|                output += insert_len;
  623|  2.07k|            } else if (copy_len) {
  ------------------
  |  Branch (623:24): [True: 1.26k, False: 0]
  ------------------
  624|  1.26k|                if (output - buffer < back_offset || copy_len > back_offset) {
  ------------------
  |  Branch (624:21): [True: 11, False: 1.25k]
  |  Branch (624:54): [True: 18, False: 1.23k]
  ------------------
  625|     29|                    retval = READSTAT_ERROR_PARSE;
  626|     29|                    goto cleanup;
  627|     29|                }
  628|  1.23k|                if (output + copy_len > buffer + ctx->row_length) {
  ------------------
  |  Branch (628:21): [True: 2, False: 1.23k]
  ------------------
  629|      2|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  630|      2|                    goto cleanup;
  631|      2|                }
  632|  1.23k|                memcpy(output, output - back_offset, copy_len);
  633|  1.23k|                output += copy_len;
  634|  1.23k|            }
  635|  3.34k|        }
  636|  1.26k|    }
  637|       |
  638|    367|    if (output - buffer != ctx->row_length) {
  ------------------
  |  Branch (638:9): [True: 6, False: 361]
  ------------------
  639|      6|        retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  640|      6|        goto cleanup;
  641|      6|    }
  642|    361|    retval = sas7bdat_parse_single_row(buffer, ctx);
  643|    408|cleanup:
  644|    408|    free(buffer);
  645|       |
  646|    408|    return retval;
  647|    361|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_rle:
  649|  1.37k|static readstat_error_t sas7bdat_parse_subheader_rle(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  650|  1.37k|    if (ctx->row_limit == ctx->parsed_row_count)
  ------------------
  |  Branch (650:9): [True: 745, False: 630]
  ------------------
  651|    745|        return READSTAT_OK;
  652|       |
  653|    630|    readstat_error_t retval = READSTAT_OK;
  654|    630|    ssize_t bytes_decompressed = 0;
  655|       |
  656|    630|    bytes_decompressed = sas_rle_decompress(ctx->row, ctx->row_length, subheader, len);
  657|       |
  658|    630|    if (bytes_decompressed != ctx->row_length) {
  ------------------
  |  Branch (658:9): [True: 143, False: 487]
  ------------------
  659|    143|        retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  660|    143|        if (ctx->handle.error) {
  ------------------
  |  Branch (660:13): [True: 0, False: 143]
  ------------------
  661|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
  662|      0|                    "ReadStat: Row #%d decompressed to %ld bytes (expected %d bytes)",
  663|      0|                    ctx->parsed_row_count, (long)(bytes_decompressed), ctx->row_length);
  664|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  665|      0|        }
  666|    143|        goto cleanup;
  667|    143|    }
  668|    487|    retval = sas7bdat_parse_single_row(ctx->row, ctx);
  669|       |
  670|    630|cleanup:
  671|    630|    return retval;
  672|    487|}
readstat_sas7bdat_read.c:sas7bdat_register_deleted_row:
  416|    898|static readstat_error_t sas7bdat_register_deleted_row(sas7bdat_ctx_t *ctx) {
  417|    898|    if (ctx->parsed_deleted_row_count >= ctx->deleted_row_count) {
  ------------------
  |  Branch (417:9): [True: 3, False: 895]
  ------------------
  418|      3|        return READSTAT_ERROR_PARSE;
  419|      3|    }
  420|    895|    ctx->parsed_deleted_row_count++;
  421|    895|    return READSTAT_OK;
  422|    898|}
readstat_sas7bdat_read.c:sas7bdat_parse_deleted_row_bitmap:
 1027|    229|        size_t page_size, const unsigned char **deleted_row_bitmap, sas7bdat_ctx_t *ctx) {
 1028|    229|    uint64_t page_unused_bytes;
 1029|    229|    if (ctx->u64) {
  ------------------
  |  Branch (1029:9): [True: 113, False: 116]
  ------------------
 1030|    113|        page_unused_bytes = sas_read8(&page[24], ctx->bswap);
 1031|    116|    } else {
 1032|    116|        page_unused_bytes = sas_read4(&page[12], ctx->bswap);
 1033|    116|    }
 1034|    229|    uint32_t row_count = ctx->page_row_count < ctx->total_row_count ? ctx->page_row_count : ctx->total_row_count;
  ------------------
  |  Branch (1034:26): [True: 125, False: 104]
  ------------------
 1035|    229|    uint64_t deleted_row_bitmap_offset = (uint64_t)row_count * ctx->row_length + page_unused_bytes;
 1036|    229|    uint32_t required_bytes = row_count / CHAR_BIT + (row_count % CHAR_BIT == 0 ? 0 : 1);
  ------------------
  |  Branch (1036:55): [True: 153, False: 76]
  ------------------
 1037|       |
 1038|    229|    if ((data - page) + deleted_row_bitmap_offset + required_bytes > page_size) {
  ------------------
  |  Branch (1038:9): [True: 118, False: 111]
  ------------------
 1039|    118|        return READSTAT_ERROR_PARSE;
 1040|    118|    }
 1041|    111|    *deleted_row_bitmap = (const unsigned char *)data + deleted_row_bitmap_offset;
 1042|    111|    return READSTAT_OK;
 1043|    229|}
readstat_sas7bdat_read.c:sas7bdat_parse_rows:
  536|    827|        const unsigned char *deleted_bitmap, sas7bdat_ctx_t *ctx) {
  537|    827|    readstat_error_t retval = READSTAT_OK;
  538|    827|    int i;
  539|    827|    size_t row_offset=0;
  540|  42.3k|    for (i=0; i<ctx->page_row_count && ctx->parsed_row_count < ctx->row_limit; i++) {
  ------------------
  |  Branch (540:15): [True: 41.9k, False: 444]
  |  Branch (540:40): [True: 41.7k, False: 198]
  ------------------
  541|  41.7k|        if (row_offset + ctx->row_length > len) {
  ------------------
  |  Branch (541:13): [True: 98, False: 41.6k]
  ------------------
  542|     98|            retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  543|     98|            goto cleanup;
  544|     98|        }
  545|  41.6k|        if (deleted_bitmap != NULL && sas7bdat_read_bitmap(deleted_bitmap, i)) {
  ------------------
  |  Branch (545:13): [True: 2.59k, False: 39.0k]
  |  Branch (545:39): [True: 782, False: 1.81k]
  ------------------
  546|    782|            if ((retval = sas7bdat_register_deleted_row(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (546:17): [True: 1, False: 781]
  ------------------
  547|      1|                goto cleanup;
  548|      1|            }
  549|  40.8k|        } else if ((retval = sas7bdat_parse_single_row(&data[row_offset], ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (549:20): [True: 86, False: 40.7k]
  ------------------
  550|     86|            goto cleanup;
  551|     86|        }
  552|       |
  553|  41.5k|        row_offset += ctx->row_length;
  554|  41.5k|    }
  555|       |
  556|    827|cleanup:
  557|    827|    return retval;
  558|    827|}
readstat_sas7bdat_read.c:sas7bdat_read_bitmap:
  528|  2.59k|static unsigned char sas7bdat_read_bitmap(const unsigned char *bitmap, int index) {
  529|  2.59k|    unsigned char current_byte = bitmap[index / CHAR_BIT];
  530|  2.59k|    unsigned char mask = 1 << (CHAR_BIT - 1 - index % CHAR_BIT);
  531|       |
  532|  2.59k|    return current_byte & mask;
  533|  2.59k|}
readstat_sas7bdat_read.c:sas7bdat_submit_columns_if_needed:
  851|  3.71k|static readstat_error_t sas7bdat_submit_columns_if_needed(sas7bdat_ctx_t *ctx, int compressed) {
  852|  3.71k|    readstat_error_t retval = READSTAT_OK;
  853|  3.71k|    if (!ctx->did_submit_columns) {
  ------------------
  |  Branch (853:9): [True: 1.13k, False: 2.58k]
  ------------------
  854|  1.13k|        if ((retval = sas7bdat_submit_columns(ctx, compressed)) != READSTAT_OK) {
  ------------------
  |  Branch (854:13): [True: 225, False: 909]
  ------------------
  855|    225|            goto cleanup;
  856|    225|        }
  857|    909|        ctx->did_submit_columns = 1;
  858|    909|    }
  859|  3.71k|cleanup:
  860|  3.71k|    return retval;
  861|  3.71k|}
readstat_sas7bdat_read.c:sas7bdat_submit_columns:
  790|  1.13k|static readstat_error_t sas7bdat_submit_columns(sas7bdat_ctx_t *ctx, int compressed) {
  791|  1.13k|    readstat_error_t retval = READSTAT_OK;
  792|  1.13k|    if (ctx->handle.metadata) {
  ------------------
  |  Branch (792:9): [True: 1.13k, False: 0]
  ------------------
  793|  1.13k|        readstat_metadata_t metadata = {
  794|  1.13k|            .row_count = ctx->row_limit,
  795|  1.13k|            .var_count = ctx->column_count,
  796|  1.13k|            .table_name = ctx->table_name,
  797|  1.13k|            .file_label = ctx->file_label,
  798|  1.13k|            .file_encoding = ctx->input_encoding, /* orig encoding? */
  799|  1.13k|            .creation_time = ctx->ctime,
  800|  1.13k|            .modified_time = ctx->mtime,
  801|  1.13k|            .file_format_version = ctx->version,
  802|  1.13k|            .compression = READSTAT_COMPRESS_NONE,
  803|  1.13k|            .endianness = ctx->little_endian ? READSTAT_ENDIAN_LITTLE : READSTAT_ENDIAN_BIG,
  ------------------
  |  Branch (803:27): [True: 280, False: 854]
  ------------------
  804|  1.13k|            .is64bit = ctx->u64
  805|  1.13k|        };
  806|  1.13k|        if (compressed) {
  ------------------
  |  Branch (806:13): [True: 473, False: 661]
  ------------------
  807|    473|            if (ctx->rdc_compression) {
  ------------------
  |  Branch (807:17): [True: 44, False: 429]
  ------------------
  808|     44|                metadata.compression = READSTAT_COMPRESS_BINARY;
  809|    429|            } else {
  810|    429|                metadata.compression = READSTAT_COMPRESS_ROWS;
  811|    429|            }
  812|    473|        }
  813|  1.13k|        if (ctx->handle.metadata(&metadata, ctx->user_ctx) != READSTAT_HANDLER_OK) {
  ------------------
  |  Branch (813:13): [True: 0, False: 1.13k]
  ------------------
  814|      0|            retval = READSTAT_ERROR_USER_ABORT;
  815|      0|            goto cleanup;
  816|      0|        }
  817|  1.13k|    }
  818|  1.13k|    if (ctx->column_count == 0)
  ------------------
  |  Branch (818:9): [True: 670, False: 464]
  ------------------
  819|    670|        goto cleanup;
  820|       |
  821|    464|    if ((ctx->variables = readstat_calloc(ctx->column_count, sizeof(readstat_variable_t *))) == NULL) {
  ------------------
  |  Branch (821:9): [True: 0, False: 464]
  ------------------
  822|      0|        retval = READSTAT_ERROR_MALLOC;
  823|      0|        goto cleanup;
  824|      0|    }
  825|    464|    int i;
  826|    464|    int index_after_skipping = 0;
  827|  3.05M|    for (i=0; i<ctx->column_count; i++) {
  ------------------
  |  Branch (827:15): [True: 3.05M, False: 239]
  ------------------
  828|  3.05M|        ctx->variables[i] = sas7bdat_init_variable(ctx, i, index_after_skipping, &retval);
  829|  3.05M|        if (ctx->variables[i] == NULL)
  ------------------
  |  Branch (829:13): [True: 225, False: 3.05M]
  ------------------
  830|    225|            break;
  831|       |
  832|  3.05M|        int cb_retval = READSTAT_HANDLER_OK;
  833|  3.05M|        if (ctx->handle.variable) {
  ------------------
  |  Branch (833:13): [True: 3.05M, False: 0]
  ------------------
  834|  3.05M|            cb_retval = ctx->handle.variable(i, ctx->variables[i], ctx->variables[i]->format, ctx->user_ctx);
  835|  3.05M|        }
  836|  3.05M|        if (cb_retval == READSTAT_HANDLER_ABORT) {
  ------------------
  |  Branch (836:13): [True: 0, False: 3.05M]
  ------------------
  837|      0|            retval = READSTAT_ERROR_USER_ABORT;
  838|      0|            goto cleanup;
  839|      0|        }
  840|       |
  841|  3.05M|        if (cb_retval == READSTAT_HANDLER_SKIP_VARIABLE) {
  ------------------
  |  Branch (841:13): [True: 0, False: 3.05M]
  ------------------
  842|      0|            ctx->variables[i]->skip = 1;
  843|  3.05M|        } else {
  844|  3.05M|            index_after_skipping++;
  845|  3.05M|        }
  846|  3.05M|    }
  847|  1.13k|cleanup:
  848|  1.13k|    return retval;
  849|    464|}
readstat_sas7bdat_read.c:sas7bdat_init_variable:
  731|  3.05M|        int index_after_skipping, readstat_error_t *out_retval) {
  732|  3.05M|    readstat_error_t retval = READSTAT_OK;
  733|  3.05M|    readstat_variable_t *variable = readstat_calloc(1, sizeof(readstat_variable_t));
  734|       |
  735|  3.05M|    variable->index = i;
  736|  3.05M|    variable->index_after_skipping = index_after_skipping;
  737|  3.05M|    variable->type = ctx->col_info[i].type;
  738|  3.05M|    variable->storage_width = ctx->col_info[i].width;
  739|       |
  740|  3.05M|    if ((retval = sas7bdat_validate_column(&ctx->col_info[i])) != READSTAT_OK) {
  ------------------
  |  Branch (740:9): [True: 73, False: 3.05M]
  ------------------
  741|     73|        goto cleanup;
  742|     73|    }
  743|  3.05M|    if ((retval = sas7bdat_copy_text_ref(variable->name, sizeof(variable->name), 
  ------------------
  |  Branch (743:9): [True: 48, False: 3.05M]
  ------------------
  744|  3.05M|                    ctx->col_info[i].name_ref, ctx)) != READSTAT_OK) {
  745|     48|        goto cleanup;
  746|     48|    }
  747|  3.05M|    if ((retval = sas7bdat_copy_text_ref(variable->format, sizeof(variable->format),
  ------------------
  |  Branch (747:9): [True: 50, False: 3.05M]
  ------------------
  748|  3.05M|                    ctx->col_info[i].format_ref, ctx)) != READSTAT_OK) {
  749|     50|        goto cleanup;
  750|     50|    }
  751|  3.05M|    size_t len = strlen(variable->format);
  752|  3.05M|    if (ctx->col_info[i].format_width) {
  ------------------
  |  Branch (752:9): [True: 617, False: 3.05M]
  ------------------
  753|    617|        len += snprintf(variable->format + len, sizeof(variable->format) - len,
  754|    617|                "%d", ctx->col_info[i].format_width);
  755|    617|    }
  756|  3.05M|    if (len && ctx->col_info[i].format_digits) {
  ------------------
  |  Branch (756:9): [True: 652, False: 3.05M]
  |  Branch (756:16): [True: 445, False: 207]
  ------------------
  757|    445|        len += snprintf(variable->format + len, sizeof(variable->format) - len,
  758|    445|                ".%d", ctx->col_info[i].format_digits);
  759|    445|    }
  760|  3.05M|    if (len) { // TODO where is the informat saved?
  ------------------
  |  Branch (760:9): [True: 652, False: 3.05M]
  ------------------
  761|    652|        readstat_variable_set_informat(variable, variable->format);
  762|    652|    }
  763|  3.05M|    if ((retval = sas7bdat_copy_text_ref(variable->label, sizeof(variable->label), 
  ------------------
  |  Branch (763:9): [True: 54, False: 3.05M]
  ------------------
  764|  3.05M|                    ctx->col_info[i].label_ref, ctx)) != READSTAT_OK) {
  765|     54|        goto cleanup;
  766|     54|    }
  767|       |
  768|  3.05M|cleanup:
  769|  3.05M|    if (retval != READSTAT_OK) {
  ------------------
  |  Branch (769:9): [True: 225, False: 3.05M]
  ------------------
  770|    225|        if (out_retval)
  ------------------
  |  Branch (770:13): [True: 225, False: 0]
  ------------------
  771|    225|            *out_retval = retval;
  772|       |
  773|    225|        if (retval == READSTAT_ERROR_CONVERT_BAD_STRING) {
  ------------------
  |  Branch (773:13): [True: 9, False: 216]
  ------------------
  774|      9|            if (ctx->handle.error) {
  ------------------
  |  Branch (774:17): [True: 0, False: 9]
  ------------------
  775|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf),
  776|      0|                        "ReadStat: Error converting variable #%d info to specified encoding: %s %s (%s)",
  777|      0|                        i, variable->name, variable->format, variable->label);
  778|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  779|      0|            }
  780|      9|        }
  781|       |
  782|    225|        free(variable);
  783|       |
  784|    225|        return NULL;
  785|    225|    }
  786|       |
  787|  3.05M|    return variable;
  788|  3.05M|}
readstat_sas7bdat_read.c:sas7bdat_validate_column:
  716|  3.05M|static readstat_error_t sas7bdat_validate_column(col_info_t *col_info) {
  717|  3.05M|    if (col_info->type == READSTAT_TYPE_DOUBLE) {
  ------------------
  |  Branch (717:9): [True: 1.12k, False: 3.05M]
  ------------------
  718|  1.12k|        if (col_info->width > 8 || col_info->width < 3) {
  ------------------
  |  Branch (718:13): [True: 45, False: 1.08k]
  |  Branch (718:36): [True: 2, False: 1.08k]
  ------------------
  719|     47|            return READSTAT_ERROR_PARSE;
  720|     47|        }
  721|  1.12k|    }
  722|  3.05M|    if (col_info->type == READSTAT_TYPE_STRING) {
  ------------------
  |  Branch (722:9): [True: 3.05M, False: 1.08k]
  ------------------
  723|  3.05M|        if (col_info->width > INT16_MAX) {
  ------------------
  |  Branch (723:13): [True: 26, False: 3.05M]
  ------------------
  724|     26|            return READSTAT_ERROR_PARSE;
  725|     26|        }
  726|  3.05M|    }
  727|  3.05M|    return READSTAT_OK;
  728|  3.05M|}
readstat_sas7bdat_read.c:sas7bdat_update_progress:
  138|  3.19k|static readstat_error_t sas7bdat_update_progress(sas7bdat_ctx_t *ctx) {
  139|  3.19k|    readstat_io_t *io = ctx->io;
  140|  3.19k|    return io->update(ctx->file_size, ctx->handle.progress, ctx->user_ctx, io->io_ctx);
  141|  3.19k|}
readstat_sas7bdat_read.c:sas7bdat_ctx_free:
  101|  3.67k|static void sas7bdat_ctx_free(sas7bdat_ctx_t *ctx) {
  102|  3.67k|    int i;
  103|  3.67k|    if (ctx->text_blobs) {
  ------------------
  |  Branch (103:9): [True: 867, False: 2.80k]
  ------------------
  104|  3.47k|        for (i=0; i<ctx->text_blob_count; i++) {
  ------------------
  |  Branch (104:19): [True: 2.61k, False: 867]
  ------------------
  105|  2.61k|            free(ctx->text_blobs[i]);
  106|  2.61k|        }
  107|    867|        free(ctx->text_blobs);
  108|    867|        free(ctx->text_blob_lengths);
  109|    867|    }
  110|  3.67k|    if (ctx->variables) {
  ------------------
  |  Branch (110:9): [True: 464, False: 3.21k]
  ------------------
  111|  15.6M|        for (i=0; i<ctx->column_count; i++) {
  ------------------
  |  Branch (111:19): [True: 15.6M, False: 464]
  ------------------
  112|  15.6M|            if (ctx->variables[i])
  ------------------
  |  Branch (112:17): [True: 3.05M, False: 12.5M]
  ------------------
  113|  3.05M|                free(ctx->variables[i]);
  114|  15.6M|        }
  115|    464|        free(ctx->variables);
  116|    464|    }
  117|  3.67k|    if (ctx->col_info)
  ------------------
  |  Branch (117:9): [True: 615, False: 3.06k]
  ------------------
  118|    615|        free(ctx->col_info);
  119|       |
  120|  3.67k|    if (ctx->scratch_buffer)
  ------------------
  |  Branch (120:9): [True: 352, False: 3.32k]
  ------------------
  121|    352|        free(ctx->scratch_buffer);
  122|       |
  123|  3.67k|    if (ctx->page)
  ------------------
  |  Branch (123:9): [True: 3.16k, False: 513]
  ------------------
  124|  3.16k|        free(ctx->page);
  125|       |
  126|  3.67k|    if (ctx->moved_page)
  ------------------
  |  Branch (126:9): [True: 0, False: 3.67k]
  ------------------
  127|      0|        free(ctx->moved_page);
  128|       |
  129|  3.67k|    if (ctx->row)
  ------------------
  |  Branch (129:9): [True: 791, False: 2.88k]
  ------------------
  130|    791|        free(ctx->row);
  131|       |
  132|  3.67k|    if (ctx->converter)
  ------------------
  |  Branch (132:9): [True: 2.10k, False: 1.56k]
  ------------------
  133|  2.10k|        iconv_close(ctx->converter);
  134|       |
  135|  3.67k|    free(ctx);
  136|  3.67k|}

sas_rle_decompress:
   47|    630|        const void *input_buf, size_t input_len) {
   48|    630|    unsigned char *buffer = (unsigned char *)output_buf;
   49|    630|    unsigned char *output = buffer;
   50|    630|    size_t output_written = 0;
   51|       |
   52|    630|    const unsigned char *input = (const unsigned char *)input_buf;
   53|       |
   54|  55.7k|    while (input < (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (54:12): [True: 55.2k, False: 507]
  ------------------
   55|  55.2k|        unsigned char control = *input++;
   56|  55.2k|        unsigned char command = (control & 0xF0) >> 4;
   57|  55.2k|        unsigned char length = (control & 0x0F);
   58|  55.2k|        int copy_len = 0;
   59|  55.2k|        int insert_len = 0;
   60|  55.2k|        unsigned char insert_byte = '\0';
   61|  55.2k|        if (input + command_lengths[command] > (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (61:13): [True: 4, False: 55.2k]
  ------------------
   62|      4|            return -1;
   63|      4|        }
   64|  55.2k|        switch (command) {
   65|  5.85k|            case SAS_RLE_COMMAND_COPY64:
  ------------------
  |  |   13|  5.85k|#define SAS_RLE_COMMAND_COPY64          0
  ------------------
  |  Branch (65:13): [True: 5.85k, False: 49.3k]
  ------------------
   66|  5.85k|                copy_len = (*input++) + 64 + length * 256;
   67|  5.85k|                break;
   68|    163|            case SAS_RLE_COMMAND_COPY64_PLUS_4096:
  ------------------
  |  |   14|    163|#define SAS_RLE_COMMAND_COPY64_PLUS_4096 1
  ------------------
  |  Branch (68:13): [True: 163, False: 55.0k]
  ------------------
   69|    163|                copy_len = (*input++) + 64 + length * 256 + 4096;
   70|    163|                break;
   71|    414|            case SAS_RLE_COMMAND_COPY96: copy_len = length + 96; break;
  ------------------
  |  |   15|    414|#define SAS_RLE_COMMAND_COPY96          2
  ------------------
  |  Branch (71:13): [True: 414, False: 54.8k]
  ------------------
   72|    659|            case SAS_RLE_COMMAND_INSERT_BYTE18:
  ------------------
  |  |   16|    659|#define SAS_RLE_COMMAND_INSERT_BYTE18   4
  ------------------
  |  Branch (72:13): [True: 659, False: 54.5k]
  ------------------
   73|    659|                insert_len = (*input++) + 18 + length * 256;
   74|    659|                insert_byte = *input++;
   75|    659|                break;
   76|  1.72k|            case SAS_RLE_COMMAND_INSERT_AT17:
  ------------------
  |  |   17|  1.72k|#define SAS_RLE_COMMAND_INSERT_AT17     5
  ------------------
  |  Branch (76:13): [True: 1.72k, False: 53.5k]
  ------------------
   77|  1.72k|                insert_len = (*input++) + 17 + length * 256;
   78|  1.72k|                insert_byte = '@';
   79|  1.72k|                break;
   80|  1.30k|            case SAS_RLE_COMMAND_INSERT_BLANK17:
  ------------------
  |  |   18|  1.30k|#define SAS_RLE_COMMAND_INSERT_BLANK17  6
  ------------------
  |  Branch (80:13): [True: 1.30k, False: 53.9k]
  ------------------
   81|  1.30k|                insert_len = (*input++) + 17 + length * 256;
   82|  1.30k|                insert_byte = ' ';
   83|  1.30k|                break;
   84|  3.28k|            case SAS_RLE_COMMAND_INSERT_ZERO17:
  ------------------
  |  |   19|  3.28k|#define SAS_RLE_COMMAND_INSERT_ZERO17   7
  ------------------
  |  Branch (84:13): [True: 3.28k, False: 51.9k]
  ------------------
   85|  3.28k|                insert_len = (*input++) + 17 + length * 256;
   86|  3.28k|                insert_byte = '\0';
   87|  3.28k|                break;
   88|  1.69k|            case SAS_RLE_COMMAND_COPY1:  copy_len = length + 1; break;
  ------------------
  |  |   20|  1.69k|#define SAS_RLE_COMMAND_COPY1           8
  ------------------
  |  Branch (88:13): [True: 1.69k, False: 53.5k]
  ------------------
   89|  13.3k|            case SAS_RLE_COMMAND_COPY17: copy_len = length + 17; break;
  ------------------
  |  |   21|  13.3k|#define SAS_RLE_COMMAND_COPY17          9
  ------------------
  |  Branch (89:13): [True: 13.3k, False: 41.8k]
  ------------------
   90|  1.11k|            case SAS_RLE_COMMAND_COPY33: copy_len = length + 33; break;
  ------------------
  |  |   22|  1.11k|#define SAS_RLE_COMMAND_COPY33         10
  ------------------
  |  Branch (90:13): [True: 1.11k, False: 54.1k]
  ------------------
   91|    440|            case SAS_RLE_COMMAND_COPY49: copy_len = length + 49; break;
  ------------------
  |  |   23|    440|#define SAS_RLE_COMMAND_COPY49         11
  ------------------
  |  Branch (91:13): [True: 440, False: 54.8k]
  ------------------
   92|  1.77k|            case SAS_RLE_COMMAND_INSERT_BYTE3:
  ------------------
  |  |   24|  1.77k|#define SAS_RLE_COMMAND_INSERT_BYTE3   12
  ------------------
  |  Branch (92:13): [True: 1.77k, False: 53.4k]
  ------------------
   93|  1.77k|                insert_byte = *input++;
   94|  1.77k|                insert_len = length + 3;
   95|  1.77k|                break;
   96|  1.81k|            case SAS_RLE_COMMAND_INSERT_AT2:
  ------------------
  |  |   25|  1.81k|#define SAS_RLE_COMMAND_INSERT_AT2     13
  ------------------
  |  Branch (96:13): [True: 1.81k, False: 53.4k]
  ------------------
   97|  1.81k|                insert_byte = '@';
   98|  1.81k|                insert_len = length + 2;
   99|  1.81k|                break;
  100|  10.5k|            case SAS_RLE_COMMAND_INSERT_BLANK2:
  ------------------
  |  |   26|  10.5k|#define SAS_RLE_COMMAND_INSERT_BLANK2  14
  ------------------
  |  Branch (100:13): [True: 10.5k, False: 44.6k]
  ------------------
  101|  10.5k|                insert_byte = ' ';
  102|  10.5k|                insert_len = length + 2;
  103|  10.5k|                break;
  104|  7.92k|            case SAS_RLE_COMMAND_INSERT_ZERO2:
  ------------------
  |  |   27|  7.92k|#define SAS_RLE_COMMAND_INSERT_ZERO2   15
  ------------------
  |  Branch (104:13): [True: 7.92k, False: 47.3k]
  ------------------
  105|  7.92k|                insert_byte = '\0';
  106|  7.92k|                insert_len = length + 2;
  107|  7.92k|                break;
  108|  3.15k|            default:
  ------------------
  |  Branch (108:13): [True: 3.15k, False: 52.0k]
  ------------------
  109|       |                /* error out here? */
  110|  3.15k|                break;
  111|  55.2k|        }
  112|  55.2k|        if (copy_len) {
  ------------------
  |  Branch (112:13): [True: 23.0k, False: 32.2k]
  ------------------
  113|  23.0k|            if (output_written + copy_len > output_len) {
  ------------------
  |  Branch (113:17): [True: 17, False: 23.0k]
  ------------------
  114|     17|                return -1;
  115|     17|            }
  116|  23.0k|            if (input + copy_len > (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (116:17): [True: 83, False: 22.9k]
  ------------------
  117|     83|                return -1;
  118|     83|            }
  119|  22.9k|            if (output) {
  ------------------
  |  Branch (119:17): [True: 22.9k, False: 0]
  ------------------
  120|  22.9k|                memcpy(&output[output_written], input, copy_len);
  121|  22.9k|            }
  122|  22.9k|            input += copy_len;
  123|  22.9k|            output_written += copy_len;
  124|  22.9k|        }
  125|  55.1k|        if (insert_len) {
  ------------------
  |  Branch (125:13): [True: 29.0k, False: 26.0k]
  ------------------
  126|  29.0k|            if (output_written + insert_len > output_len) {
  ------------------
  |  Branch (126:17): [True: 19, False: 29.0k]
  ------------------
  127|     19|                return -1;
  128|     19|            }
  129|  29.0k|            if (output) {
  ------------------
  |  Branch (129:17): [True: 29.0k, False: 0]
  ------------------
  130|  29.0k|                memset(&output[output_written], insert_byte, insert_len);
  131|  29.0k|            }
  132|  29.0k|            output_written += insert_len;
  133|  29.0k|        }
  134|  55.1k|    }
  135|       |
  136|    507|    return output_written;
  137|    630|}

rt_open_handler:
    8|  3.67k|int rt_open_handler(const char *path, void *io_ctx) {
    9|  3.67k|    return 0;
   10|  3.67k|}
rt_close_handler:
   12|  3.67k|int rt_close_handler(void *io_ctx) {
   13|  3.67k|    return 0;
   14|  3.67k|}
rt_seek_handler:
   17|  30.1k|        readstat_io_flags_t whence, void *io_ctx) {
   18|  30.1k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   19|  30.1k|    readstat_off_t newpos = -1;
   20|  30.1k|    if (whence == READSTAT_SEEK_SET) {
  ------------------
  |  Branch (20:9): [True: 19.6k, False: 10.5k]
  ------------------
   21|  19.6k|        newpos = offset;
   22|  19.6k|    } else if (whence == READSTAT_SEEK_CUR) {
  ------------------
  |  Branch (22:16): [True: 6.85k, False: 3.67k]
  ------------------
   23|  6.85k|        newpos = buffer_ctx->pos + offset;
   24|  6.85k|    } else if (whence == READSTAT_SEEK_END) {
  ------------------
  |  Branch (24:16): [True: 3.67k, False: 0]
  ------------------
   25|  3.67k|        newpos = buffer_ctx->buffer->used + offset;
   26|  3.67k|    }
   27|       |
   28|  30.1k|    if (newpos < 0)
  ------------------
  |  Branch (28:9): [True: 128, False: 30.0k]
  ------------------
   29|    128|        return -1;
   30|       |
   31|  30.0k|    if (newpos > buffer_ctx->buffer->used)
  ------------------
  |  Branch (31:9): [True: 235, False: 29.7k]
  ------------------
   32|    235|        return -1;
   33|       |
   34|  29.7k|    buffer_ctx->pos = newpos;
   35|  29.7k|    return newpos;
   36|  30.0k|}
rt_read_handler:
   38|  51.1k|ssize_t rt_read_handler(void *buf, size_t nbytes, void *io_ctx) {
   39|  51.1k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   40|  51.1k|    ssize_t bytes_copied = 0;
   41|  51.1k|    ssize_t bytes_left = buffer_ctx->buffer->used - buffer_ctx->pos;
   42|  51.1k|    if (nbytes <= bytes_left) {
  ------------------
  |  Branch (42:9): [True: 50.7k, False: 419]
  ------------------
   43|  50.7k|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, nbytes);
   44|  50.7k|        bytes_copied = nbytes;
   45|  50.7k|    } else if (bytes_left > 0) {
  ------------------
  |  Branch (45:16): [True: 218, False: 201]
  ------------------
   46|    218|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, bytes_left);
   47|    218|        bytes_copied = bytes_left;
   48|    218|    }
   49|  51.1k|    buffer_ctx->pos += bytes_copied;
   50|  51.1k|    return bytes_copied;
   51|  51.1k|}
rt_update_handler:
   54|  3.19k|        void *user_ctx, void *io_ctx) {
   55|  3.19k|    if (!progress_handler)
  ------------------
  |  Branch (55:9): [True: 3.19k, False: 0]
  ------------------
   56|  3.19k|        return READSTAT_OK;
   57|       |
   58|      0|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   59|       |
   60|      0|    if (progress_handler(1.0 * buffer_ctx->pos / buffer_ctx->buffer->used, user_ctx))
  ------------------
  |  Branch (60:9): [True: 0, False: 0]
  ------------------
   61|      0|        return READSTAT_ERROR_USER_ABORT;
   62|       |
   63|      0|    return READSTAT_OK;
   64|      0|}

