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

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

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

readstat_convert:
   39|  21.3M|readstat_error_t readstat_convert(char *dst, size_t dst_len, const char *src, size_t src_len, iconv_t converter) {
   40|  21.3M|    return readstat_convert_impl(dst, dst_len, src, src_len, converter, 1);
   41|  21.3M|}
readstat_convert.c:readstat_convert_impl:
    8|  21.3M|        iconv_t converter, int trim_spaces) {
    9|       |    /* strip off spaces from the input because the programs use ASCII space
   10|       |     * padding even with non-ASCII encoding. */
   11|  21.3M|    while (src_len && ((trim_spaces && src[src_len-1] == ' ') || src[src_len-1] == '\0')) {
  ------------------
  |  Branch (11:12): [True: 65.8k, False: 21.3M]
  |  Branch (11:25): [True: 65.8k, False: 0]
  |  Branch (11:40): [True: 4.42k, False: 61.4k]
  |  Branch (11:66): [True: 40.0k, False: 21.3k]
  ------------------
   12|  44.5k|        src_len--;
   13|  44.5k|    }
   14|  21.3M|    if (dst_len == 0) {
  ------------------
  |  Branch (14:9): [True: 0, False: 21.3M]
  ------------------
   15|      0|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   16|  21.3M|    } else if (converter) {
  ------------------
  |  Branch (16:16): [True: 864k, False: 20.4M]
  ------------------
   17|   864k|        size_t dst_left = dst_len - 1;
   18|   864k|        char *dst_end = dst;
   19|   864k|        size_t status = iconv(converter, (readstat_iconv_inbuf_t)&src, &src_len, &dst_end, &dst_left);
   20|   864k|        if (status == (size_t)-1) {
  ------------------
  |  Branch (20:13): [True: 3.67k, False: 861k]
  ------------------
   21|  3.67k|            if (errno == E2BIG) {
  ------------------
  |  Branch (21:17): [True: 4, False: 3.66k]
  ------------------
   22|      4|                return READSTAT_ERROR_CONVERT_LONG_STRING;
   23|  3.66k|            } else if (errno == EILSEQ) {
  ------------------
  |  Branch (23:24): [True: 19, False: 3.64k]
  ------------------
   24|     19|                return READSTAT_ERROR_CONVERT_BAD_STRING;
   25|  3.64k|            } else if (errno != EINVAL) { /* EINVAL indicates improper truncation; accept it */
  ------------------
  |  Branch (25:24): [True: 0, False: 3.64k]
  ------------------
   26|      0|                return READSTAT_ERROR_CONVERT;
   27|      0|            }
   28|  3.67k|        }
   29|   864k|        dst[dst_len - dst_left - 1] = '\0';
   30|  20.4M|    } else if (src_len + 1 > dst_len) {
  ------------------
  |  Branch (30:16): [True: 20, False: 20.4M]
  ------------------
   31|     20|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   32|  20.4M|    } else {
   33|  20.4M|        memcpy(dst, src, src_len);
   34|  20.4M|        dst[src_len] = '\0';
   35|  20.4M|    }
   36|  21.3M|    return READSTAT_OK;
   37|  21.3M|}

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

readstat_malloc:
   10|  13.0k|void *readstat_malloc(size_t len) {
   11|  13.0k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|  26.0k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (11:9): [True: 0, False: 13.0k]
  |  Branch (11:34): [True: 0, False: 13.0k]
  ------------------
   12|      0|        return NULL;
   13|      0|    }
   14|  13.0k|    return malloc(len);
   15|  13.0k|}
readstat_calloc:
   17|  2.25M|void *readstat_calloc(size_t count, size_t size) {
   18|  2.25M|    if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  4.51M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  4.51M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  2.25M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (18:9): [True: 0, False: 2.25M]
  |  Branch (18:36): [True: 0, False: 2.25M]
  |  Branch (18:62): [True: 0, False: 2.25M]
  ------------------
   19|      0|        return NULL;
   20|      0|    }
   21|  2.25M|    if (count == 0 || size == 0) {
  ------------------
  |  Branch (21:9): [True: 0, False: 2.25M]
  |  Branch (21:23): [True: 0, False: 2.25M]
  ------------------
   22|      0|        return NULL;
   23|      0|    }
   24|  2.25M|    return calloc(count, size);
   25|  2.25M|}
readstat_realloc:
   27|   230k|void *readstat_realloc(void *ptr, size_t len) {
   28|   230k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|   461k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (28:9): [True: 94, False: 230k]
  |  Branch (28:34): [True: 2, False: 230k]
  ------------------
   29|     96|        if (ptr)
  ------------------
  |  Branch (29:13): [True: 44, False: 52]
  ------------------
   30|     44|            free(ptr);
   31|     96|        return NULL;
   32|     96|    }
   33|   230k|    return realloc(ptr, len);
   34|   230k|}

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

sas_read8:
  147|  94.6k|uint64_t sas_read8(const char *data, int bswap) {
  148|  94.6k|    uint64_t tmp;
  149|  94.6k|    memcpy(&tmp, data, 8);
  150|  94.6k|    return bswap ? byteswap8(tmp) : tmp;
  ------------------
  |  Branch (150:12): [True: 82.2k, False: 12.3k]
  ------------------
  151|  94.6k|}
sas_read4:
  153|   318k|uint32_t sas_read4(const char *data, int bswap) {
  154|   318k|    uint32_t tmp;
  155|   318k|    memcpy(&tmp, data, 4);
  156|   318k|    return bswap ? byteswap4(tmp) : tmp;
  ------------------
  |  Branch (156:12): [True: 231k, False: 86.8k]
  ------------------
  157|   318k|}
sas_read2:
  159|   242k|uint16_t sas_read2(const char *data, int bswap) {
  160|   242k|    uint16_t tmp;
  161|   242k|    memcpy(&tmp, data, 2);
  162|   242k|    return bswap ? byteswap2(tmp) : tmp;
  ------------------
  |  Branch (162:12): [True: 113k, False: 128k]
  ------------------
  163|   242k|}
sas_subheader_remainder:
  165|  13.3k|size_t sas_subheader_remainder(size_t len, size_t signature_len) {
  166|  13.3k|    return len - (4+2*signature_len);
  167|  13.3k|}
sas_read_header:
  170|  4.49k|        readstat_error_handler error_handler, void *user_ctx) {
  171|  4.49k|    sas_header_start_t  header_start;
  172|  4.49k|    sas_header_end_t    header_end;
  173|  4.49k|    int retval = READSTAT_OK;
  174|  4.49k|    char error_buf[1024];
  175|  4.49k|    time_t epoch = sas_epoch();
  176|       |
  177|  4.49k|    if (io->read(&header_start, sizeof(sas_header_start_t), io->io_ctx) < sizeof(sas_header_start_t)) {
  ------------------
  |  Branch (177:9): [True: 20, False: 4.47k]
  ------------------
  178|     20|        retval = READSTAT_ERROR_READ;
  179|     20|        goto cleanup;
  180|     20|    }
  181|  4.47k|    if (memcmp(header_start.magic, sas7bdat_magic_number, sizeof(sas7bdat_magic_number)) != 0 &&
  ------------------
  |  Branch (181:9): [True: 2.07k, False: 2.39k]
  ------------------
  182|  2.07k|            memcmp(header_start.magic, sas7bdat_magic_number_alt, sizeof(sas7bdat_magic_number_alt)) != 0 &&
  ------------------
  |  Branch (182:13): [True: 2.04k, False: 28]
  ------------------
  183|  2.04k|            memcmp(header_start.magic, sas7bcat_magic_number, sizeof(sas7bcat_magic_number)) != 0) {
  ------------------
  |  Branch (183:13): [True: 133, False: 1.91k]
  ------------------
  184|    133|        retval = READSTAT_ERROR_PARSE;
  185|    133|        goto cleanup;
  186|    133|    }
  187|  4.34k|    if (header_start.a1 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   88|  4.34k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (187:9): [True: 9, False: 4.33k]
  ------------------
  188|      9|        hinfo->pad1 = 4;
  189|      9|    }
  190|  4.34k|    if (header_start.a2 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   88|  4.34k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (190:9): [True: 2.36k, False: 1.98k]
  ------------------
  191|  2.36k|        hinfo->u64 = 1;
  192|  2.36k|    }
  193|  4.34k|    int bswap = 0;
  194|  4.34k|    if (header_start.endian == SAS_ENDIAN_BIG) {
  ------------------
  |  |   81|  4.34k|#define SAS_ENDIAN_BIG       0x00
  ------------------
  |  Branch (194:9): [True: 3.23k, False: 1.10k]
  ------------------
  195|  3.23k|        bswap = machine_is_little_endian();
  196|  3.23k|        hinfo->little_endian = 0;
  197|  3.23k|    } else if (header_start.endian == SAS_ENDIAN_LITTLE) {
  ------------------
  |  |   82|  1.10k|#define SAS_ENDIAN_LITTLE    0x01
  ------------------
  |  Branch (197:16): [True: 1.09k, False: 14]
  ------------------
  198|  1.09k|        bswap = !machine_is_little_endian();
  199|  1.09k|        hinfo->little_endian = 1;
  200|  1.09k|    } else {
  201|     14|        retval = READSTAT_ERROR_PARSE;
  202|     14|        goto cleanup;
  203|     14|    }
  204|  4.32k|    int i;
  205|   210k|    for (i=0; i<sizeof(_charset_table)/sizeof(_charset_table[0]); i++) {
  ------------------
  |  Branch (205:15): [True: 208k, False: 2.46k]
  ------------------
  206|   208k|        if (header_start.encoding == _charset_table[i].code) {
  ------------------
  |  Branch (206:13): [True: 1.86k, False: 206k]
  ------------------
  207|  1.86k|            hinfo->encoding = _charset_table[i].name;
  208|  1.86k|            break;
  209|  1.86k|        }
  210|   208k|    }
  211|  4.32k|    if (hinfo->encoding == NULL && header_start.encoding != 0) {
  ------------------
  |  Branch (211:9): [True: 2.46k, False: 1.86k]
  |  Branch (211:36): [True: 10, False: 2.45k]
  ------------------
  212|     10|        if (error_handler) {
  ------------------
  |  Branch (212:13): [True: 0, False: 10]
  ------------------
  213|      0|            snprintf(error_buf, sizeof(error_buf), "Unsupported character set code: %d", header_start.encoding);
  214|      0|            error_handler(error_buf, user_ctx);
  215|      0|        }
  216|     10|        retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
  217|     10|        goto cleanup;
  218|     10|    }
  219|  4.31k|    memcpy(hinfo->table_name, header_start.table_name, sizeof(header_start.table_name));
  220|  4.31k|    if (io->seek(hinfo->pad1, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (220:9): [True: 5, False: 4.31k]
  ------------------
  221|      5|        retval = READSTAT_ERROR_SEEK;
  222|      5|        goto cleanup;
  223|      5|    }
  224|       |
  225|  4.31k|    double creation_time, modification_time, creation_time_diff, modification_time_diff;
  226|       |
  227|  4.31k|    if (io->read(&creation_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (227:9): [True: 12, False: 4.30k]
  ------------------
  228|     12|        retval = READSTAT_ERROR_READ;
  229|     12|        goto cleanup;
  230|     12|    }
  231|  4.30k|    if (bswap)
  ------------------
  |  Branch (231:9): [True: 3.21k, False: 1.08k]
  ------------------
  232|  3.21k|        creation_time = byteswap_double(creation_time);
  233|       |
  234|  4.30k|    if (io->read(&modification_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (234:9): [True: 6, False: 4.29k]
  ------------------
  235|      6|        retval = READSTAT_ERROR_READ;
  236|      6|        goto cleanup;
  237|      6|    }
  238|  4.29k|    if (bswap)
  ------------------
  |  Branch (238:9): [True: 3.20k, False: 1.08k]
  ------------------
  239|  3.20k|        modification_time = byteswap_double(modification_time);
  240|       |
  241|  4.29k|    if (io->read(&creation_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (241:9): [True: 6, False: 4.28k]
  ------------------
  242|      6|        retval = READSTAT_ERROR_READ;
  243|      6|        goto cleanup;
  244|      6|    }
  245|  4.28k|    if (bswap)
  ------------------
  |  Branch (245:9): [True: 3.20k, False: 1.08k]
  ------------------
  246|  3.20k|        creation_time_diff = byteswap_double(creation_time_diff);
  247|       |    
  248|  4.28k|    if (io->read(&modification_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (248:9): [True: 6, False: 4.28k]
  ------------------
  249|      6|        retval = READSTAT_ERROR_READ;
  250|      6|        goto cleanup;
  251|      6|    }
  252|  4.28k|    if (bswap)
  ------------------
  |  Branch (252:9): [True: 3.19k, False: 1.08k]
  ------------------
  253|  3.19k|        modification_time_diff = byteswap_double(modification_time_diff);
  254|       |    
  255|  4.28k|    hinfo->creation_time = sas_convert_time(creation_time, creation_time_diff, epoch);
  256|  4.28k|    hinfo->modification_time = sas_convert_time(modification_time, modification_time_diff, epoch);
  257|       |
  258|  4.28k|    uint32_t header_size, page_size;
  259|       |
  260|  4.28k|    if (io->read(&header_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (260:9): [True: 8, False: 4.27k]
  ------------------
  261|      8|        retval = READSTAT_ERROR_READ;
  262|      8|        goto cleanup;
  263|      8|    }
  264|  4.27k|    if (io->read(&page_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (264:9): [True: 3, False: 4.27k]
  ------------------
  265|      3|        retval = READSTAT_ERROR_READ;
  266|      3|        goto cleanup;
  267|      3|    }
  268|       |
  269|  4.27k|    hinfo->header_size = bswap ? byteswap4(header_size) : header_size;
  ------------------
  |  Branch (269:26): [True: 3.19k, False: 1.08k]
  ------------------
  270|  4.27k|    hinfo->page_size = bswap ? byteswap4(page_size) : page_size;
  ------------------
  |  Branch (270:24): [True: 3.19k, False: 1.08k]
  ------------------
  271|       |
  272|  4.27k|    if (hinfo->header_size < 1024 || hinfo->page_size < 1024) {
  ------------------
  |  Branch (272:9): [True: 10, False: 4.26k]
  |  Branch (272:38): [True: 11, False: 4.25k]
  ------------------
  273|     21|        retval = READSTAT_ERROR_PARSE;
  274|     21|        goto cleanup;
  275|     21|    }
  276|  4.25k|    if (hinfo->header_size > (1<<24) || hinfo->page_size > (1<<24)) {
  ------------------
  |  Branch (276:9): [True: 18, False: 4.23k]
  |  Branch (276:41): [True: 26, False: 4.20k]
  ------------------
  277|     44|        retval = READSTAT_ERROR_PARSE;
  278|     44|        goto cleanup;
  279|     44|    }
  280|       |
  281|  4.20k|    if (hinfo->u64) {
  ------------------
  |  Branch (281:9): [True: 2.30k, False: 1.90k]
  ------------------
  282|  2.30k|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_64BIT;
  ------------------
  |  |  123|  2.30k|#define SAS_PAGE_HEADER_SIZE_64BIT  40
  ------------------
  283|  2.30k|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_64BIT;
  ------------------
  |  |  120|  2.30k|#define SAS_SUBHEADER_POINTER_SIZE_64BIT    24
  ------------------
  284|  2.30k|    } else {
  285|  1.90k|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_32BIT;
  ------------------
  |  |  122|  1.90k|#define SAS_PAGE_HEADER_SIZE_32BIT  24
  ------------------
  286|  1.90k|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_32BIT;
  ------------------
  |  |  119|  1.90k|#define SAS_SUBHEADER_POINTER_SIZE_32BIT    12
  ------------------
  287|  1.90k|    }
  288|       |
  289|  4.20k|    if (hinfo->u64) {
  ------------------
  |  Branch (289:9): [True: 2.30k, False: 1.90k]
  ------------------
  290|  2.30k|        uint64_t page_count;
  291|  2.30k|        if (io->read(&page_count, sizeof(uint64_t), io->io_ctx) < sizeof(uint64_t)) {
  ------------------
  |  Branch (291:13): [True: 17, False: 2.29k]
  ------------------
  292|     17|            retval = READSTAT_ERROR_READ;
  293|     17|            goto cleanup;
  294|     17|        }
  295|  2.29k|        hinfo->page_count = bswap ? byteswap8(page_count) : page_count;
  ------------------
  |  Branch (295:29): [True: 1.84k, False: 441]
  ------------------
  296|  2.29k|    } else {
  297|  1.90k|        uint32_t page_count;
  298|  1.90k|        if (io->read(&page_count, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (298:13): [True: 23, False: 1.87k]
  ------------------
  299|     23|            retval = READSTAT_ERROR_READ;
  300|     23|            goto cleanup;
  301|     23|        }
  302|  1.87k|        hinfo->page_count = bswap ? byteswap4(page_count) : page_count;
  ------------------
  |  Branch (302:29): [True: 1.24k, False: 628]
  ------------------
  303|  1.87k|    }
  304|  4.16k|    if (hinfo->page_count > (1<<24)) {
  ------------------
  |  Branch (304:9): [True: 14, False: 4.15k]
  ------------------
  305|     14|        retval = READSTAT_ERROR_PARSE;
  306|     14|        goto cleanup;
  307|     14|    }
  308|       |    
  309|  4.15k|    if (io->seek(8, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (309:9): [True: 70, False: 4.08k]
  ------------------
  310|     70|        retval = READSTAT_ERROR_SEEK;
  311|     70|        if (error_handler) {
  ------------------
  |  Branch (311:13): [True: 0, False: 70]
  ------------------
  312|      0|            snprintf(error_buf, sizeof(error_buf), "ReadStat: Failed to seek forward by %d", 8);
  313|      0|            error_handler(error_buf, user_ctx);
  314|      0|        }
  315|     70|        goto cleanup;
  316|     70|    }
  317|  4.08k|    if (io->read(&header_end, sizeof(sas_header_end_t), io->io_ctx) < sizeof(sas_header_end_t)) {
  ------------------
  |  Branch (317:9): [True: 17, False: 4.06k]
  ------------------
  318|     17|        retval = READSTAT_ERROR_READ;
  319|     17|        goto cleanup;
  320|     17|    }
  321|  4.06k|    char major, revision_tag, revision;
  322|  4.06k|    int minor;
  323|       |    /* The release field is not NUL-terminated in the file; copy it into a
  324|       |     * terminated buffer before handing it to sscanf, which calls strlen on it. */
  325|  4.06k|    char release[sizeof(header_end.release)+1];
  326|  4.06k|    memcpy(release, header_end.release, sizeof(header_end.release));
  327|  4.06k|    release[sizeof(header_end.release)] = '\0';
  328|  4.06k|    if (sscanf(release, "%c.%04d%c%c", &major, &minor, &revision_tag, &revision) != 4) {
  ------------------
  |  Branch (328:9): [True: 3, False: 4.06k]
  ------------------
  329|      3|        retval = READSTAT_ERROR_PARSE;
  330|      3|        goto cleanup;
  331|      3|    }
  332|       |
  333|  4.06k|    if (major >= '1' && major <= '9') {
  ------------------
  |  Branch (333:9): [True: 4.05k, False: 11]
  |  Branch (333:25): [True: 4.04k, False: 11]
  ------------------
  334|  4.04k|        hinfo->major_version = major - '0';
  335|  4.04k|    } else if (major == 'V') {
  ------------------
  |  Branch (335:16): [True: 4, False: 18]
  ------------------
  336|       |        // It appears that SAS Visual Forecaster reports the major version as "V"
  337|       |        // Treat it as version 9 for all intents and purposes
  338|      4|        hinfo->major_version = 9;
  339|     18|    } else {
  340|     18|        retval = READSTAT_ERROR_PARSE;
  341|     18|        goto cleanup;
  342|     18|    }
  343|       |    // revision_tag is usually M, but J has been observed in the wild (not created with SAS?)
  344|       |    // files have also been observed with TK in place of the revision details.
  345|  4.04k|    if ((major == '8' || major == '9') && revision_tag != 'M' && revision_tag != 'J' && revision_tag != 'T') {
  ------------------
  |  Branch (345:10): [True: 309, False: 3.73k]
  |  Branch (345:26): [True: 155, False: 3.58k]
  |  Branch (345:43): [True: 38, False: 426]
  |  Branch (345:66): [True: 25, False: 13]
  |  Branch (345:89): [True: 16, False: 9]
  ------------------
  346|     16|        retval = READSTAT_ERROR_PARSE;
  347|     16|        goto cleanup;
  348|     16|    }
  349|  4.02k|    hinfo->minor_version = minor;
  350|  4.02k|    if (revision >= '0' && revision <= '9') {
  ------------------
  |  Branch (350:9): [True: 4.01k, False: 13]
  |  Branch (350:28): [True: 4.00k, False: 13]
  ------------------
  351|  4.00k|        hinfo->revision = revision - '0';
  352|  4.00k|    } else if (revision_tag == 'T' && revision == 'K') {
  ------------------
  |  Branch (352:16): [True: 13, False: 13]
  |  Branch (352:39): [True: 1, False: 12]
  ------------------
  353|       |        // files have been observed with TK in place of the revision details.
  354|      1|        hinfo->revision = 0;
  355|     25|    } else {
  356|     25|        retval = READSTAT_ERROR_PARSE;
  357|     25|        goto cleanup;
  358|     25|    }
  359|       |
  360|  4.00k|    if ((major == '8' || major == '9') && minor == 0 && hinfo->revision == 0) {
  ------------------
  |  Branch (360:10): [True: 292, False: 3.71k]
  |  Branch (360:26): [True: 149, False: 3.56k]
  |  Branch (360:43): [True: 120, False: 321]
  |  Branch (360:57): [True: 84, False: 36]
  ------------------
  361|       |        /* A bit of a hack, but most SAS installations are running a minor update */
  362|     84|        hinfo->vendor = READSTAT_VENDOR_STAT_TRANSFER;
  363|  3.92k|    } else {
  364|  3.92k|        hinfo->vendor = READSTAT_VENDOR_SAS;
  365|  3.92k|    }
  366|  4.00k|    if (io->seek(hinfo->header_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (366:9): [True: 47, False: 3.95k]
  ------------------
  367|     47|        retval = READSTAT_ERROR_SEEK;
  368|     47|        if (error_handler) {
  ------------------
  |  Branch (368:13): [True: 0, False: 47]
  ------------------
  369|      0|            snprintf(error_buf, sizeof(error_buf), 
  370|      0|                    "ReadStat: Failed to seek to position %" PRId64, hinfo->header_size);
  371|      0|            error_handler(error_buf, user_ctx);
  372|      0|        }
  373|     47|        goto cleanup;
  374|     47|    }
  375|       |
  376|  4.49k|cleanup:
  377|  4.49k|    return retval;
  378|  4.00k|}
sas_validate_tag:
  599|  5.47k|readstat_error_t sas_validate_tag(char tag) {
  600|  5.47k|    if (tag == '_' || (tag >= 'A' && tag <= 'Z'))
  ------------------
  |  Branch (600:9): [True: 2.85k, False: 2.62k]
  |  Branch (600:24): [True: 994, False: 1.63k]
  |  Branch (600:38): [True: 727, False: 267]
  ------------------
  601|  3.57k|        return READSTAT_OK;
  602|       |
  603|  1.89k|    return READSTAT_ERROR_TAGGED_VALUE_IS_OUT_OF_RANGE;
  604|  5.47k|}
sas_assign_tag:
  639|  5.47k|void sas_assign_tag(readstat_value_t *value, uint8_t tag) {
  640|       |    /* We accommodate two tag schemes. In the first, the tag is an ASCII code
  641|       |     * given by uint8_t tag above. System missing is represented by an ASCII
  642|       |     * period. In the second scheme, (tag-2) is an offset from 'A', except when
  643|       |     * tag == 0, in which case it represents an underscore, or tag == 1, in
  644|       |     * which case it represents system-missing.
  645|       |     */
  646|  5.47k|    if (tag == 0) {
  ------------------
  |  Branch (646:9): [True: 2.84k, False: 2.63k]
  ------------------
  647|  2.84k|        tag = '_';
  648|  2.84k|    } else if (tag >= 2 && tag < 28) {
  ------------------
  |  Branch (648:16): [True: 2.23k, False: 396]
  |  Branch (648:28): [True: 675, False: 1.56k]
  ------------------
  649|    675|        tag = 'A' + (tag - 2);
  650|    675|    }
  651|  5.47k|    if (sas_validate_tag(tag) == READSTAT_OK) {
  ------------------
  |  Branch (651:9): [True: 3.57k, False: 1.89k]
  ------------------
  652|  3.57k|        value->tag = tag;
  653|  3.57k|        value->is_tagged_missing = 1;
  654|  3.57k|    } else {
  655|  1.89k|        value->tag = 0;
  656|  1.89k|        value->is_system_missing = 1;
  657|  1.89k|    }
  658|  5.47k|}
readstat_sas.c:sas_epoch:
  131|  4.49k|static time_t sas_epoch(void) {
  132|  4.49k|    return - 3653 * 86400; // seconds between 01-01-1960 and 01-01-1970
  133|  4.49k|}
readstat_sas.c:sas_convert_time:
  135|  8.56k|static time_t sas_convert_time(double time, double time_diff, time_t epoch) {
  136|  8.56k|    time -= time_diff;
  137|  8.56k|    time += epoch;
  138|  8.56k|    if (isnan(time))
  ------------------
  |  Branch (138:9): [True: 1.03k, False: 7.52k]
  ------------------
  139|  1.03k|        return 0;
  140|  7.52k|    if (time > (double)LONG_MAX)
  ------------------
  |  Branch (140:9): [True: 1.78k, False: 5.74k]
  ------------------
  141|  1.78k|        return LONG_MAX;
  142|  5.74k|    if (time < (double)LONG_MIN)
  ------------------
  |  Branch (142:9): [True: 1.19k, False: 4.55k]
  ------------------
  143|  1.19k|        return LONG_MIN;
  144|  4.55k|    return time;
  145|  5.74k|}

readstat_parse_sas7bdat:
 1481|  4.49k|readstat_error_t readstat_parse_sas7bdat(readstat_parser_t *parser, const char *path, void *user_ctx) {
 1482|  4.49k|    int64_t last_examined_page_pass1 = 0;
 1483|  4.49k|    readstat_error_t retval = READSTAT_OK;
 1484|  4.49k|    readstat_io_t *io = parser->io;
 1485|       |
 1486|  4.49k|    sas7bdat_ctx_t  *ctx = calloc(1, sizeof(sas7bdat_ctx_t));
 1487|  4.49k|    sas_header_info_t  *hinfo = calloc(1, sizeof(sas_header_info_t));
 1488|       |
 1489|  4.49k|    if (ctx == NULL || hinfo == NULL) {
  ------------------
  |  Branch (1489:9): [True: 0, False: 4.49k]
  |  Branch (1489:24): [True: 0, False: 4.49k]
  ------------------
 1490|      0|        retval = READSTAT_ERROR_MALLOC;
 1491|      0|        goto cleanup;
 1492|      0|    }
 1493|       |
 1494|  4.49k|    ctx->handle = parser->handlers;
 1495|  4.49k|    ctx->input_encoding = parser->input_encoding;
 1496|  4.49k|    ctx->output_encoding = parser->output_encoding;
 1497|  4.49k|    ctx->user_ctx = user_ctx;
 1498|  4.49k|    ctx->io = parser->io;
 1499|  4.49k|    ctx->row_limit = parser->row_limit;
 1500|  4.49k|    ctx->moved_page_index = -1;
 1501|  4.49k|    if (parser->row_offset > 0)
  ------------------
  |  Branch (1501:9): [True: 0, False: 4.49k]
  ------------------
 1502|      0|        ctx->row_offset = parser->row_offset;
 1503|       |
 1504|  4.49k|    if (io->open(path, io->io_ctx) == -1) {
  ------------------
  |  Branch (1504:9): [True: 0, False: 4.49k]
  ------------------
 1505|      0|        retval = READSTAT_ERROR_OPEN;
 1506|      0|        goto cleanup;
 1507|      0|    }
 1508|       |
 1509|  4.49k|    if ((ctx->file_size = io->seek(0, READSTAT_SEEK_END, io->io_ctx)) == -1) {
  ------------------
  |  Branch (1509:9): [True: 0, False: 4.49k]
  ------------------
 1510|      0|        retval = READSTAT_ERROR_SEEK;
 1511|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1511:13): [True: 0, False: 0]
  ------------------
 1512|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to end of file");
 1513|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1514|      0|        }
 1515|      0|        goto cleanup;
 1516|      0|    }
 1517|       |
 1518|  4.49k|    if (io->seek(0, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1518:9): [True: 0, False: 4.49k]
  ------------------
 1519|      0|        retval = READSTAT_ERROR_SEEK;
 1520|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1520:13): [True: 0, False: 0]
  ------------------
 1521|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to beginning of file");
 1522|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1523|      0|        }
 1524|      0|        goto cleanup;
 1525|      0|    }
 1526|       |
 1527|  4.49k|    if ((retval = sas_read_header(io, hinfo, ctx->handle.error, user_ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1527:9): [True: 538, False: 3.95k]
  ------------------
 1528|    538|        goto cleanup;
 1529|    538|    }
 1530|       |
 1531|  3.95k|    ctx->u64 = hinfo->u64;
 1532|  3.95k|    ctx->little_endian = hinfo->little_endian;
 1533|  3.95k|    ctx->vendor = hinfo->vendor;
 1534|  3.95k|    ctx->bswap = machine_is_little_endian() ^ hinfo->little_endian;
 1535|  3.95k|    ctx->header_size = hinfo->header_size;
 1536|  3.95k|    ctx->page_count = hinfo->page_count;
 1537|  3.95k|    ctx->page_size = hinfo->page_size;
 1538|  3.95k|    ctx->page_header_size = hinfo->page_header_size;
 1539|  3.95k|    ctx->subheader_pointer_size = hinfo->subheader_pointer_size;
 1540|  3.95k|    ctx->subheader_signature_size = ctx->u64 ? 8 : 4;
  ------------------
  |  Branch (1540:37): [True: 2.23k, False: 1.71k]
  ------------------
 1541|  3.95k|    ctx->ctime = hinfo->creation_time;
 1542|  3.95k|    ctx->mtime = hinfo->modification_time;
 1543|  3.95k|    ctx->version = hinfo->major_version;
 1544|  3.95k|    if (ctx->input_encoding == NULL) {
  ------------------
  |  Branch (1544:9): [True: 3.95k, False: 0]
  ------------------
 1545|  3.95k|        ctx->input_encoding = hinfo->encoding;
 1546|  3.95k|    }
 1547|  3.95k|    if ((ctx->page = readstat_malloc(ctx->page_size)) == NULL) {
  ------------------
  |  Branch (1547:9): [True: 0, False: 3.95k]
  ------------------
 1548|      0|        retval = READSTAT_ERROR_MALLOC;
 1549|      0|        goto cleanup;
 1550|      0|    }
 1551|       |
 1552|  3.95k|    const char *input_encoding = ctx->input_encoding;
 1553|  3.95k|    if (input_encoding == NULL) {
  ------------------
  |  Branch (1553:9): [True: 2.26k, False: 1.69k]
  ------------------
 1554|  2.26k|        input_encoding = SAS_DEFAULT_STRING_ENCODING;
  ------------------
  |  |  141|  2.26k|#define SAS_DEFAULT_STRING_ENCODING "WINDOWS-1252"
  ------------------
 1555|  2.26k|    }
 1556|  3.95k|    if (ctx->output_encoding && strcmp(input_encoding, ctx->output_encoding) != 0) {
  ------------------
  |  Branch (1556:9): [True: 3.95k, False: 0]
  |  Branch (1556:33): [True: 2.76k, False: 1.19k]
  ------------------
 1557|  2.76k|        iconv_t converter = iconv_open(ctx->output_encoding, input_encoding);
 1558|  2.76k|        if (converter == (iconv_t)-1) {
  ------------------
  |  Branch (1558:13): [True: 2, False: 2.75k]
  ------------------
 1559|      2|            retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
 1560|      2|            goto cleanup;
 1561|      2|        }
 1562|  2.75k|        ctx->converter = converter;
 1563|  2.75k|    }
 1564|       |
 1565|  3.95k|    if ((retval = readstat_convert(ctx->table_name, sizeof(ctx->table_name),
  ------------------
  |  Branch (1565:9): [True: 4, False: 3.95k]
  ------------------
 1566|  3.95k|                hinfo->table_name, sizeof(hinfo->table_name), ctx->converter)) != READSTAT_OK) {
 1567|      4|        goto cleanup;
 1568|      4|    }
 1569|       |
 1570|  3.95k|    if ((retval = sas7bdat_parse_meta_pages_pass1(ctx, &last_examined_page_pass1)) != READSTAT_OK) {
  ------------------
  |  Branch (1570:9): [True: 230, False: 3.72k]
  ------------------
 1571|    230|        goto cleanup;
 1572|    230|    }
 1573|       |
 1574|  3.72k|    if ((retval = sas7bdat_parse_amd_pages_pass1(last_examined_page_pass1, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1574:9): [True: 1.16k, False: 2.55k]
  ------------------
 1575|  1.16k|        goto cleanup;
 1576|  1.16k|    }
 1577|       |
 1578|  2.55k|    if (io->seek(ctx->header_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1578:9): [True: 0, False: 2.55k]
  ------------------
 1579|      0|        retval = READSTAT_ERROR_SEEK;
 1580|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1580:13): [True: 0, False: 0]
  ------------------
 1581|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64, 
 1582|      0|                    ctx->header_size);
 1583|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1584|      0|        }
 1585|      0|        goto cleanup;
 1586|      0|    }
 1587|       |
 1588|  2.55k|    if ((retval = sas7bdat_parse_all_pages_pass2(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1588:9): [True: 2.20k, False: 355]
  ------------------
 1589|  2.20k|        goto cleanup;
 1590|  2.20k|    }
 1591|       |    
 1592|    355|    if ((retval = sas7bdat_submit_columns_if_needed(ctx, 0)) != READSTAT_OK) {
  ------------------
  |  Branch (1592:9): [True: 100, False: 255]
  ------------------
 1593|    100|        goto cleanup;
 1594|    100|    }
 1595|       |
 1596|    255|    if (ctx->handle.value && ctx->parsed_row_count != ctx->row_limit) {
  ------------------
  |  Branch (1596:9): [True: 255, False: 0]
  |  Branch (1596:30): [True: 130, False: 125]
  ------------------
 1597|    130|        retval = READSTAT_ERROR_ROW_COUNT_MISMATCH;
 1598|    130|        if (ctx->handle.error) {
  ------------------
  |  Branch (1598:13): [True: 0, False: 130]
  ------------------
 1599|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Expected %d rows in file, found %d",
 1600|      0|                    ctx->row_limit, ctx->parsed_row_count);
 1601|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1602|      0|        }
 1603|    130|        goto cleanup;
 1604|    130|    }
 1605|       |
 1606|    125|    if ((retval = sas7bdat_update_progress(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1606:9): [True: 0, False: 125]
  ------------------
 1607|      0|        goto cleanup;
 1608|      0|    }
 1609|       |
 1610|  4.49k|cleanup:
 1611|  4.49k|    io->close(io->io_ctx);
 1612|       |
 1613|  4.49k|    if (retval == READSTAT_ERROR_OPEN ||
  ------------------
  |  Branch (1613:9): [True: 0, False: 4.49k]
  ------------------
 1614|  4.49k|            retval == READSTAT_ERROR_READ ||
  ------------------
  |  Branch (1614:13): [True: 614, False: 3.88k]
  ------------------
 1615|  3.88k|            retval == READSTAT_ERROR_SEEK) {
  ------------------
  |  Branch (1615:13): [True: 491, False: 3.39k]
  ------------------
 1616|  1.10k|        if (ctx->handle.error) {
  ------------------
  |  Branch (1616:13): [True: 0, False: 1.10k]
  ------------------
 1617|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: %s (retval = %d): %s (errno = %d)", 
 1618|      0|                    readstat_error_message(retval), retval, strerror(errno), errno);
 1619|      0|            ctx->handle.error(ctx->error_buf, user_ctx);
 1620|      0|        }
 1621|  1.10k|    }
 1622|       |
 1623|  4.49k|    if (ctx)
  ------------------
  |  Branch (1623:9): [True: 4.49k, False: 0]
  ------------------
 1624|  4.49k|        sas7bdat_ctx_free(ctx);
 1625|  4.49k|    if (hinfo)
  ------------------
  |  Branch (1625:9): [True: 4.49k, False: 0]
  ------------------
 1626|  4.49k|        free(hinfo);
 1627|       |
 1628|  4.49k|    return retval;
 1629|    125|}
readstat_sas7bdat_read.c:sas7bdat_parse_meta_pages_pass1:
 1322|  3.95k|static readstat_error_t sas7bdat_parse_meta_pages_pass1(sas7bdat_ctx_t *ctx, int64_t *outLastExaminedPage) {
 1323|  3.95k|    readstat_error_t retval = READSTAT_OK;
 1324|  3.95k|    readstat_io_t *io = ctx->io;
 1325|  3.95k|    int64_t i;
 1326|       |
 1327|       |    /* look for META and MIX pages at beginning... */
 1328|  11.5k|    for (i=0; i<ctx->page_count; i++) {
  ------------------
  |  Branch (1328:15): [True: 8.77k, False: 2.79k]
  ------------------
 1329|  8.77k|        if (io->seek(ctx->header_size + i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1329:13): [True: 9, False: 8.76k]
  ------------------
 1330|      9|            retval = READSTAT_ERROR_SEEK;
 1331|      9|            if (ctx->handle.error) {
  ------------------
  |  Branch (1331:17): [True: 0, False: 9]
  ------------------
 1332|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64 
 1333|      0|                        " (= %" PRId64 " + %" PRId64 "*%" PRId64 ")",
 1334|      0|                        ctx->header_size + i*ctx->page_size, ctx->header_size, i, ctx->page_size);
 1335|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1336|      0|            }
 1337|      9|            goto cleanup;
 1338|      9|        }
 1339|       |
 1340|  8.76k|        readstat_off_t off = 0;
 1341|  8.76k|        if (ctx->u64)
  ------------------
  |  Branch (1341:13): [True: 6.45k, False: 2.31k]
  ------------------
 1342|  6.45k|            off = 16;
 1343|       |
 1344|  8.76k|        size_t head_len = off + 16 + 2;
 1345|  8.76k|        size_t tail_len = ctx->page_size - head_len;
 1346|       |
 1347|  8.76k|        if (io->read(ctx->page, head_len, io->io_ctx) < head_len) {
  ------------------
  |  Branch (1347:13): [True: 137, False: 8.62k]
  ------------------
 1348|    137|            retval = READSTAT_ERROR_READ;
 1349|    137|            goto cleanup;
 1350|    137|        }
 1351|       |
 1352|  8.62k|        uint16_t page_type = sas_read2(&ctx->page[off+16], ctx->bswap);
 1353|       |
 1354|  8.62k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA)
  ------------------
  |  |  113|  8.62k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA)
  ------------------
  |  |  110|  8.62k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1354:13): [True: 930, False: 7.69k]
  ------------------
 1355|    930|            break;
 1356|  7.69k|        if ((page_type & SAS_PAGE_TYPE_COMP))
  ------------------
  |  |  117|  7.69k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1356:13): [True: 1.61k, False: 6.08k]
  ------------------
 1357|  1.61k|            continue;
 1358|       |
 1359|  6.08k|        if (io->read(ctx->page + head_len, tail_len, io->io_ctx) < tail_len) {
  ------------------
  |  Branch (1359:13): [True: 27, False: 6.06k]
  ------------------
 1360|     27|            retval = READSTAT_ERROR_READ;
 1361|     27|            goto cleanup;
 1362|     27|        }
 1363|       |
 1364|  6.06k|        if ((retval = sas7bdat_parse_page_pass1(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1364:13): [True: 57, False: 6.00k]
  ------------------
 1365|     57|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1365:17): [True: 0, False: 57]
  |  Branch (1365:38): [True: 0, False: 0]
  ------------------
 1366|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1367|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1368|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1369|      0|                        i, pos - ctx->page_size, pos-1);
 1370|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1371|      0|            }
 1372|     57|            goto cleanup;
 1373|     57|        }
 1374|  6.06k|    }
 1375|       |
 1376|  3.95k|cleanup:
 1377|  3.95k|    if (outLastExaminedPage)
  ------------------
  |  Branch (1377:9): [True: 3.95k, False: 0]
  ------------------
 1378|  3.95k|        *outLastExaminedPage = i;
 1379|       |
 1380|  3.95k|    return retval;
 1381|  3.95k|}
readstat_sas7bdat_read.c:sas7bdat_parse_page_pass1:
 1024|  9.50k|static readstat_error_t sas7bdat_parse_page_pass1(const char *page, size_t page_size, sas7bdat_ctx_t *ctx) {
 1025|  9.50k|    readstat_error_t retval = READSTAT_OK;
 1026|       |
 1027|  9.50k|    uint16_t subheader_count = sas_read2(&page[ctx->page_header_size-4], ctx->bswap);
 1028|       |
 1029|  9.50k|    int i;
 1030|  9.50k|    const char *shp = &page[ctx->page_header_size];
 1031|  9.50k|    int lshp = ctx->subheader_pointer_size;
 1032|       |
 1033|  9.50k|    if (ctx->page_header_size + subheader_count*lshp > page_size) {
  ------------------
  |  Branch (1033:9): [True: 27, False: 9.48k]
  ------------------
 1034|     27|        retval = READSTAT_ERROR_PARSE;
 1035|     27|        goto cleanup;
 1036|     27|    }
 1037|       |
 1038|   101k|    for (i=0; i<subheader_count; i++) {
  ------------------
  |  Branch (1038:15): [True: 93.1k, False: 8.59k]
  ------------------
 1039|  93.1k|        subheader_pointer_t shp_info = { 0 };
 1040|  93.1k|        if ((retval = sas7bdat_parse_subheader_pointer(shp, page + page_size - shp, &shp_info, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1040:13): [True: 0, False: 93.1k]
  ------------------
 1041|      0|            goto cleanup;
 1042|      0|        }
 1043|  93.1k|        if (shp_info.len > 0 && shp_info.compression != SAS_COMPRESSION_TRUNC && shp_info.compression != SAS_COMPRESSION_REFERENCE) {
  ------------------
  |  |  126|   154k|#define SAS_COMPRESSION_TRUNC              0x01
  ------------------
                      if (shp_info.len > 0 && shp_info.compression != SAS_COMPRESSION_TRUNC && shp_info.compression != SAS_COMPRESSION_REFERENCE) {
  ------------------
  |  |  128|  54.0k|#define SAS_COMPRESSION_REFERENCE          0x03
  ------------------
  |  Branch (1043:13): [True: 61.3k, False: 31.7k]
  |  Branch (1043:33): [True: 54.0k, False: 7.33k]
  |  Branch (1043:82): [True: 51.9k, False: 2.10k]
  ------------------
 1044|  51.9k|            if ((retval = sas7bdat_validate_subheader_pointer(&shp_info, page_size, subheader_count, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1044:17): [True: 792, False: 51.1k]
  ------------------
 1045|    792|                goto cleanup;
 1046|    792|            }
 1047|  51.1k|            if (shp_info.compression == SAS_COMPRESSION_NONE) {
  ------------------
  |  |  125|  51.1k|#define SAS_COMPRESSION_NONE               0x00
  ------------------
  |  Branch (1047:17): [True: 41.5k, False: 9.57k]
  ------------------
 1048|  41.5k|                sas_subheader_type_t subheader_type = sas7bdat_parse_subheader_type(page + shp_info.offset, ctx);
 1049|  41.5k|                if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (1049:21): [True: 8.59k, False: 32.9k]
  ------------------
 1050|  8.59k|                    if ((retval = sas7bdat_parse_subheader(subheader_type, page + shp_info.offset, shp_info.len, ctx))
  ------------------
  |  Branch (1050:25): [True: 63, False: 8.53k]
  ------------------
 1051|  8.59k|                            != READSTAT_OK) {
 1052|     63|                        goto cleanup;
 1053|     63|                    }
 1054|  8.59k|                }
 1055|  41.5k|            } else if (shp_info.compression == SAS_COMPRESSION_ROW || shp_info.compression == SAS_COMPRESSION_DELETED_ROW
  ------------------
  |  |  129|  19.1k|#define SAS_COMPRESSION_ROW                0x04
  ------------------
                          } else if (shp_info.compression == SAS_COMPRESSION_ROW || shp_info.compression == SAS_COMPRESSION_DELETED_ROW
  ------------------
  |  |  130|  12.9k|#define SAS_COMPRESSION_DELETED_ROW        0x05
  ------------------
  |  Branch (1055:24): [True: 6.19k, False: 3.38k]
  |  Branch (1055:71): [True: 472, False: 2.90k]
  ------------------
 1056|  9.54k|                    || sas7bdat_is_moved_row(shp_info.compression)) {
  ------------------
  |  Branch (1056:24): [True: 2.87k, False: 33]
  ------------------
 1057|       |                /* void */
 1058|  9.54k|            } else {
 1059|     33|                retval = READSTAT_ERROR_UNSUPPORTED_COMPRESSION;
 1060|     33|                goto cleanup;
 1061|     33|            }
 1062|  51.1k|        }
 1063|       |
 1064|  92.2k|        shp += lshp;
 1065|  92.2k|    }
 1066|       |
 1067|  9.50k|cleanup:
 1068|       |
 1069|  9.50k|    return retval;
 1070|  9.48k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_pointer:
  971|   145k|        subheader_pointer_t *info, sas7bdat_ctx_t *ctx) {
  972|   145k|    readstat_error_t retval = READSTAT_OK;
  973|   145k|    if (ctx->u64) {
  ------------------
  |  Branch (973:9): [True: 42.9k, False: 102k]
  ------------------
  974|  42.9k|        if (shp_size <= 17) {
  ------------------
  |  Branch (974:13): [True: 0, False: 42.9k]
  ------------------
  975|      0|            retval = READSTAT_ERROR_PARSE;
  976|      0|            goto cleanup;
  977|      0|        }
  978|  42.9k|        info->offset = sas_read8(&shp[0], ctx->bswap);
  979|  42.9k|        info->len = sas_read8(&shp[8], ctx->bswap);
  980|  42.9k|        info->compression = shp[16];
  981|  42.9k|        info->is_compressed_data = shp[17];
  982|   102k|    } else {
  983|   102k|        if (shp_size <= 9) {
  ------------------
  |  Branch (983:13): [True: 0, False: 102k]
  ------------------
  984|      0|            retval = READSTAT_ERROR_PARSE;
  985|      0|            goto cleanup;
  986|      0|        }
  987|   102k|        info->offset = sas_read4(&shp[0], ctx->bswap);
  988|   102k|        info->len = sas_read4(&shp[4], ctx->bswap);
  989|   102k|        info->compression = shp[8];
  990|   102k|        info->is_compressed_data = shp[9];
  991|   102k|    }
  992|   145k|cleanup:
  993|   145k|    return retval;
  994|   145k|}
readstat_sas7bdat_read.c:sas7bdat_validate_subheader_pointer:
  997|  80.2k|        uint16_t subheader_count, sas7bdat_ctx_t *ctx) {
  998|  80.2k|    if (shp_info->offset > page_size)
  ------------------
  |  Branch (998:9): [True: 627, False: 79.6k]
  ------------------
  999|    627|        return READSTAT_ERROR_PARSE;
 1000|  79.6k|    if (shp_info->len > page_size)
  ------------------
  |  Branch (1000:9): [True: 486, False: 79.1k]
  ------------------
 1001|    486|        return READSTAT_ERROR_PARSE;
 1002|  79.1k|    if (shp_info->offset + shp_info->len > page_size)
  ------------------
  |  Branch (1002:9): [True: 47, False: 79.1k]
  ------------------
 1003|     47|        return READSTAT_ERROR_PARSE;
 1004|  79.1k|    if (shp_info->offset < ctx->page_header_size + subheader_count*ctx->subheader_pointer_size)
  ------------------
  |  Branch (1004:9): [True: 43, False: 79.0k]
  ------------------
 1005|     43|        return READSTAT_ERROR_PARSE;
 1006|  79.0k|    if (shp_info->compression == SAS_COMPRESSION_NONE || shp_info->compression == SAS_COMPRESSION_NONE_MOVED) {
  ------------------
  |  |  125|   158k|#define SAS_COMPRESSION_NONE               0x00
  ------------------
                  if (shp_info->compression == SAS_COMPRESSION_NONE || shp_info->compression == SAS_COMPRESSION_NONE_MOVED) {
  ------------------
  |  |  127|  13.8k|#define SAS_COMPRESSION_NONE_MOVED         0x02
  ------------------
  |  Branch (1006:9): [True: 65.2k, False: 13.8k]
  |  Branch (1006:58): [True: 1.00k, False: 12.8k]
  ------------------
 1007|  66.2k|        if (shp_info->len < ctx->subheader_signature_size)
  ------------------
  |  Branch (1007:13): [True: 13, False: 66.1k]
  ------------------
 1008|     13|            return READSTAT_ERROR_PARSE;
 1009|  66.1k|        if (shp_info->offset + ctx->subheader_signature_size > page_size)
  ------------------
  |  Branch (1009:13): [True: 0, False: 66.1k]
  ------------------
 1010|      0|            return READSTAT_ERROR_PARSE;
 1011|  66.1k|    }
 1012|       |    
 1013|  79.0k|    return READSTAT_OK;
 1014|  79.0k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_type:
  935|  65.2k|static sas_subheader_type_t sas7bdat_parse_subheader_type(const char* subheader, sas7bdat_ctx_t* ctx) {
  936|  65.2k|    if (!ctx->u64) {
  ------------------
  |  Branch (936:9): [True: 39.8k, False: 25.3k]
  ------------------
  937|  39.8k|        uint32_t signature_32 = sas_read4(subheader, ctx->bswap);
  938|  39.8k|        return sas7bdat_parse_subheader_type_32(signature_32);
  939|  39.8k|    }
  940|       |
  941|  25.3k|    if (!ctx->little_endian) {
  ------------------
  |  Branch (941:9): [True: 22.3k, False: 2.98k]
  ------------------
  942|       |        /* Big-endian 64-bit files lay out signatures differently from
  943|       |         * little-endian ones: ROW_SIZE and COLUMN_SIZE occupy only the first
  944|       |         * four bytes (the remaining four are unspecified), while the 0xFFFF...
  945|       |         * family is written as 0xFFFFFFFF followed by the 32-bit signature. */
  946|  22.3k|        uint32_t signature_32 = sas_read4(subheader, ctx->bswap);
  947|  22.3k|        if (signature_32 == SAS_SUBHEADER_SIGNATURE_ROW_SIZE) {
  ------------------
  |  |   93|  22.3k|#define SAS_SUBHEADER_SIGNATURE_ROW_SIZE       0xF7F7F7F7
  ------------------
  |  Branch (947:13): [True: 923, False: 21.4k]
  ------------------
  948|    923|            return SAS_SUBHEADER_TYPE_ROW_SIZE;
  949|  21.4k|        } else if (signature_32 == SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE) {
  ------------------
  |  |   94|  21.4k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE    0xF6F6F6F6
  ------------------
  |  Branch (949:20): [True: 334, False: 21.1k]
  ------------------
  950|    334|            return SAS_SUBHEADER_TYPE_COLUMN_SIZE;
  951|  21.1k|        } else if (signature_32 != SAS_SUBHEADER_SIGNATURE_COLUMN_NAME) {
  ------------------
  |  |  104|  21.1k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_NAME    0xFFFFFFFF
  ------------------
  |  Branch (951:20): [True: 1.12k, False: 19.9k]
  ------------------
  952|  1.12k|            return SAS_SUBHEADER_TYPE_DATA;
  953|  1.12k|        }
  954|  19.9k|        return sas7bdat_parse_subheader_type_32(sas_read4(&subheader[4], ctx->bswap));
  955|  22.3k|    }
  956|       |
  957|  2.98k|    uint64_t signature = sas_read8(subheader, ctx->bswap);
  958|  2.98k|    if (signature == SAS_SUBHEADER_SIGNATURE_ROW_SIZE) {
  ------------------
  |  |   93|  2.98k|#define SAS_SUBHEADER_SIGNATURE_ROW_SIZE       0xF7F7F7F7
  ------------------
  |  Branch (958:9): [True: 190, False: 2.79k]
  ------------------
  959|    190|        return SAS_SUBHEADER_TYPE_ROW_SIZE;
  960|  2.79k|    } else if (signature == SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE) {
  ------------------
  |  |   94|  2.79k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE    0xF6F6F6F6
  ------------------
  |  Branch (960:16): [True: 199, False: 2.59k]
  ------------------
  961|    199|        return SAS_SUBHEADER_TYPE_COLUMN_SIZE;
  962|  2.59k|    } else if ((signature & SAS_SUBHEADER_SIGNATURE_64BIT_MASK) != SAS_SUBHEADER_SIGNATURE_64BIT_MASK) {
  ------------------
  |  |  106|  2.59k|#define SAS_SUBHEADER_SIGNATURE_64BIT_MASK     0xFFFFFFFF00000000
  ------------------
                  } else if ((signature & SAS_SUBHEADER_SIGNATURE_64BIT_MASK) != SAS_SUBHEADER_SIGNATURE_64BIT_MASK) {
  ------------------
  |  |  106|  2.59k|#define SAS_SUBHEADER_SIGNATURE_64BIT_MASK     0xFFFFFFFF00000000
  ------------------
  |  Branch (962:16): [True: 772, False: 1.82k]
  ------------------
  963|    772|        return SAS_SUBHEADER_TYPE_DATA;
  964|    772|    }
  965|       |
  966|  1.82k|    uint32_t lower_bytes = (uint32_t)(signature & SAS_SUBHEADER_SIGNATURE_32BIT_MASK);
  ------------------
  |  |  107|  1.82k|#define SAS_SUBHEADER_SIGNATURE_32BIT_MASK     0x00000000FFFFFFFF
  ------------------
  967|  1.82k|    return sas7bdat_parse_subheader_type_32(lower_bytes);
  968|  2.98k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_type_32:
  909|  61.6k|static sas_subheader_type_t sas7bdat_parse_subheader_type_32(uint32_t signature) {
  910|  61.6k|    switch (signature) {
  911|  6.79k|        case SAS_SUBHEADER_SIGNATURE_ROW_SIZE:
  ------------------
  |  |   93|  6.79k|#define SAS_SUBHEADER_SIGNATURE_ROW_SIZE       0xF7F7F7F7
  ------------------
  |  Branch (911:9): [True: 6.79k, False: 54.8k]
  ------------------
  912|  6.79k|            return SAS_SUBHEADER_TYPE_ROW_SIZE;
  913|  4.00k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE:
  ------------------
  |  |   94|  4.00k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE    0xF6F6F6F6
  ------------------
  |  Branch (913:9): [True: 4.00k, False: 57.6k]
  ------------------
  914|  4.00k|            return SAS_SUBHEADER_TYPE_COLUMN_SIZE;
  915|  5.00k|        case SAS_SUBHEADER_SIGNATURE_COUNTS:
  ------------------
  |  |   95|  5.00k|#define SAS_SUBHEADER_SIGNATURE_COUNTS         0xFFFFFC00
  ------------------
  |  Branch (915:9): [True: 5.00k, False: 56.6k]
  ------------------
  916|  5.00k|            return SAS_SUBHEADER_TYPE_COUNTS;
  917|  11.9k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_FORMAT:
  ------------------
  |  |   96|  11.9k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_FORMAT  0xFFFFFBFE
  ------------------
  |  Branch (917:9): [True: 11.9k, False: 49.7k]
  ------------------
  918|  11.9k|            return SAS_SUBHEADER_TYPE_COLUMN_FORMAT;
  919|  6.08k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_ATTRS:
  ------------------
  |  |  101|  6.08k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_ATTRS   0xFFFFFFFC
  ------------------
  |  Branch (919:9): [True: 6.08k, False: 55.5k]
  ------------------
  920|  6.08k|            return SAS_SUBHEADER_TYPE_COLUMN_ATTRS;
  921|  13.2k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_TEXT:
  ------------------
  |  |  102|  13.2k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_TEXT    0xFFFFFFFD
  ------------------
  |  Branch (921:9): [True: 13.2k, False: 48.4k]
  ------------------
  922|  13.2k|            return SAS_SUBHEADER_TYPE_COLUMN_TEXT;
  923|  2.16k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_LIST:
  ------------------
  |  |  103|  2.16k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_LIST    0xFFFFFFFE
  ------------------
  |  Branch (923:9): [True: 2.16k, False: 59.5k]
  ------------------
  924|  2.16k|            return SAS_SUBHEADER_TYPE_COLUMN_LIST;
  925|  6.69k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_NAME:
  ------------------
  |  |  104|  6.69k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_NAME    0xFFFFFFFF
  ------------------
  |  Branch (925:9): [True: 6.69k, False: 54.9k]
  ------------------
  926|  6.69k|            return SAS_SUBHEADER_TYPE_COLUMN_NAME;
  927|  5.72k|        default:
  ------------------
  |  Branch (927:9): [True: 5.72k, False: 55.9k]
  ------------------
  928|  5.72k|            if ((signature & SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) == SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) {
  ------------------
  |  |   98|  5.72k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_MASK    0xFFFFFFF8
  ------------------
                          if ((signature & SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) == SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) {
  ------------------
  |  |   98|  5.72k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_MASK    0xFFFFFFF8
  ------------------
  |  Branch (928:17): [True: 2.33k, False: 3.38k]
  ------------------
  929|  2.33k|                return SAS_SUBHEADER_TYPE_UNKNOWN;
  930|  2.33k|            }
  931|  3.38k|            return SAS_SUBHEADER_TYPE_DATA;
  932|  61.6k|    }
  933|  61.6k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader:
  695|  26.9k|        size_t len, sas7bdat_ctx_t *ctx) {
  696|  26.9k|    readstat_error_t retval = READSTAT_OK;
  697|       |
  698|  26.9k|    if (len < 2 + ctx->subheader_signature_size) {
  ------------------
  |  Branch (698:9): [True: 7, False: 26.9k]
  ------------------
  699|      7|        retval = READSTAT_ERROR_PARSE;
  700|      7|        goto cleanup;
  701|      7|    }
  702|  26.9k|    if (subheader_type == SAS_SUBHEADER_TYPE_ROW_SIZE) {
  ------------------
  |  Branch (702:9): [True: 3.07k, False: 23.9k]
  ------------------
  703|  3.07k|        retval = sas7bdat_parse_row_size_subheader(subheader, len, ctx);
  704|  23.9k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_SIZE) {
  ------------------
  |  Branch (704:16): [True: 1.64k, False: 22.2k]
  ------------------
  705|  1.64k|        retval = sas7bdat_parse_column_size_subheader(subheader, len, ctx);
  706|  22.2k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COUNTS) {
  ------------------
  |  Branch (706:16): [True: 2.01k, False: 20.2k]
  ------------------
  707|       |        /* void */
  708|  20.2k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (708:16): [True: 8.59k, False: 11.6k]
  ------------------
  709|  8.59k|        retval = sas7bdat_parse_column_text_subheader(subheader, len, ctx);
  710|  11.6k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_NAME) {
  ------------------
  |  Branch (710:16): [True: 2.30k, False: 9.35k]
  ------------------
  711|  2.30k|        retval = sas7bdat_parse_column_name_subheader(subheader, len, ctx);
  712|  9.35k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_ATTRS) {
  ------------------
  |  Branch (712:16): [True: 2.43k, False: 6.92k]
  ------------------
  713|  2.43k|        retval = sas7bdat_parse_column_attributes_subheader(subheader, len, ctx);
  714|  6.92k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_FORMAT) {
  ------------------
  |  Branch (714:16): [True: 5.18k, False: 1.73k]
  ------------------
  715|  5.18k|        retval = sas7bdat_parse_column_format_subheader(subheader, len, ctx);
  716|  5.18k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_LIST) {
  ------------------
  |  Branch (716:16): [True: 808, False: 928]
  ------------------
  717|       |        /* void */
  718|    928|    } else if (subheader_type == SAS_SUBHEADER_TYPE_UNKNOWN) {
  ------------------
  |  Branch (718:16): [True: 902, False: 26]
  ------------------
  719|       |        /* void */
  720|    902|    } else {
  721|     26|        retval = READSTAT_ERROR_PARSE;
  722|     26|    }
  723|       |
  724|  26.9k|cleanup:
  725|       |
  726|  26.9k|    return retval;
  727|  26.9k|}
readstat_sas7bdat_read.c:sas7bdat_parse_row_size_subheader:
  245|  3.07k|static readstat_error_t sas7bdat_parse_row_size_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  246|  3.07k|    readstat_error_t retval = READSTAT_OK;
  247|  3.07k|    uint64_t total_row_count;
  248|  3.07k|    uint64_t row_length, deleted_row_count, page_row_count;
  249|       |
  250|  3.07k|    if (len < (ctx->u64 ? 250: 190)) {
  ------------------
  |  Branch (250:9): [True: 10, False: 3.06k]
  |  Branch (250:16): [True: 963, False: 2.11k]
  ------------------
  251|     10|        retval = READSTAT_ERROR_PARSE;
  252|     10|        goto cleanup;
  253|     10|    }
  254|       |
  255|  3.06k|    if (ctx->u64) {
  ------------------
  |  Branch (255:9): [True: 960, False: 2.10k]
  ------------------
  256|    960|        row_length = sas_read8(&subheader[40], ctx->bswap);
  257|    960|        total_row_count = sas_read8(&subheader[48], ctx->bswap);
  258|    960|        deleted_row_count = sas_read8(&subheader[56], ctx->bswap);
  259|    960|        page_row_count = sas_read8(&subheader[120], ctx->bswap);
  260|  2.10k|    } else {
  261|  2.10k|        row_length = sas_read4(&subheader[20], ctx->bswap);
  262|  2.10k|        total_row_count = sas_read4(&subheader[24], ctx->bswap);
  263|  2.10k|        deleted_row_count = sas_read4(&subheader[28], ctx->bswap);
  264|  2.10k|        page_row_count = sas_read4(&subheader[60], ctx->bswap);
  265|  2.10k|    }
  266|       |
  267|  3.06k|    if (deleted_row_count > total_row_count) {
  ------------------
  |  Branch (267:9): [True: 33, False: 3.03k]
  ------------------
  268|     33|        retval = READSTAT_ERROR_PARSE;
  269|     33|        goto cleanup;
  270|     33|    }
  271|  3.03k|    ctx->total_row_count = total_row_count;
  272|  3.03k|    ctx->deleted_row_count = deleted_row_count;
  273|       |
  274|  3.03k|    sas_text_ref_t file_label_ref = sas7bdat_parse_text_ref(&subheader[len-130], ctx);
  275|  3.03k|    if (file_label_ref.length) {
  ------------------
  |  Branch (275:9): [True: 545, False: 2.48k]
  ------------------
  276|    545|        if ((retval = sas7bdat_copy_text_ref(ctx->file_label, sizeof(ctx->file_label),
  ------------------
  |  Branch (276:13): [True: 39, False: 506]
  ------------------
  277|    545|                        file_label_ref, ctx)) != READSTAT_OK) {
  278|     39|            goto cleanup;
  279|     39|        }
  280|    545|    }
  281|       |
  282|  2.99k|    sas_text_ref_t compression_ref = sas7bdat_parse_text_ref(&subheader[len-118], ctx);
  283|  2.99k|    if (compression_ref.length) {
  ------------------
  |  Branch (283:9): [True: 1.00k, False: 1.98k]
  ------------------
  284|  1.00k|        char compression[9];
  285|  1.00k|        if ((retval = sas7bdat_copy_text_ref(compression, sizeof(compression),
  ------------------
  |  Branch (285:13): [True: 38, False: 968]
  ------------------
  286|  1.00k|                        compression_ref, ctx)) != READSTAT_OK) {
  287|     38|            goto cleanup;
  288|     38|        }
  289|    968|        ctx->rdc_compression = (memcmp(compression, SAS_COMPRESSION_SIGNATURE_RDC, 8) == 0);
  ------------------
  |  |  136|    968|#define SAS_COMPRESSION_SIGNATURE_RDC  "SASYZCR2"
  ------------------
  290|    968|    }
  291|       |
  292|  2.95k|    ctx->row_length = row_length;
  293|  2.95k|    ctx->row = readstat_realloc(ctx->row, ctx->row_length);
  294|  2.95k|    if (ctx->row == NULL) {
  ------------------
  |  Branch (294:9): [True: 6, False: 2.94k]
  ------------------
  295|      6|        retval = READSTAT_ERROR_MALLOC;
  296|      6|        goto cleanup;
  297|      6|    }
  298|       |
  299|  2.94k|    ctx->page_row_count = page_row_count;
  300|  2.94k|    uint64_t live_row_count = total_row_count - deleted_row_count;
  301|  2.94k|    uint64_t total_row_count_after_skipping = live_row_count;
  302|  2.94k|    if (live_row_count > ctx->row_offset) {
  ------------------
  |  Branch (302:9): [True: 2.67k, False: 268]
  ------------------
  303|  2.67k|        total_row_count_after_skipping -= ctx->row_offset;
  304|  2.67k|    } else {
  305|    268|        total_row_count_after_skipping = 0;
  306|    268|        ctx->row_offset = live_row_count;
  307|    268|    }
  308|  2.94k|    if (ctx->row_limit == 0 || total_row_count_after_skipping < ctx->row_limit)
  ------------------
  |  Branch (308:9): [True: 1.10k, False: 1.83k]
  |  Branch (308:32): [True: 72, False: 1.76k]
  ------------------
  309|  1.18k|        ctx->row_limit = total_row_count_after_skipping;
  310|       |
  311|  3.07k|cleanup:
  312|  3.07k|    return retval;
  313|  2.94k|}
readstat_sas7bdat_read.c:sas7bdat_parse_text_ref:
  146|  56.5k|static sas_text_ref_t sas7bdat_parse_text_ref(const char *data, sas7bdat_ctx_t *ctx) {
  147|  56.5k|    sas_text_ref_t  ref;
  148|       |
  149|  56.5k|    ref.index = sas_read2(&data[0], ctx->bswap);
  150|  56.5k|    ref.offset = sas_read2(&data[2], ctx->bswap);
  151|  56.5k|    ref.length = sas_read2(&data[4], ctx->bswap);
  152|       |
  153|  56.5k|    return ref;
  154|  56.5k|}
readstat_sas7bdat_read.c:sas7bdat_copy_text_ref:
  156|  9.02M|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) {
  157|  9.02M|    if (text_ref.index >= ctx->text_blob_count)
  ------------------
  |  Branch (157:9): [True: 117, False: 9.02M]
  ------------------
  158|    117|        return READSTAT_ERROR_PARSE;
  159|       |    
  160|  9.02M|    if (text_ref.length == 0) {
  ------------------
  |  Branch (160:9): [True: 9.01M, False: 5.40k]
  ------------------
  161|  9.01M|        out_buffer[0] = '\0';
  162|  9.01M|        return READSTAT_OK;
  163|  9.01M|    }
  164|       |
  165|  5.40k|    char *blob = ctx->text_blobs[text_ref.index];
  166|       |
  167|  5.40k|    if (text_ref.offset + text_ref.length > ctx->text_blob_lengths[text_ref.index])
  ------------------
  |  Branch (167:9): [True: 87, False: 5.31k]
  ------------------
  168|     87|        return READSTAT_ERROR_PARSE;
  169|       |
  170|  5.31k|    return readstat_convert(out_buffer, out_buffer_len, &blob[text_ref.offset], text_ref.length,
  171|  5.31k|            ctx->converter);
  172|  5.40k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_size_subheader:
  217|  1.64k|static readstat_error_t sas7bdat_parse_column_size_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  218|  1.64k|    uint64_t col_count;
  219|  1.64k|    readstat_error_t retval = READSTAT_OK;
  220|       |
  221|  1.64k|    if (ctx->column_count || ctx->did_submit_columns) {
  ------------------
  |  Branch (221:9): [True: 21, False: 1.62k]
  |  Branch (221:30): [True: 2, False: 1.61k]
  ------------------
  222|     23|        retval = READSTAT_ERROR_PARSE;
  223|     23|        goto cleanup;
  224|     23|    }
  225|       |
  226|  1.61k|    if (len < (ctx->u64 ? 16 : 8)) {
  ------------------
  |  Branch (226:9): [True: 3, False: 1.61k]
  |  Branch (226:16): [True: 533, False: 1.08k]
  ------------------
  227|      3|        retval = READSTAT_ERROR_PARSE;
  228|      3|        goto cleanup;
  229|      3|    }
  230|       |
  231|  1.61k|    if (ctx->u64) {
  ------------------
  |  Branch (231:9): [True: 532, False: 1.08k]
  ------------------
  232|    532|        col_count = sas_read8(&subheader[8], ctx->bswap);
  233|  1.08k|    } else {
  234|  1.08k|        col_count = sas_read4(&subheader[4], ctx->bswap);
  235|  1.08k|    }
  236|       |
  237|  1.61k|    ctx->column_count = col_count;
  238|       |
  239|  1.61k|    retval = sas7bdat_realloc_col_info(ctx, ctx->column_count);
  240|       |
  241|  1.64k|cleanup:
  242|  1.64k|    return retval;
  243|  1.61k|}
readstat_sas7bdat_read.c:sas7bdat_realloc_col_info:
  204|  11.4k|static readstat_error_t sas7bdat_realloc_col_info(sas7bdat_ctx_t *ctx, size_t count) {
  205|  11.4k|    if (ctx->col_info_count < count) {
  ------------------
  |  Branch (205:9): [True: 4.52k, False: 6.89k]
  ------------------
  206|  4.52k|        size_t old_count = ctx->col_info_count;
  207|  4.52k|        ctx->col_info_count = count;
  208|  4.52k|        ctx->col_info = readstat_realloc(ctx->col_info, ctx->col_info_count * sizeof(col_info_t));
  209|  4.52k|        if (ctx->col_info == NULL) {
  ------------------
  |  Branch (209:13): [True: 86, False: 4.44k]
  ------------------
  210|     86|            return READSTAT_ERROR_MALLOC;
  211|     86|        }
  212|  4.44k|        memset(ctx->col_info + old_count, 0, (count - old_count) * sizeof(col_info_t));
  213|  4.44k|    }
  214|  11.3k|    return READSTAT_OK;
  215|  11.4k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_text_subheader:
  174|  8.59k|static readstat_error_t sas7bdat_parse_column_text_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  175|  8.59k|    readstat_error_t retval = READSTAT_OK;
  176|  8.59k|    size_t signature_len = ctx->subheader_signature_size;
  177|  8.59k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  178|  8.59k|    char *blob = NULL;
  179|  8.59k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (179:9): [True: 60, False: 8.53k]
  ------------------
  180|     60|        retval = READSTAT_ERROR_PARSE;
  181|     60|        goto cleanup;
  182|     60|    }
  183|  8.53k|    ctx->text_blob_count++;
  184|  8.53k|    ctx->text_blobs = readstat_realloc(ctx->text_blobs, ctx->text_blob_count * sizeof(char *));
  185|  8.53k|    ctx->text_blob_lengths = readstat_realloc(ctx->text_blob_lengths,
  186|  8.53k|            ctx->text_blob_count * sizeof(ctx->text_blob_lengths[0]));
  187|  8.53k|    if (ctx->text_blobs == NULL || ctx->text_blob_lengths == NULL) {
  ------------------
  |  Branch (187:9): [True: 0, False: 8.53k]
  |  Branch (187:36): [True: 0, False: 8.53k]
  ------------------
  188|      0|        retval = READSTAT_ERROR_MALLOC;
  189|      0|        goto cleanup;
  190|      0|    }
  191|       |
  192|  8.53k|    if ((blob = readstat_malloc(len-signature_len)) == NULL) {
  ------------------
  |  Branch (192:9): [True: 0, False: 8.53k]
  ------------------
  193|      0|        retval = READSTAT_ERROR_MALLOC;
  194|      0|        goto cleanup;
  195|      0|    }
  196|  8.53k|    memcpy(blob, subheader+signature_len, len-signature_len);
  197|  8.53k|    ctx->text_blob_lengths[ctx->text_blob_count-1] = len-signature_len;
  198|  8.53k|    ctx->text_blobs[ctx->text_blob_count-1] = blob;
  199|       |
  200|  8.59k|cleanup:
  201|  8.59k|    return retval;
  202|  8.53k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_name_subheader:
  315|  2.30k|static readstat_error_t sas7bdat_parse_column_name_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  316|  2.30k|    readstat_error_t retval = READSTAT_OK;
  317|  2.30k|    size_t signature_len = ctx->subheader_signature_size;
  318|  2.30k|    int cmax = ctx->u64 ? (len-28)/8 : (len-20)/8;
  ------------------
  |  Branch (318:16): [True: 922, False: 1.38k]
  ------------------
  319|  2.30k|    int i;
  320|  2.30k|    const char *cnp = &subheader[signature_len+8];
  321|  2.30k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  322|       |
  323|  2.30k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (323:9): [True: 64, False: 2.24k]
  ------------------
  324|     64|        retval = READSTAT_ERROR_PARSE;
  325|     64|        goto cleanup;
  326|     64|    }
  327|       |
  328|  2.24k|    ctx->col_names_count += cmax;
  329|       |
  330|  2.24k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_names_count)) != READSTAT_OK)
  ------------------
  |  Branch (330:9): [True: 14, False: 2.22k]
  ------------------
  331|     14|        goto cleanup;
  332|       |
  333|  37.1k|    for (i=ctx->col_names_count-cmax; i<ctx->col_names_count; i++) {
  ------------------
  |  Branch (333:39): [True: 34.9k, False: 2.22k]
  ------------------
  334|  34.9k|        ctx->col_info[i].name_ref = sas7bdat_parse_text_ref(cnp, ctx);
  335|  34.9k|        cnp += 8;
  336|  34.9k|    }
  337|       |
  338|  2.30k|cleanup:
  339|       |
  340|  2.30k|    return retval;
  341|  2.22k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_attributes_subheader:
  343|  2.43k|static readstat_error_t sas7bdat_parse_column_attributes_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  344|  2.43k|    readstat_error_t retval = READSTAT_OK;
  345|  2.43k|    size_t signature_len = ctx->subheader_signature_size;
  346|  2.43k|    int cmax = ctx->u64 ? (len-28)/16 : (len-20)/12;
  ------------------
  |  Branch (346:16): [True: 347, False: 2.08k]
  ------------------
  347|  2.43k|    int i;
  348|  2.43k|    const char *cap = &subheader[signature_len+8];
  349|  2.43k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  350|       |
  351|  2.43k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (351:9): [True: 56, False: 2.37k]
  ------------------
  352|     56|        retval = READSTAT_ERROR_PARSE;
  353|     56|        goto cleanup;
  354|     56|    }
  355|  2.37k|    ctx->col_attrs_count += cmax;
  356|  2.37k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_attrs_count)) != READSTAT_OK)
  ------------------
  |  Branch (356:9): [True: 9, False: 2.36k]
  ------------------
  357|      9|        goto cleanup;
  358|       |
  359|  13.6k|    for (i=ctx->col_attrs_count-cmax; i<ctx->col_attrs_count; i++) {
  ------------------
  |  Branch (359:39): [True: 11.2k, False: 2.35k]
  ------------------
  360|  11.2k|        if (ctx->u64) {
  ------------------
  |  Branch (360:13): [True: 823, False: 10.4k]
  ------------------
  361|    823|            ctx->col_info[i].offset = sas_read8(&cap[0], ctx->bswap);
  362|  10.4k|        } else {
  363|  10.4k|            ctx->col_info[i].offset = sas_read4(&cap[0], ctx->bswap);
  364|  10.4k|        }
  365|       |
  366|  11.2k|        readstat_off_t off=4;
  367|  11.2k|        if (ctx->u64)
  ------------------
  |  Branch (367:13): [True: 823, False: 10.4k]
  ------------------
  368|    823|            off=8;
  369|       |
  370|  11.2k|        ctx->col_info[i].width = sas_read4(&cap[off], ctx->bswap);
  371|  11.2k|        if (ctx->col_info[i].width > ctx->max_col_width)
  ------------------
  |  Branch (371:13): [True: 842, False: 10.4k]
  ------------------
  372|    842|            ctx->max_col_width = ctx->col_info[i].width;
  373|       |
  374|  11.2k|        if (cap[off+6] == SAS_COLUMN_TYPE_NUM) {
  ------------------
  |  |   90|  11.2k|#define SAS_COLUMN_TYPE_NUM  0x01
  ------------------
  |  Branch (374:13): [True: 6.03k, False: 5.24k]
  ------------------
  375|  6.03k|            ctx->col_info[i].type = READSTAT_TYPE_DOUBLE;
  376|  6.03k|        } else if (cap[off+6] == SAS_COLUMN_TYPE_CHR) {
  ------------------
  |  |   91|  5.24k|#define SAS_COLUMN_TYPE_CHR  0x02
  ------------------
  |  Branch (376:20): [True: 5.23k, False: 17]
  ------------------
  377|  5.23k|            ctx->col_info[i].type = READSTAT_TYPE_STRING;
  378|  5.23k|        } else {
  379|     17|            retval = READSTAT_ERROR_PARSE;
  380|     17|            goto cleanup;
  381|     17|        }
  382|  11.2k|        ctx->col_info[i].index = i;
  383|  11.2k|        cap += off+8;
  384|  11.2k|    }
  385|       |
  386|  2.43k|cleanup:
  387|       |
  388|  2.43k|    return retval;
  389|  2.36k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_format_subheader:
  391|  5.18k|static readstat_error_t sas7bdat_parse_column_format_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  392|  5.18k|    readstat_error_t retval = READSTAT_OK;
  393|       |
  394|  5.18k|    if (len < (ctx->u64 ? 58 : 46)) {
  ------------------
  |  Branch (394:9): [True: 2, False: 5.18k]
  |  Branch (394:16): [True: 377, False: 4.81k]
  ------------------
  395|      2|        retval = READSTAT_ERROR_PARSE;
  396|      2|        goto cleanup;
  397|      2|    }
  398|       |
  399|  5.18k|    ctx->col_formats_count++;
  400|  5.18k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_formats_count)) != READSTAT_OK)
  ------------------
  |  Branch (400:9): [True: 0, False: 5.18k]
  ------------------
  401|      0|        goto cleanup;
  402|       |
  403|  5.18k|    col_info_t *col_info = &ctx->col_info[ctx->col_formats_count-1];
  404|  5.18k|    if (ctx->u64) {
  ------------------
  |  Branch (404:9): [True: 377, False: 4.81k]
  ------------------
  405|    377|        col_info->format_width = sas_read2(&subheader[24], ctx->bswap);
  406|    377|        col_info->format_digits = sas_read2(&subheader[26], ctx->bswap);
  407|    377|        col_info->informat_width = sas_read2(&subheader[28], ctx->bswap);
  408|    377|        col_info->informat_digits = sas_read2(&subheader[30], ctx->bswap);
  409|  4.81k|    } else {
  410|  4.81k|        col_info->format_width = sas_read2(&subheader[12], ctx->bswap);
  411|  4.81k|        col_info->format_digits = sas_read2(&subheader[14], ctx->bswap);
  412|  4.81k|        col_info->informat_width = sas_read2(&subheader[16], ctx->bswap);
  413|  4.81k|        col_info->informat_digits = sas_read2(&subheader[18], ctx->bswap);
  414|  4.81k|    }
  415|  5.18k|    col_info->informat_ref = sas7bdat_parse_text_ref(
  416|  5.18k|            ctx->u64 ? &subheader[40] : &subheader[28], ctx);
  ------------------
  |  Branch (416:13): [True: 377, False: 4.81k]
  ------------------
  417|  5.18k|    col_info->format_ref = sas7bdat_parse_text_ref(
  418|  5.18k|            ctx->u64 ? &subheader[46] : &subheader[34], ctx);
  ------------------
  |  Branch (418:13): [True: 377, False: 4.81k]
  ------------------
  419|  5.18k|    col_info->label_ref = sas7bdat_parse_text_ref(
  420|  5.18k|            ctx->u64 ? &subheader[52] : &subheader[40], ctx);
  ------------------
  |  Branch (420:13): [True: 377, False: 4.81k]
  ------------------
  421|       |
  422|  5.18k|cleanup:
  423|  5.18k|    return retval;
  424|  5.18k|}
readstat_sas7bdat_read.c:sas7bdat_is_moved_row:
 1016|  4.71k|static int sas7bdat_is_moved_row(unsigned char compression) {
 1017|  4.71k|    return compression == SAS_COMPRESSION_ROW_MOVED
  ------------------
  |  |  131|  9.43k|#define SAS_COMPRESSION_ROW_MOVED          0x06
  ------------------
  |  Branch (1017:12): [True: 1.16k, False: 3.54k]
  ------------------
 1018|  3.54k|        || compression == SAS_COMPRESSION_NONE_MOVED
  ------------------
  |  |  127|  8.26k|#define SAS_COMPRESSION_NONE_MOVED         0x02
  ------------------
  |  Branch (1018:12): [True: 960, False: 2.58k]
  ------------------
 1019|  2.58k|        || compression == SAS_COMPRESSION_ROW_UNREFERENCED
  ------------------
  |  |  133|  7.30k|#define SAS_COMPRESSION_ROW_UNREFERENCED   0x0d
  ------------------
  |  Branch (1019:12): [True: 1.46k, False: 1.12k]
  ------------------
 1020|  1.12k|        || compression == SAS_COMPRESSION_NONE_UNREFERENCED;
  ------------------
  |  |  132|  1.12k|#define SAS_COMPRESSION_NONE_UNREFERENCED  0x09
  ------------------
  |  Branch (1020:12): [True: 1.07k, False: 45]
  ------------------
 1021|  4.71k|}
readstat_sas7bdat_read.c:sas7bdat_parse_amd_pages_pass1:
 1383|  3.72k|static readstat_error_t sas7bdat_parse_amd_pages_pass1(int64_t last_examined_page_pass1, sas7bdat_ctx_t *ctx) {
 1384|  3.72k|    readstat_error_t retval = READSTAT_OK;
 1385|  3.72k|    readstat_io_t *io = ctx->io;
 1386|  3.72k|    uint64_t i;
 1387|  3.72k|    uint64_t amd_page_count = 0;
 1388|       |
 1389|       |    /* ...then AMD pages at the end */
 1390|  7.64k|    for (i=ctx->page_count-1; i>last_examined_page_pass1; i--) {
  ------------------
  |  Branch (1390:31): [True: 5.75k, False: 1.89k]
  ------------------
 1391|  5.75k|        if (io->seek(ctx->header_size + i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1391:13): [True: 276, False: 5.48k]
  ------------------
 1392|    276|            retval = READSTAT_ERROR_SEEK;
 1393|    276|            if (ctx->handle.error) {
  ------------------
  |  Branch (1393:17): [True: 0, False: 276]
  ------------------
 1394|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64 
 1395|      0|                        " (= %" PRId64 " + %" PRId64 "*%" PRId64 ")",
 1396|      0|                        ctx->header_size + i*ctx->page_size, ctx->header_size, i, ctx->page_size);
 1397|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1398|      0|            }
 1399|    276|            goto cleanup;
 1400|    276|        }
 1401|       |
 1402|  5.48k|        readstat_off_t off = 0;
 1403|  5.48k|        if (ctx->u64)
  ------------------
  |  Branch (1403:13): [True: 4.76k, False: 718]
  ------------------
 1404|  4.76k|            off = 16;
 1405|       |
 1406|  5.48k|        size_t head_len = off + 16 + 2;
 1407|  5.48k|        size_t tail_len = ctx->page_size - head_len;
 1408|       |
 1409|  5.48k|        if (io->read(ctx->page, head_len, io->io_ctx) < head_len) {
  ------------------
  |  Branch (1409:13): [True: 10, False: 5.47k]
  ------------------
 1410|     10|            retval = READSTAT_ERROR_READ;
 1411|     10|            goto cleanup;
 1412|     10|        }
 1413|       |
 1414|  5.47k|        uint16_t page_type = sas_read2(&ctx->page[off+16], ctx->bswap);
 1415|       |
 1416|  5.47k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  113|  5.47k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  110|  5.47k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1416:13): [True: 1.18k, False: 4.28k]
  ------------------
 1417|       |            /* Usually AMD pages are at the end but sometimes data pages appear after them */
 1418|  1.18k|            if (amd_page_count > 0)
  ------------------
  |  Branch (1418:17): [True: 668, False: 521]
  ------------------
 1419|    668|                break;
 1420|    521|            continue;
 1421|  1.18k|        }
 1422|  4.28k|        if ((page_type & SAS_PAGE_TYPE_COMP))
  ------------------
  |  |  117|  4.28k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1422:13): [True: 815, False: 3.46k]
  ------------------
 1423|    815|            continue;
 1424|       |
 1425|  3.46k|        if (io->read(ctx->page + head_len, tail_len, io->io_ctx) < tail_len) {
  ------------------
  |  Branch (1425:13): [True: 18, False: 3.44k]
  ------------------
 1426|     18|            retval = READSTAT_ERROR_READ;
 1427|     18|            goto cleanup;
 1428|     18|        }
 1429|       |
 1430|  3.44k|        if ((retval = sas7bdat_parse_page_pass1(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1430:13): [True: 858, False: 2.59k]
  ------------------
 1431|    858|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1431:17): [True: 0, False: 858]
  |  Branch (1431:38): [True: 0, False: 0]
  ------------------
 1432|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1433|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1434|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1435|      0|                        i, pos - ctx->page_size, pos-1);
 1436|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1437|      0|            }
 1438|    858|            goto cleanup;
 1439|    858|        }
 1440|       |
 1441|  2.59k|        amd_page_count++;
 1442|  2.59k|    }
 1443|       |
 1444|  3.72k|cleanup:
 1445|       |
 1446|  3.72k|    return retval;
 1447|  3.72k|}
readstat_sas7bdat_read.c:sas7bdat_parse_all_pages_pass2:
 1449|  2.55k|static readstat_error_t sas7bdat_parse_all_pages_pass2(sas7bdat_ctx_t *ctx) {
 1450|  2.55k|    readstat_error_t retval = READSTAT_OK;
 1451|  2.55k|    readstat_io_t *io = ctx->io;
 1452|  2.55k|    int64_t i;
 1453|       |
 1454|  7.61k|    for (i=0; i<ctx->page_count; i++) {
  ------------------
  |  Branch (1454:15): [True: 7.27k, False: 343]
  ------------------
 1455|  7.27k|        if ((retval = sas7bdat_update_progress(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1455:13): [True: 0, False: 7.27k]
  ------------------
 1456|      0|            goto cleanup;
 1457|      0|        }
 1458|  7.27k|        if (io->read(ctx->page, ctx->page_size, io->io_ctx) < ctx->page_size) {
  ------------------
  |  Branch (1458:13): [True: 160, False: 7.11k]
  ------------------
 1459|    160|            retval = READSTAT_ERROR_READ;
 1460|    160|            goto cleanup;
 1461|    160|        }
 1462|       |
 1463|  7.11k|        if ((retval = sas7bdat_parse_page_pass2(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1463:13): [True: 2.04k, False: 5.06k]
  ------------------
 1464|  2.04k|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1464:17): [True: 0, False: 2.04k]
  |  Branch (1464:38): [True: 0, False: 0]
  ------------------
 1465|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1466|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1467|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1468|      0|                        i, pos - ctx->page_size, pos-1);
 1469|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1470|      0|            }
 1471|  2.04k|            goto cleanup;
 1472|  2.04k|        }
 1473|  5.06k|        if (ctx->row_limit > 0 && ctx->parsed_row_count == ctx->row_limit)
  ------------------
  |  Branch (1473:13): [True: 852, False: 4.21k]
  |  Branch (1473:35): [True: 12, False: 840]
  ------------------
 1474|     12|            break;
 1475|  5.06k|    }
 1476|  2.55k|cleanup:
 1477|       |
 1478|  2.55k|    return retval;
 1479|  2.55k|}
readstat_sas7bdat_read.c:sas7bdat_parse_page_pass2:
 1204|  7.11k|static readstat_error_t sas7bdat_parse_page_pass2(const char *page, size_t page_size, sas7bdat_ctx_t *ctx) {
 1205|  7.11k|    uint16_t page_type;
 1206|       |
 1207|  7.11k|    readstat_error_t retval = READSTAT_OK;
 1208|       |
 1209|  7.11k|    page_type = sas_read2(&page[ctx->page_header_size-8], ctx->bswap);
 1210|       |
 1211|  7.11k|    const char *data = NULL;
 1212|       |
 1213|  7.11k|    if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  113|  7.11k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                  if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  110|  7.11k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1213:9): [True: 898, False: 6.21k]
  ------------------
 1214|    898|        ctx->page_row_count = sas_read2(&page[ctx->page_header_size-6], ctx->bswap);
 1215|    898|        data = &page[ctx->page_header_size];
 1216|  6.21k|    } else if (!(page_type & SAS_PAGE_TYPE_COMP)) {
  ------------------
  |  |  117|  6.21k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1216:16): [True: 5.45k, False: 761]
  ------------------
 1217|  5.45k|        uint16_t subheader_count = sas_read2(&page[ctx->page_header_size-4], ctx->bswap);
 1218|       |
 1219|  5.45k|        int i;
 1220|  5.45k|        const char *shp = &page[ctx->page_header_size];
 1221|  5.45k|        int lshp = ctx->subheader_pointer_size;
 1222|       |
 1223|  5.45k|        if (ctx->page_header_size + subheader_count*lshp > page_size) {
  ------------------
  |  Branch (1223:13): [True: 30, False: 5.42k]
  ------------------
 1224|     30|            retval = READSTAT_ERROR_PARSE;
 1225|     30|            goto cleanup;
 1226|     30|        }
 1227|       |
 1228|  55.7k|        for (i=0; i<subheader_count; i++) {
  ------------------
  |  Branch (1228:19): [True: 51.9k, False: 3.80k]
  ------------------
 1229|  51.9k|            subheader_pointer_t shp_info = { 0 };
 1230|  51.9k|            if ((retval = sas7bdat_parse_subheader_pointer(shp, page + page_size - shp, &shp_info, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1230:17): [True: 0, False: 51.9k]
  ------------------
 1231|      0|                goto cleanup;
 1232|      0|            }
 1233|  51.9k|            if (shp_info.len > 0 && shp_info.compression == SAS_COMPRESSION_REFERENCE) {
  ------------------
  |  |  128|  33.5k|#define SAS_COMPRESSION_REFERENCE          0x03
  ------------------
  |  Branch (1233:17): [True: 33.5k, False: 18.3k]
  |  Branch (1233:37): [True: 821, False: 32.7k]
  ------------------
 1234|    821|                uint64_t page_index = shp_info.offset - 1;
 1235|    821|                uint64_t subheader_index = shp_info.len - 1;
 1236|    821|                if ((retval = sas7bdat_parse_moved_row(page_index, subheader_index, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1236:21): [True: 590, False: 231]
  ------------------
 1237|    590|                    goto cleanup;
 1238|    590|                }
 1239|  51.0k|            } else if (shp_info.len > 0 && shp_info.compression != SAS_COMPRESSION_TRUNC) {
  ------------------
  |  |  126|  32.7k|#define SAS_COMPRESSION_TRUNC              0x01
  ------------------
  |  Branch (1239:24): [True: 32.7k, False: 18.3k]
  |  Branch (1239:44): [True: 27.8k, False: 4.91k]
  ------------------
 1240|  27.8k|                if ((retval = sas7bdat_validate_subheader_pointer(&shp_info, page_size, subheader_count, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1240:21): [True: 210, False: 27.6k]
  ------------------
 1241|    210|                    goto cleanup;
 1242|    210|                }
 1243|  27.6k|                if (shp_info.compression == SAS_COMPRESSION_NONE) {
  ------------------
  |  |  125|  27.6k|#define SAS_COMPRESSION_NONE               0x00
  ------------------
  |  Branch (1243:21): [True: 23.5k, False: 4.03k]
  ------------------
 1244|  23.5k|                    sas_subheader_type_t subheader_type = sas7bdat_parse_subheader_type(page + shp_info.offset, ctx);
 1245|  23.5k|                    if (shp_info.is_compressed_data && subheader_type == SAS_SUBHEADER_TYPE_DATA) {
  ------------------
  |  Branch (1245:25): [True: 15.8k, False: 7.76k]
  |  Branch (1245:56): [True: 550, False: 15.2k]
  ------------------
 1246|    550|                        if (shp_info.len != ctx->row_length) {
  ------------------
  |  Branch (1246:29): [True: 81, False: 469]
  ------------------
 1247|     81|                            retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
 1248|     81|                            goto cleanup;
 1249|     81|                        }
 1250|    469|                        if ((retval = sas7bdat_submit_columns_if_needed(ctx, 1)) != READSTAT_OK) {
  ------------------
  |  Branch (1250:29): [True: 3, False: 466]
  ------------------
 1251|      3|                            goto cleanup;
 1252|      3|                        }
 1253|    466|                        if ((retval = sas7bdat_parse_single_row(page + shp_info.offset, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1253:29): [True: 8, False: 458]
  ------------------
 1254|      8|                            goto cleanup;
 1255|      8|                        }
 1256|  23.0k|                    } else {
 1257|  23.0k|                        if (subheader_type != SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (1257:29): [True: 18.3k, False: 4.65k]
  ------------------
 1258|  18.3k|                            if ((retval = sas7bdat_parse_subheader(subheader_type, page + shp_info.offset, shp_info.len, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1258:33): [True: 407, False: 17.9k]
  ------------------
 1259|    407|                                goto cleanup;
 1260|    407|                            }
 1261|  18.3k|                        }
 1262|  23.0k|                    }
 1263|  23.5k|                } else if (shp_info.compression == SAS_COMPRESSION_ROW) {
  ------------------
  |  |  129|  4.03k|#define SAS_COMPRESSION_ROW                0x04
  ------------------
  |  Branch (1263:28): [True: 2.02k, False: 2.00k]
  ------------------
 1264|  2.02k|                    if ((retval = sas7bdat_submit_columns_if_needed(ctx, 1)) != READSTAT_OK) {
  ------------------
  |  Branch (1264:25): [True: 37, False: 1.99k]
  ------------------
 1265|     37|                        goto cleanup;
 1266|     37|                    }
 1267|  1.99k|                    if ((retval = sas7bdat_parse_subheader_compressed(page + shp_info.offset, shp_info.len, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1267:25): [True: 267, False: 1.72k]
  ------------------
 1268|    267|                        goto cleanup;
 1269|    267|                    }
 1270|  2.00k|                } else if (shp_info.compression == SAS_COMPRESSION_DELETED_ROW) {
  ------------------
  |  |  130|  2.00k|#define SAS_COMPRESSION_DELETED_ROW        0x05
  ------------------
  |  Branch (1270:28): [True: 196, False: 1.80k]
  ------------------
 1271|    196|                    if ((retval = sas7bdat_register_deleted_row(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1271:25): [True: 2, False: 194]
  ------------------
 1272|      2|                        goto cleanup;
 1273|      2|                    }
 1274|  1.80k|                } else if (sas7bdat_is_moved_row(shp_info.compression)) {
  ------------------
  |  Branch (1274:28): [True: 1.79k, False: 12]
  ------------------
 1275|       |                    /* void */
 1276|  1.79k|                } else {
 1277|     12|                    retval = READSTAT_ERROR_UNSUPPORTED_COMPRESSION;
 1278|     12|                    goto cleanup;
 1279|     12|                }
 1280|  27.6k|            }
 1281|       |
 1282|  50.2k|            shp += lshp;
 1283|  50.2k|        }
 1284|       |
 1285|  3.80k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_MIX) {
  ------------------
  |  |  113|  3.80k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_MIX) {
  ------------------
  |  |  111|  3.80k|#define SAS_PAGE_TYPE_MIX    0x0200
  ------------------
  |  Branch (1285:13): [True: 2.00k, False: 1.79k]
  ------------------
 1286|       |            /* HACK - this is supposed to obey 8-byte boundaries but
 1287|       |             * some files created by Stat/Transfer don't. So verify that the
 1288|       |             * padding is { 0, 0, 0, 0 } or { ' ', ' ', ' ', ' ' } (or that
 1289|       |             * the file is not from Stat/Transfer) before skipping it */
 1290|  2.00k|            if ((shp-page)%8 == 4 && shp + 4 <= page + page_size &&
  ------------------
  |  Branch (1290:17): [True: 361, False: 1.64k]
  |  Branch (1290:38): [True: 358, False: 3]
  ------------------
 1291|    358|                    (*(uint32_t *)shp == 0x00000000 ||
  ------------------
  |  Branch (1291:22): [True: 85, False: 273]
  ------------------
 1292|    273|                     *(uint32_t *)shp == 0x20202020 ||
  ------------------
  |  Branch (1292:22): [True: 40, False: 233]
  ------------------
 1293|    325|                     ctx->vendor != READSTAT_VENDOR_STAT_TRANSFER)) {
  ------------------
  |  Branch (1293:22): [True: 200, False: 33]
  ------------------
 1294|    325|                data = shp + 4;
 1295|  1.68k|            } else {
 1296|  1.68k|                data = shp;
 1297|  1.68k|            }
 1298|  2.00k|        }
 1299|  3.80k|    }
 1300|  5.46k|    if (data) {
  ------------------
  |  Branch (1300:9): [True: 2.90k, False: 2.56k]
  ------------------
 1301|  2.90k|        if ((retval = sas7bdat_submit_columns_if_needed(ctx, 0)) != READSTAT_OK) {
  ------------------
  |  Branch (1301:13): [True: 101, False: 2.80k]
  ------------------
 1302|    101|            goto cleanup;
 1303|    101|        }
 1304|  2.80k|        if (ctx->handle.value) {
  ------------------
  |  Branch (1304:13): [True: 2.80k, False: 0]
  ------------------
 1305|  2.80k|            const unsigned char *deleted_row_bitmap = NULL;
 1306|  2.80k|            uint32_t deleted_row_bitmap_row_count = 0;
 1307|  2.80k|            if (page_type & SAS_PAGE_TYPE_DELETED_ROWS) {
  ------------------
  |  |  115|  2.80k|#define SAS_PAGE_TYPE_DELETED_ROWS  0x0080
  ------------------
  |  Branch (1307:17): [True: 710, False: 2.09k]
  ------------------
 1308|    710|                if ((retval = sas7bdat_parse_deleted_row_bitmap(page, data, page_size,
  ------------------
  |  Branch (1308:21): [True: 119, False: 591]
  ------------------
 1309|    710|                        &deleted_row_bitmap, &deleted_row_bitmap_row_count, ctx)) != READSTAT_OK) {
 1310|    119|                    goto cleanup;
 1311|    119|                }
 1312|    710|            }
 1313|  2.68k|            retval = sas7bdat_parse_rows(data, page + page_size - data,
 1314|  2.68k|                    deleted_row_bitmap, deleted_row_bitmap_row_count, ctx);
 1315|  2.68k|        }
 1316|  2.80k|    } 
 1317|  7.11k|cleanup:
 1318|       |
 1319|  7.11k|    return retval;
 1320|  5.46k|}
readstat_sas7bdat_read.c:sas7bdat_parse_moved_row:
 1093|    821|static readstat_error_t sas7bdat_parse_moved_row(uint64_t page_index, uint64_t subheader_index, sas7bdat_ctx_t *ctx) {
 1094|    821|    readstat_error_t retval = READSTAT_OK;
 1095|    821|    readstat_io_t *io = ctx->io;
 1096|       |
 1097|    821|    const uint64_t page_size = ctx->page_size;
 1098|    821|    int64_t saved_pos = -1;
 1099|       |
 1100|    821|    if (page_index >= ctx->page_count) {
  ------------------
  |  Branch (1100:9): [True: 101, False: 720]
  ------------------
 1101|    101|        retval = READSTAT_ERROR_PARSE;
 1102|    101|        goto cleanup;
 1103|    101|    }
 1104|       |
 1105|    720|    if (ctx->moved_page == NULL) {
  ------------------
  |  Branch (1105:9): [True: 513, False: 207]
  ------------------
 1106|    513|        if ((ctx->moved_page = readstat_malloc(page_size)) == NULL) {
  ------------------
  |  Branch (1106:13): [True: 0, False: 513]
  ------------------
 1107|      0|            retval = READSTAT_ERROR_MALLOC;
 1108|      0|            goto cleanup;
 1109|      0|        }
 1110|    513|        ctx->moved_page_index = -1;
 1111|    513|    }
 1112|    720|    char *page = ctx->moved_page;
 1113|       |
 1114|    720|    if (ctx->moved_page_index != (int64_t)page_index) {
  ------------------
  |  Branch (1114:9): [True: 562, False: 158]
  ------------------
 1115|    562|        saved_pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1116|    562|        if (saved_pos == -1) {
  ------------------
  |  Branch (1116:13): [True: 0, False: 562]
  ------------------
 1117|      0|            retval = READSTAT_ERROR_SEEK;
 1118|      0|            goto cleanup;
 1119|      0|        }
 1120|    562|        if (io->seek(ctx->header_size + page_index * page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1120:13): [True: 84, False: 478]
  ------------------
 1121|     84|            retval = READSTAT_ERROR_SEEK;
 1122|     84|            if (ctx->handle.error) {
  ------------------
  |  Branch (1122:17): [True: 0, False: 84]
  ------------------
 1123|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64
 1124|      0|                        " (= %" PRId64 " + %" PRId64 "*%" PRId64 ")",
 1125|      0|                        ctx->header_size + page_index * page_size, ctx->header_size, page_index, page_size);
 1126|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1127|      0|            }
 1128|     84|            goto cleanup;
 1129|     84|        }
 1130|    478|        ssize_t bytes_read = io->read(page, page_size, io->io_ctx);
 1131|    478|        if (bytes_read < 0 || (uint64_t)bytes_read < page_size) {
  ------------------
  |  Branch (1131:13): [True: 0, False: 478]
  |  Branch (1131:31): [True: 6, False: 472]
  ------------------
 1132|      6|            retval = READSTAT_ERROR_READ;
 1133|      6|            goto cleanup;
 1134|      6|        }
 1135|    472|        ctx->moved_page_index = (int64_t)page_index;
 1136|    472|    }
 1137|       |
 1138|    630|    uint16_t page_type = sas_read2(&page[ctx->page_header_size - 8], ctx->bswap);
 1139|    630|    if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA || page_type & SAS_PAGE_TYPE_COMP) {
  ------------------
  |  |  113|    630|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                  if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA || page_type & SAS_PAGE_TYPE_COMP) {
  ------------------
  |  |  110|  1.26k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
                  if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA || page_type & SAS_PAGE_TYPE_COMP) {
  ------------------
  |  |  117|    628|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1139:9): [True: 2, False: 628]
  |  Branch (1139:67): [True: 2, False: 626]
  ------------------
 1140|      4|        retval = READSTAT_ERROR_READ;
 1141|      4|        goto cleanup;
 1142|      4|    }
 1143|    626|    uint16_t subheader_count = sas_read2(&page[ctx->page_header_size - 4], ctx->bswap);
 1144|    626|    if (subheader_index >= subheader_count) {
  ------------------
  |  Branch (1144:9): [True: 107, False: 519]
  ------------------
 1145|    107|        retval = READSTAT_ERROR_READ;
 1146|    107|        goto cleanup;
 1147|    107|    }
 1148|    519|    uint64_t shp_offset = ctx->page_header_size + subheader_index * ctx->subheader_pointer_size;
 1149|    519|    if (shp_offset + ctx->subheader_pointer_size > page_size) {
  ------------------
  |  Branch (1149:9): [True: 22, False: 497]
  ------------------
 1150|     22|        retval = READSTAT_ERROR_READ;
 1151|     22|        goto cleanup;
 1152|     22|    }
 1153|       |
 1154|    497|    const char *shp = &page[shp_offset];
 1155|    497|    subheader_pointer_t shp_info = { 0 };
 1156|    497|    if ((retval = sas7bdat_parse_subheader_pointer(shp, page + page_size - shp, &shp_info, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1156:9): [True: 0, False: 497]
  ------------------
 1157|      0|        goto cleanup;
 1158|      0|    }
 1159|    497|    if ((retval = sas7bdat_validate_subheader_pointer(&shp_info, page_size, subheader_count, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1159:9): [True: 214, False: 283]
  ------------------
 1160|    214|        goto cleanup;
 1161|    214|    }
 1162|    283|    if ((retval = sas7bdat_submit_columns_if_needed(ctx, 1)) != READSTAT_OK) {
  ------------------
  |  Branch (1162:9): [True: 3, False: 280]
  ------------------
 1163|      3|        goto cleanup;
 1164|      3|    }
 1165|       |
 1166|    280|    if (shp_info.compression == SAS_COMPRESSION_NONE_MOVED) {
  ------------------
  |  |  127|    280|#define SAS_COMPRESSION_NONE_MOVED         0x02
  ------------------
  |  Branch (1166:9): [True: 46, False: 234]
  ------------------
 1167|     46|        sas_subheader_type_t subheader_type = sas7bdat_parse_subheader_type(page + shp_info.offset, ctx);
 1168|     46|        if (!shp_info.is_compressed_data || subheader_type != SAS_SUBHEADER_TYPE_DATA) {
  ------------------
  |  Branch (1168:13): [True: 2, False: 44]
  |  Branch (1168:45): [True: 3, False: 41]
  ------------------
 1169|      5|            retval = READSTAT_ERROR_READ;
 1170|      5|            goto cleanup;
 1171|      5|        }
 1172|     41|        if (shp_info.len != ctx->row_length) {
  ------------------
  |  Branch (1172:13): [True: 28, False: 13]
  ------------------
 1173|     28|            retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
 1174|     28|            goto cleanup;
 1175|     28|        }
 1176|     13|        if ((retval = sas7bdat_parse_single_row(page + shp_info.offset, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1176:13): [True: 0, False: 13]
  ------------------
 1177|      0|            goto cleanup;
 1178|      0|        }
 1179|    234|    } else if (shp_info.compression == SAS_COMPRESSION_ROW_MOVED) {
  ------------------
  |  |  131|    234|#define SAS_COMPRESSION_ROW_MOVED          0x06
  ------------------
  |  Branch (1179:16): [True: 218, False: 16]
  ------------------
 1180|    218|        if ((retval = sas7bdat_parse_subheader_compressed(page + shp_info.offset, shp_info.len, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1180:13): [True: 0, False: 218]
  ------------------
 1181|      0|            goto cleanup;
 1182|      0|        }
 1183|    218|    } else {
 1184|     16|        retval = READSTAT_ERROR_UNSUPPORTED_COMPRESSION;
 1185|     16|        goto cleanup;
 1186|     16|    }
 1187|       |
 1188|    821|cleanup:
 1189|       |
 1190|    821|    if (saved_pos != -1) {
  ------------------
  |  Branch (1190:9): [True: 562, False: 259]
  ------------------
 1191|    562|        if (io->seek(saved_pos, READSTAT_SEEK_SET, io->io_ctx) == -1 && retval == READSTAT_OK) {
  ------------------
  |  Branch (1191:13): [True: 0, False: 562]
  |  Branch (1191:73): [True: 0, False: 0]
  ------------------
 1192|      0|            retval = READSTAT_ERROR_SEEK;
 1193|      0|            if (ctx->handle.error) {
  ------------------
  |  Branch (1193:17): [True: 0, False: 0]
  ------------------
 1194|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf),
 1195|      0|                        "ReadStat: Failed to seek to position %" PRId64, saved_pos);
 1196|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1197|      0|            }
 1198|      0|        }
 1199|    562|    }
 1200|       |
 1201|    821|    return retval;
 1202|    280|}
readstat_sas7bdat_read.c:sas7bdat_parse_single_row:
  498|   206k|static readstat_error_t sas7bdat_parse_single_row(const char *data, sas7bdat_ctx_t *ctx) {
  499|   206k|    if (ctx->parsed_row_count == ctx->row_limit)
  ------------------
  |  Branch (499:9): [True: 290, False: 206k]
  ------------------
  500|    290|        return READSTAT_OK;
  501|   206k|    if (ctx->row_offset) {
  ------------------
  |  Branch (501:9): [True: 0, False: 206k]
  ------------------
  502|      0|        ctx->row_offset--;
  503|      0|        return READSTAT_OK;
  504|      0|    }
  505|       |
  506|   206k|    readstat_error_t retval = READSTAT_OK;
  507|   206k|    int j;
  508|   206k|    if (ctx->handle.value) {
  ------------------
  |  Branch (508:9): [True: 206k, False: 0]
  ------------------
  509|   206k|        ctx->scratch_buffer_len = 4*ctx->max_col_width+1;
  510|   206k|        ctx->scratch_buffer = readstat_realloc(ctx->scratch_buffer, ctx->scratch_buffer_len);
  511|   206k|        if (ctx->scratch_buffer == NULL) {
  ------------------
  |  Branch (511:13): [True: 4, False: 206k]
  ------------------
  512|      4|            retval = READSTAT_ERROR_MALLOC;
  513|      4|            goto cleanup;
  514|      4|        }
  515|       |
  516|  21.6M|        for (j=0; j<ctx->column_count; j++) {
  ------------------
  |  Branch (516:19): [True: 21.4M, False: 206k]
  ------------------
  517|  21.4M|            col_info_t *col_info = &ctx->col_info[j];
  518|  21.4M|            readstat_variable_t *variable = ctx->variables[j];
  519|  21.4M|            if (variable->skip)
  ------------------
  |  Branch (519:17): [True: 0, False: 21.4M]
  ------------------
  520|      0|                continue;
  521|       |
  522|  21.4M|            if (col_info->offset > ctx->row_length || col_info->offset + col_info->width > ctx->row_length) {
  ------------------
  |  Branch (522:17): [True: 86, False: 21.4M]
  |  Branch (522:55): [True: 21, False: 21.4M]
  ------------------
  523|    107|                retval = READSTAT_ERROR_PARSE;
  524|    107|                goto cleanup;
  525|    107|            }
  526|  21.4M|            retval = sas7bdat_handle_data_value(variable, col_info, &data[col_info->offset], ctx);
  527|  21.4M|            if (retval != READSTAT_OK) {
  ------------------
  |  Branch (527:17): [True: 13, False: 21.4M]
  ------------------
  528|     13|                goto cleanup;
  529|     13|            }
  530|  21.4M|        }
  531|   206k|    }
  532|   206k|    ctx->parsed_row_count++;
  533|       |
  534|   206k|cleanup:
  535|   206k|    return retval;
  536|   206k|}
readstat_sas7bdat_read.c:sas7bdat_handle_data_value:
  435|  21.4M|        col_info_t *col_info, const char *col_data, sas7bdat_ctx_t *ctx) {
  436|  21.4M|    readstat_error_t retval = READSTAT_OK;
  437|  21.4M|    int cb_retval = 0;
  438|  21.4M|    readstat_value_t value;
  439|  21.4M|    memset(&value, 0, sizeof(readstat_value_t));
  440|       |
  441|  21.4M|    value.type = col_info->type;
  442|       |
  443|  21.4M|    if (col_info->type == READSTAT_TYPE_STRING) {
  ------------------
  |  Branch (443:9): [True: 21.3M, False: 102k]
  ------------------
  444|  21.3M|        retval = readstat_convert(ctx->scratch_buffer, ctx->scratch_buffer_len,
  445|  21.3M|                col_data, col_info->width, ctx->converter);
  446|  21.3M|        if (retval != READSTAT_OK) {
  ------------------
  |  Branch (446:13): [True: 9, False: 21.3M]
  ------------------
  447|      9|            if (ctx->handle.error) {
  ------------------
  |  Branch (447:17): [True: 0, False: 9]
  ------------------
  448|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf),
  449|      0|                        "ReadStat: Error converting string (row=%u, col=%u) to specified encoding: %.*s",
  450|      0|                        ctx->parsed_row_count+1, col_info->index+1, col_info->width, col_data);
  451|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  452|      0|            }
  453|      9|            goto cleanup;
  454|      9|        }
  455|       |
  456|  21.3M|        value.v.string_value = ctx->scratch_buffer;
  457|  21.3M|    } else if (col_info->type == READSTAT_TYPE_DOUBLE) {
  ------------------
  |  Branch (457:16): [True: 102k, False: 0]
  ------------------
  458|   102k|        uint64_t  val = 0;
  459|   102k|        double dval = NAN;
  460|   102k|        if (col_info->width < 1 || col_info->width > 8) {
  ------------------
  |  Branch (460:13): [True: 1, False: 102k]
  |  Branch (460:36): [True: 3, False: 102k]
  ------------------
  461|       |            /* A DOUBLE is 3-8 bytes on disk, but col_info can be mutated by a
  462|       |             * COLUMN_ATTRS subheader after the columns were validated. Guard
  463|       |             * here so a corrupt width can't produce an undefined shift below. */
  464|      4|            retval = READSTAT_ERROR_PARSE;
  465|      4|            goto cleanup;
  466|      4|        }
  467|   102k|        if (ctx->little_endian) {
  ------------------
  |  Branch (467:13): [True: 102k, False: 0]
  ------------------
  468|   102k|            int k;
  469|   632k|            for (k=0; k<col_info->width; k++) {
  ------------------
  |  Branch (469:23): [True: 530k, False: 102k]
  ------------------
  470|   530k|                val = (val << 8) | (unsigned char)col_data[col_info->width-1-k];
  471|   530k|            }
  472|   102k|        } else {
  473|      0|            int k;
  474|      0|            for (k=0; k<col_info->width; k++) {
  ------------------
  |  Branch (474:23): [True: 0, False: 0]
  ------------------
  475|      0|                val = (val << 8) | (unsigned char)col_data[k];
  476|      0|            }
  477|      0|        }
  478|   102k|        val <<= (8-col_info->width)*8;
  479|       |
  480|   102k|        memcpy(&dval, &val, 8);
  481|       |
  482|   102k|        if (isnan(dval)) {
  ------------------
  |  Branch (482:13): [True: 5.47k, False: 96.5k]
  ------------------
  483|  5.47k|            value.v.double_value = NAN;
  484|  5.47k|            sas_assign_tag(&value, ~((val >> 40) & 0xFF));
  485|  96.5k|        } else {
  486|  96.5k|            value.v.double_value = dval;
  487|  96.5k|        }
  488|   102k|    }
  489|  21.4M|    cb_retval = ctx->handle.value(ctx->parsed_row_count, variable, value, ctx->user_ctx);
  490|       |
  491|  21.4M|    if (cb_retval != READSTAT_HANDLER_OK)
  ------------------
  |  |   22|  21.4M|#define READSTAT_HANDLER_OK             0
  ------------------
  |  Branch (491:9): [True: 0, False: 21.4M]
  ------------------
  492|      0|        retval = READSTAT_ERROR_USER_ABORT;
  493|       |
  494|  21.4M|cleanup:
  495|  21.4M|    return retval;
  496|  21.4M|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_compressed:
  687|  2.21k|static readstat_error_t sas7bdat_parse_subheader_compressed(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  688|  2.21k|    if (ctx->rdc_compression)
  ------------------
  |  Branch (688:9): [True: 300, False: 1.91k]
  ------------------
  689|    300|        return sas7bdat_parse_subheader_rdc(subheader, len, ctx);
  690|       |
  691|  1.91k|    return sas7bdat_parse_subheader_rle(subheader, len, ctx);
  692|  2.21k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_rdc:
  575|    300|static readstat_error_t sas7bdat_parse_subheader_rdc(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  576|    300|    readstat_error_t retval = READSTAT_OK;
  577|    300|    const unsigned char *input = (const unsigned char *)subheader;
  578|    300|    char *buffer = ctx->row;
  579|    300|    if (buffer == NULL) return READSTAT_ERROR_PARSE;
  ------------------
  |  Branch (579:9): [True: 0, False: 300]
  ------------------
  580|    300|    char *output = buffer;
  581|  1.93k|    while (input + 2 <= (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (581:12): [True: 1.66k, False: 267]
  ------------------
  582|  1.66k|        int i;
  583|  1.66k|        unsigned short prefix = (input[0] << 8) + input[1];
  584|  1.66k|        input += 2;
  585|  25.2k|        for (i=0; i<16; i++) {
  ------------------
  |  Branch (585:19): [True: 23.8k, False: 1.37k]
  ------------------
  586|  23.8k|            if ((prefix & (1 << (15 - i))) == 0) {
  ------------------
  |  Branch (586:17): [True: 18.3k, False: 5.52k]
  ------------------
  587|  18.3k|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (587:21): [True: 259, False: 18.0k]
  ------------------
  588|    259|                    break;
  589|    259|                }
  590|  18.0k|                if (output + 1 > buffer + ctx->row_length) {
  ------------------
  |  Branch (590:21): [True: 2, False: 18.0k]
  ------------------
  591|      2|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  592|      2|                    goto cleanup;
  593|      2|                }
  594|  18.0k|                *output++ = *input++;
  595|  18.0k|                continue;
  596|  18.0k|            }
  597|       |
  598|  5.52k|            if (input + 2 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (598:17): [True: 1, False: 5.52k]
  ------------------
  599|      1|                retval = READSTAT_ERROR_PARSE;
  600|      1|                goto cleanup;
  601|      1|            }
  602|       |
  603|  5.52k|            unsigned char marker_byte = *input++;
  604|  5.52k|            unsigned char next_byte = *input++;
  605|  5.52k|            size_t insert_len = 0, copy_len = 0;
  606|  5.52k|            unsigned char insert_byte = 0x00;
  607|  5.52k|            size_t back_offset = 0;
  608|       |
  609|  5.52k|            if (marker_byte <= 0x0F) {
  ------------------
  |  Branch (609:17): [True: 2.41k, False: 3.10k]
  ------------------
  610|  2.41k|                insert_len = 3 + marker_byte;
  611|  2.41k|                insert_byte = next_byte;
  612|  3.10k|            } else if ((marker_byte >> 4) == 1) {
  ------------------
  |  Branch (612:24): [True: 1.35k, False: 1.75k]
  ------------------
  613|  1.35k|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (613:21): [True: 1, False: 1.35k]
  ------------------
  614|      1|                    retval = READSTAT_ERROR_PARSE;
  615|      1|                    goto cleanup;
  616|      1|                }
  617|  1.35k|                insert_len = 19 + (marker_byte & 0x0F) + next_byte * 16;
  618|  1.35k|                insert_byte = *input++;
  619|  1.75k|            } else if ((marker_byte >> 4) == 2) {
  ------------------
  |  Branch (619:24): [True: 445, False: 1.30k]
  ------------------
  620|    445|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (620:21): [True: 1, False: 444]
  ------------------
  621|      1|                    retval = READSTAT_ERROR_PARSE;
  622|      1|                    goto cleanup;
  623|      1|                }
  624|    444|                copy_len = 16 + (*input++);
  625|    444|                back_offset = 3 + (marker_byte & 0x0F) + next_byte * 16;
  626|  1.30k|            } else {
  627|  1.30k|                copy_len = (marker_byte >> 4);
  628|  1.30k|                back_offset = 3 + (marker_byte & 0x0F) + next_byte * 16;
  629|  1.30k|            }
  630|       |
  631|  5.52k|            if (insert_len) {
  ------------------
  |  Branch (631:17): [True: 3.77k, False: 1.74k]
  ------------------
  632|  3.77k|                if (output + insert_len > buffer + ctx->row_length) {
  ------------------
  |  Branch (632:21): [True: 2, False: 3.77k]
  ------------------
  633|      2|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  634|      2|                    goto cleanup;
  635|      2|                }
  636|  3.77k|                memset(output, insert_byte, insert_len);
  637|  3.77k|                output += insert_len;
  638|  3.77k|            } else if (copy_len) {
  ------------------
  |  Branch (638:24): [True: 1.74k, False: 0]
  ------------------
  639|  1.74k|                if (output - buffer < back_offset || copy_len > back_offset) {
  ------------------
  |  Branch (639:21): [True: 8, False: 1.74k]
  |  Branch (639:54): [True: 15, False: 1.72k]
  ------------------
  640|     23|                    retval = READSTAT_ERROR_PARSE;
  641|     23|                    goto cleanup;
  642|     23|                }
  643|  1.72k|                if (output + copy_len > buffer + ctx->row_length) {
  ------------------
  |  Branch (643:21): [True: 3, False: 1.72k]
  ------------------
  644|      3|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  645|      3|                    goto cleanup;
  646|      3|                }
  647|  1.72k|                memcpy(output, output - back_offset, copy_len);
  648|  1.72k|                output += copy_len;
  649|  1.72k|            }
  650|  5.52k|        }
  651|  1.66k|    }
  652|       |
  653|    267|    if (output - buffer != ctx->row_length) {
  ------------------
  |  Branch (653:9): [True: 26, False: 241]
  ------------------
  654|     26|        retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  655|     26|        goto cleanup;
  656|     26|    }
  657|    241|    retval = sas7bdat_parse_single_row(buffer, ctx);
  658|    300|cleanup:
  659|    300|    return retval;
  660|    241|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_rle:
  662|  1.91k|static readstat_error_t sas7bdat_parse_subheader_rle(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  663|  1.91k|    if (ctx->row_limit == ctx->parsed_row_count)
  ------------------
  |  Branch (663:9): [True: 1.41k, False: 495]
  ------------------
  664|  1.41k|        return READSTAT_OK;
  665|       |
  666|    495|    readstat_error_t retval = READSTAT_OK;
  667|    495|    ssize_t bytes_decompressed = 0;
  668|       |
  669|    495|    bytes_decompressed = sas_rle_decompress(ctx->row, ctx->row_length, subheader, len);
  670|       |
  671|    495|    if (bytes_decompressed != ctx->row_length) {
  ------------------
  |  Branch (671:9): [True: 162, False: 333]
  ------------------
  672|    162|        retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  673|    162|        if (ctx->handle.error) {
  ------------------
  |  Branch (673:13): [True: 0, False: 162]
  ------------------
  674|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
  675|      0|                    "ReadStat: Row #%d decompressed to %ld bytes (expected %d bytes)",
  676|      0|                    ctx->parsed_row_count, (long)(bytes_decompressed), ctx->row_length);
  677|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  678|      0|        }
  679|    162|        goto cleanup;
  680|    162|    }
  681|    333|    retval = sas7bdat_parse_single_row(ctx->row, ctx);
  682|       |
  683|    495|cleanup:
  684|    495|    return retval;
  685|    333|}
readstat_sas7bdat_read.c:sas7bdat_register_deleted_row:
  426|  6.17k|static readstat_error_t sas7bdat_register_deleted_row(sas7bdat_ctx_t *ctx) {
  427|  6.17k|    if (ctx->parsed_deleted_row_count >= ctx->deleted_row_count) {
  ------------------
  |  Branch (427:9): [True: 5, False: 6.16k]
  ------------------
  428|      5|        return READSTAT_ERROR_PARSE;
  429|      5|    }
  430|  6.16k|    ctx->parsed_deleted_row_count++;
  431|  6.16k|    return READSTAT_OK;
  432|  6.17k|}
readstat_sas7bdat_read.c:sas7bdat_parse_deleted_row_bitmap:
 1074|    710|        sas7bdat_ctx_t *ctx) {
 1075|    710|    uint64_t page_unused_bytes;
 1076|    710|    if (ctx->u64) {
  ------------------
  |  Branch (1076:9): [True: 555, False: 155]
  ------------------
 1077|    555|        page_unused_bytes = sas_read8(&page[24], ctx->bswap);
 1078|    555|    } else {
 1079|    155|        page_unused_bytes = sas_read4(&page[12], ctx->bswap);
 1080|    155|    }
 1081|    710|    uint32_t row_count = ctx->page_row_count < ctx->total_row_count ? ctx->page_row_count : ctx->total_row_count;
  ------------------
  |  Branch (1081:26): [True: 346, False: 364]
  ------------------
 1082|    710|    uint64_t deleted_row_bitmap_offset = (uint64_t)row_count * ctx->row_length + page_unused_bytes;
 1083|    710|    uint32_t required_bytes = row_count / CHAR_BIT + (row_count % CHAR_BIT == 0 ? 0 : 1);
  ------------------
  |  Branch (1083:55): [True: 609, False: 101]
  ------------------
 1084|       |
 1085|    710|    if ((data - page) + deleted_row_bitmap_offset + required_bytes > page_size) {
  ------------------
  |  Branch (1085:9): [True: 119, False: 591]
  ------------------
 1086|    119|        return READSTAT_ERROR_PARSE;
 1087|    119|    }
 1088|    591|    *deleted_row_bitmap = (const unsigned char *)data + deleted_row_bitmap_offset;
 1089|    591|    *deleted_row_bitmap_row_count = row_count;
 1090|    591|    return READSTAT_OK;
 1091|    710|}
readstat_sas7bdat_read.c:sas7bdat_parse_rows:
  546|  2.68k|        const unsigned char *deleted_bitmap, uint32_t deleted_bitmap_row_count, sas7bdat_ctx_t *ctx) {
  547|  2.68k|    readstat_error_t retval = READSTAT_OK;
  548|  2.68k|    uint32_t i;
  549|  2.68k|    size_t row_offset=0;
  550|   213k|    for (i=0; i<ctx->page_row_count && ctx->parsed_row_count < ctx->row_limit; i++) {
  ------------------
  |  Branch (550:15): [True: 212k, False: 1.74k]
  |  Branch (550:40): [True: 211k, False: 760]
  ------------------
  551|   211k|        if (row_offset + ctx->row_length > len) {
  ------------------
  |  Branch (551:13): [True: 100, False: 211k]
  ------------------
  552|    100|            retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  553|    100|            goto cleanup;
  554|    100|        }
  555|   211k|        if (deleted_bitmap != NULL && i >= deleted_bitmap_row_count) {
  ------------------
  |  Branch (555:13): [True: 18.6k, False: 192k]
  |  Branch (555:39): [True: 4, False: 18.6k]
  ------------------
  556|       |            /* The page claims more rows than the deleted-row bitmap covers */
  557|      4|            retval = READSTAT_ERROR_PARSE;
  558|      4|            goto cleanup;
  559|      4|        }
  560|   211k|        if (deleted_bitmap != NULL && sas7bdat_read_bitmap(deleted_bitmap, i)) {
  ------------------
  |  Branch (560:13): [True: 18.6k, False: 192k]
  |  Branch (560:39): [True: 5.97k, False: 12.6k]
  ------------------
  561|  5.97k|            if ((retval = sas7bdat_register_deleted_row(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (561:17): [True: 3, False: 5.97k]
  ------------------
  562|      3|                goto cleanup;
  563|      3|            }
  564|   205k|        } else if ((retval = sas7bdat_parse_single_row(&data[row_offset], ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (564:20): [True: 70, False: 205k]
  ------------------
  565|     70|            goto cleanup;
  566|     70|        }
  567|       |
  568|   211k|        row_offset += ctx->row_length;
  569|   211k|    }
  570|       |
  571|  2.68k|cleanup:
  572|  2.68k|    return retval;
  573|  2.68k|}
readstat_sas7bdat_read.c:sas7bdat_read_bitmap:
  538|  18.6k|static unsigned char sas7bdat_read_bitmap(const unsigned char *bitmap, int index) {
  539|  18.6k|    unsigned char current_byte = bitmap[index / CHAR_BIT];
  540|  18.6k|    unsigned char mask = 1 << (CHAR_BIT - 1 - index % CHAR_BIT);
  541|       |
  542|  18.6k|    return current_byte & mask;
  543|  18.6k|}
readstat_sas7bdat_read.c:sas7bdat_submit_columns_if_needed:
  897|  6.04k|static readstat_error_t sas7bdat_submit_columns_if_needed(sas7bdat_ctx_t *ctx, int compressed) {
  898|  6.04k|    readstat_error_t retval = READSTAT_OK;
  899|  6.04k|    if (!ctx->did_submit_columns) {
  ------------------
  |  Branch (899:9): [True: 1.63k, False: 4.40k]
  ------------------
  900|  1.63k|        if ((retval = sas7bdat_submit_columns(ctx, compressed)) != READSTAT_OK) {
  ------------------
  |  Branch (900:13): [True: 244, False: 1.38k]
  ------------------
  901|    244|            goto cleanup;
  902|    244|        }
  903|  1.38k|        ctx->did_submit_columns = 1;
  904|  1.38k|    }
  905|  6.04k|cleanup:
  906|  6.04k|    return retval;
  907|  6.04k|}
readstat_sas7bdat_read.c:sas7bdat_submit_columns:
  836|  1.63k|static readstat_error_t sas7bdat_submit_columns(sas7bdat_ctx_t *ctx, int compressed) {
  837|  1.63k|    readstat_error_t retval = READSTAT_OK;
  838|  1.63k|    if (ctx->handle.metadata) {
  ------------------
  |  Branch (838:9): [True: 1.63k, False: 0]
  ------------------
  839|  1.63k|        readstat_metadata_t metadata = {
  840|  1.63k|            .row_count = ctx->row_limit,
  841|  1.63k|            .var_count = ctx->column_count,
  842|  1.63k|            .table_name = ctx->table_name,
  843|  1.63k|            .file_label = ctx->file_label,
  844|  1.63k|            .file_encoding = ctx->input_encoding, /* orig encoding? */
  845|  1.63k|            .creation_time = ctx->ctime,
  846|  1.63k|            .modified_time = ctx->mtime,
  847|  1.63k|            .file_format_version = ctx->version,
  848|  1.63k|            .compression = READSTAT_COMPRESS_NONE,
  849|  1.63k|            .endianness = ctx->little_endian ? READSTAT_ENDIAN_LITTLE : READSTAT_ENDIAN_BIG,
  ------------------
  |  Branch (849:27): [True: 290, False: 1.34k]
  ------------------
  850|  1.63k|            .is64bit = ctx->u64
  851|  1.63k|        };
  852|  1.63k|        if (compressed) {
  ------------------
  |  Branch (852:13): [True: 647, False: 986]
  ------------------
  853|    647|            if (ctx->rdc_compression) {
  ------------------
  |  Branch (853:17): [True: 58, False: 589]
  ------------------
  854|     58|                metadata.compression = READSTAT_COMPRESS_BINARY;
  855|    589|            } else {
  856|    589|                metadata.compression = READSTAT_COMPRESS_ROWS;
  857|    589|            }
  858|    647|        }
  859|  1.63k|        if (ctx->handle.metadata(&metadata, ctx->user_ctx) != READSTAT_HANDLER_OK) {
  ------------------
  |  |   22|  1.63k|#define READSTAT_HANDLER_OK             0
  ------------------
  |  Branch (859:13): [True: 0, False: 1.63k]
  ------------------
  860|      0|            retval = READSTAT_ERROR_USER_ABORT;
  861|      0|            goto cleanup;
  862|      0|        }
  863|  1.63k|    }
  864|  1.63k|    if (ctx->column_count == 0)
  ------------------
  |  Branch (864:9): [True: 1.12k, False: 506]
  ------------------
  865|  1.12k|        goto cleanup;
  866|       |
  867|    506|    if ((ctx->variables = readstat_calloc(ctx->column_count, sizeof(readstat_variable_t *))) == NULL) {
  ------------------
  |  Branch (867:9): [True: 0, False: 506]
  ------------------
  868|      0|        retval = READSTAT_ERROR_MALLOC;
  869|      0|        goto cleanup;
  870|      0|    }
  871|    506|    int i;
  872|    506|    int index_after_skipping = 0;
  873|  2.25M|    for (i=0; i<ctx->column_count; i++) {
  ------------------
  |  Branch (873:15): [True: 2.25M, False: 262]
  ------------------
  874|  2.25M|        ctx->variables[i] = sas7bdat_init_variable(ctx, i, index_after_skipping, &retval);
  875|  2.25M|        if (ctx->variables[i] == NULL)
  ------------------
  |  Branch (875:13): [True: 244, False: 2.25M]
  ------------------
  876|    244|            break;
  877|       |
  878|  2.25M|        int cb_retval = READSTAT_HANDLER_OK;
  ------------------
  |  |   22|  2.25M|#define READSTAT_HANDLER_OK             0
  ------------------
  879|  2.25M|        if (ctx->handle.variable) {
  ------------------
  |  Branch (879:13): [True: 2.25M, False: 0]
  ------------------
  880|  2.25M|            cb_retval = ctx->handle.variable(i, ctx->variables[i], ctx->variables[i]->format, ctx->user_ctx);
  881|  2.25M|        }
  882|  2.25M|        if (cb_retval == READSTAT_HANDLER_ABORT) {
  ------------------
  |  |   23|  2.25M|#define READSTAT_HANDLER_ABORT          1
  ------------------
  |  Branch (882:13): [True: 0, False: 2.25M]
  ------------------
  883|      0|            retval = READSTAT_ERROR_USER_ABORT;
  884|      0|            goto cleanup;
  885|      0|        }
  886|       |
  887|  2.25M|        if (cb_retval == READSTAT_HANDLER_SKIP_VARIABLE) {
  ------------------
  |  |   24|  2.25M|#define READSTAT_HANDLER_SKIP_VARIABLE  2
  ------------------
  |  Branch (887:13): [True: 0, False: 2.25M]
  ------------------
  888|      0|            ctx->variables[i]->skip = 1;
  889|  2.25M|        } else {
  890|  2.25M|            index_after_skipping++;
  891|  2.25M|        }
  892|  2.25M|    }
  893|  1.63k|cleanup:
  894|  1.63k|    return retval;
  895|    506|}
readstat_sas7bdat_read.c:sas7bdat_init_variable:
  783|  2.25M|        int index_after_skipping, readstat_error_t *out_retval) {
  784|  2.25M|    readstat_error_t retval = READSTAT_OK;
  785|  2.25M|    readstat_variable_t *variable = readstat_calloc(1, sizeof(readstat_variable_t));
  786|       |
  787|  2.25M|    variable->index = i;
  788|  2.25M|    variable->index_after_skipping = index_after_skipping;
  789|  2.25M|    variable->type = ctx->col_info[i].type;
  790|  2.25M|    variable->storage_width = ctx->col_info[i].width;
  791|       |
  792|  2.25M|    if ((retval = sas7bdat_validate_column(&ctx->col_info[i])) != READSTAT_OK) {
  ------------------
  |  Branch (792:9): [True: 66, False: 2.25M]
  ------------------
  793|     66|        goto cleanup;
  794|     66|    }
  795|  2.25M|    if ((retval = sas7bdat_copy_text_ref(variable->name, sizeof(variable->name), 
  ------------------
  |  Branch (795:9): [True: 55, False: 2.25M]
  ------------------
  796|  2.25M|                    ctx->col_info[i].name_ref, ctx)) != READSTAT_OK) {
  797|     55|        goto cleanup;
  798|     55|    }
  799|  2.25M|    if ((retval = sas7bdat_construct_format(variable->format, sizeof(variable->format),
  ------------------
  |  Branch (799:9): [True: 45, False: 2.25M]
  ------------------
  800|  2.25M|                    ctx->col_info[i].format_ref, ctx->col_info[i].format_width,
  801|  2.25M|                    ctx->col_info[i].format_digits, ctx)) != READSTAT_OK) {
  802|     45|        goto cleanup;
  803|     45|    }
  804|  2.25M|    if ((retval = sas7bdat_construct_format(variable->informat, sizeof(variable->informat),
  ------------------
  |  Branch (804:9): [True: 31, False: 2.25M]
  ------------------
  805|  2.25M|                    ctx->col_info[i].informat_ref, ctx->col_info[i].informat_width,
  806|  2.25M|                    ctx->col_info[i].informat_digits, ctx)) != READSTAT_OK) {
  807|     31|        goto cleanup;
  808|     31|    }
  809|  2.25M|    if ((retval = sas7bdat_copy_text_ref(variable->label, sizeof(variable->label), 
  ------------------
  |  Branch (809:9): [True: 47, False: 2.25M]
  ------------------
  810|  2.25M|                    ctx->col_info[i].label_ref, ctx)) != READSTAT_OK) {
  811|     47|        goto cleanup;
  812|     47|    }
  813|       |
  814|  2.25M|cleanup:
  815|  2.25M|    if (retval != READSTAT_OK) {
  ------------------
  |  Branch (815:9): [True: 244, False: 2.25M]
  ------------------
  816|    244|        if (out_retval)
  ------------------
  |  Branch (816:13): [True: 244, False: 0]
  ------------------
  817|    244|            *out_retval = retval;
  818|       |
  819|    244|        if (retval == READSTAT_ERROR_CONVERT_BAD_STRING) {
  ------------------
  |  Branch (819:13): [True: 5, False: 239]
  ------------------
  820|      5|            if (ctx->handle.error) {
  ------------------
  |  Branch (820:17): [True: 0, False: 5]
  ------------------
  821|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf),
  822|      0|                        "ReadStat: Error converting variable #%d info to specified encoding: %s %s (%s)",
  823|      0|                        i, variable->name, variable->format, variable->label);
  824|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  825|      0|            }
  826|      5|        }
  827|       |
  828|    244|        free(variable);
  829|       |
  830|    244|        return NULL;
  831|    244|    }
  832|       |
  833|  2.25M|    return variable;
  834|  2.25M|}
readstat_sas7bdat_read.c:sas7bdat_validate_column:
  729|  2.25M|static readstat_error_t sas7bdat_validate_column(col_info_t *col_info) {
  730|  2.25M|    if (col_info->type == READSTAT_TYPE_DOUBLE) {
  ------------------
  |  Branch (730:9): [True: 1.57k, False: 2.25M]
  ------------------
  731|  1.57k|        if (col_info->width > 8 || col_info->width < 3) {
  ------------------
  |  Branch (731:13): [True: 30, False: 1.54k]
  |  Branch (731:36): [True: 4, False: 1.54k]
  ------------------
  732|     34|            return READSTAT_ERROR_PARSE;
  733|     34|        }
  734|  1.57k|    }
  735|  2.25M|    if (col_info->type == READSTAT_TYPE_STRING) {
  ------------------
  |  Branch (735:9): [True: 2.25M, False: 1.54k]
  ------------------
  736|  2.25M|        if (col_info->width > INT16_MAX) {
  ------------------
  |  Branch (736:13): [True: 32, False: 2.25M]
  ------------------
  737|     32|            return READSTAT_ERROR_PARSE;
  738|     32|        }
  739|  2.25M|    }
  740|  2.25M|    return READSTAT_OK;
  741|  2.25M|}
readstat_sas7bdat_read.c:sas7bdat_construct_format:
  744|  4.51M|        sas_text_ref_t text_ref, int width, int digits, sas7bdat_ctx_t *ctx) {
  745|  4.51M|    readstat_error_t retval = sas7bdat_copy_text_ref(dst, dst_len, text_ref, ctx);
  746|  4.51M|    if (retval != READSTAT_OK)
  ------------------
  |  Branch (746:9): [True: 55, False: 4.51M]
  ------------------
  747|     55|        return retval;
  748|       |
  749|       |    /* Digits require a width */
  750|  4.51M|    if (width == 0 && digits != 0)
  ------------------
  |  Branch (750:9): [True: 4.50M, False: 1.21k]
  |  Branch (750:23): [True: 17, False: 4.50M]
  ------------------
  751|     17|        return READSTAT_ERROR_PARSE;
  752|       |
  753|  4.51M|    if (strchr(dst, '.')) {
  ------------------
  |  Branch (753:9): [True: 130, False: 4.51M]
  ------------------
  754|       |        /* Legacy layout: earlier ReadStat writers stored the full format
  755|       |         * string (e.g. "YYMMDD10." or "BEST.") verbatim as the name, with
  756|       |         * width and digits both 0. Return it unchanged. */
  757|    130|        if (width == 0 && digits == 0)
  ------------------
  |  Branch (757:13): [True: 129, False: 1]
  |  Branch (757:27): [True: 129, False: 0]
  ------------------
  758|    129|            return READSTAT_OK;
  759|      1|        return READSTAT_ERROR_PARSE;
  760|    130|    }
  761|       |
  762|       |    /* No format at all */
  763|  4.51M|    if (dst[0] == '\0' && width == 0)
  ------------------
  |  Branch (763:9): [True: 4.50M, False: 689]
  |  Branch (763:27): [True: 4.50M, False: 924]
  ------------------
  764|  4.50M|        return READSTAT_OK;
  765|       |
  766|       |    /* NAME. / NAMEw. / NAMEw.d (NAME may be empty, as in the w.d format) */
  767|  1.61k|    size_t len = strlen(dst);
  768|  1.61k|    int written;
  769|  1.61k|    if (digits) {
  ------------------
  |  Branch (769:9): [True: 822, False: 791]
  ------------------
  770|    822|        written = snprintf(dst + len, dst_len - len, "%d.%d", width, digits);
  771|    822|    } else if (width) {
  ------------------
  |  Branch (771:16): [True: 391, False: 400]
  ------------------
  772|    391|        written = snprintf(dst + len, dst_len - len, "%d.", width);
  773|    400|    } else {
  774|    400|        written = snprintf(dst + len, dst_len - len, ".");
  775|    400|    }
  776|  1.61k|    if (written < 0 || (size_t)written >= dst_len - len)
  ------------------
  |  Branch (776:9): [True: 0, False: 1.61k]
  |  Branch (776:24): [True: 3, False: 1.61k]
  ------------------
  777|      3|        return READSTAT_ERROR_PARSE;
  778|       |
  779|  1.61k|    return READSTAT_OK;
  780|  1.61k|}
readstat_sas7bdat_read.c:sas7bdat_update_progress:
  141|  7.39k|static readstat_error_t sas7bdat_update_progress(sas7bdat_ctx_t *ctx) {
  142|  7.39k|    readstat_io_t *io = ctx->io;
  143|  7.39k|    return io->update(ctx->file_size, ctx->handle.progress, ctx->user_ctx, io->io_ctx);
  144|  7.39k|}
readstat_sas7bdat_read.c:sas7bdat_ctx_free:
  104|  4.49k|static void sas7bdat_ctx_free(sas7bdat_ctx_t *ctx) {
  105|  4.49k|    int i;
  106|  4.49k|    if (ctx->text_blobs) {
  ------------------
  |  Branch (106:9): [True: 944, False: 3.55k]
  ------------------
  107|  9.47k|        for (i=0; i<ctx->text_blob_count; i++) {
  ------------------
  |  Branch (107:19): [True: 8.53k, False: 944]
  ------------------
  108|  8.53k|            free(ctx->text_blobs[i]);
  109|  8.53k|        }
  110|    944|        free(ctx->text_blobs);
  111|    944|        free(ctx->text_blob_lengths);
  112|    944|    }
  113|  4.49k|    if (ctx->variables) {
  ------------------
  |  Branch (113:9): [True: 506, False: 3.98k]
  ------------------
  114|  14.7M|        for (i=0; i<ctx->column_count; i++) {
  ------------------
  |  Branch (114:19): [True: 14.7M, False: 506]
  ------------------
  115|  14.7M|            if (ctx->variables[i])
  ------------------
  |  Branch (115:17): [True: 2.25M, False: 12.5M]
  ------------------
  116|  2.25M|                free(ctx->variables[i]);
  117|  14.7M|        }
  118|    506|        free(ctx->variables);
  119|    506|    }
  120|  4.49k|    if (ctx->col_info)
  ------------------
  |  Branch (120:9): [True: 716, False: 3.77k]
  ------------------
  121|    716|        free(ctx->col_info);
  122|       |
  123|  4.49k|    if (ctx->scratch_buffer)
  ------------------
  |  Branch (123:9): [True: 344, False: 4.15k]
  ------------------
  124|    344|        free(ctx->scratch_buffer);
  125|       |
  126|  4.49k|    if (ctx->page)
  ------------------
  |  Branch (126:9): [True: 3.95k, False: 538]
  ------------------
  127|  3.95k|        free(ctx->page);
  128|       |
  129|  4.49k|    if (ctx->moved_page)
  ------------------
  |  Branch (129:9): [True: 513, False: 3.98k]
  ------------------
  130|    513|        free(ctx->moved_page);
  131|       |
  132|  4.49k|    if (ctx->row)
  ------------------
  |  Branch (132:9): [True: 884, False: 3.61k]
  ------------------
  133|    884|        free(ctx->row);
  134|       |
  135|  4.49k|    if (ctx->converter)
  ------------------
  |  Branch (135:9): [True: 2.75k, False: 1.73k]
  ------------------
  136|  2.75k|        iconv_close(ctx->converter);
  137|       |
  138|  4.49k|    free(ctx);
  139|  4.49k|}

sas_rle_decompress:
   47|    495|        const void *input_buf, size_t input_len) {
   48|    495|    unsigned char *buffer = (unsigned char *)output_buf;
   49|    495|    unsigned char *output = buffer;
   50|    495|    size_t output_written = 0;
   51|       |
   52|    495|    const unsigned char *input = (const unsigned char *)input_buf;
   53|       |
   54|  89.7k|    while (input < (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (54:12): [True: 89.3k, False: 360]
  ------------------
   55|  89.3k|        unsigned char control = *input++;
   56|  89.3k|        unsigned char command = (control & 0xF0) >> 4;
   57|  89.3k|        unsigned char length = (control & 0x0F);
   58|  89.3k|        int copy_len = 0;
   59|  89.3k|        int insert_len = 0;
   60|  89.3k|        unsigned char insert_byte = '\0';
   61|  89.3k|        if (input + command_lengths[command] > (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (61:13): [True: 6, False: 89.3k]
  ------------------
   62|      6|            return -1;
   63|      6|        }
   64|  89.3k|        switch (command) {
   65|  7.10k|            case SAS_RLE_COMMAND_COPY64:
  ------------------
  |  |   13|  7.10k|#define SAS_RLE_COMMAND_COPY64          0
  ------------------
  |  Branch (65:13): [True: 7.10k, False: 82.2k]
  ------------------
   66|  7.10k|                copy_len = (*input++) + 64 + length * 256;
   67|  7.10k|                break;
   68|    285|            case SAS_RLE_COMMAND_COPY64_PLUS_4096:
  ------------------
  |  |   14|    285|#define SAS_RLE_COMMAND_COPY64_PLUS_4096 1
  ------------------
  |  Branch (68:13): [True: 285, False: 89.1k]
  ------------------
   69|    285|                copy_len = (*input++) + 64 + length * 256 + 4096;
   70|    285|                break;
   71|    657|            case SAS_RLE_COMMAND_COPY96: copy_len = length + 96; break;
  ------------------
  |  |   15|    657|#define SAS_RLE_COMMAND_COPY96          2
  ------------------
  |  Branch (71:13): [True: 657, False: 88.7k]
  ------------------
   72|    870|            case SAS_RLE_COMMAND_INSERT_BYTE18:
  ------------------
  |  |   16|    870|#define SAS_RLE_COMMAND_INSERT_BYTE18   4
  ------------------
  |  Branch (72:13): [True: 870, False: 88.5k]
  ------------------
   73|    870|                insert_len = (*input++) + 18 + length * 256;
   74|    870|                insert_byte = *input++;
   75|    870|                break;
   76|    821|            case SAS_RLE_COMMAND_INSERT_AT17:
  ------------------
  |  |   17|    821|#define SAS_RLE_COMMAND_INSERT_AT17     5
  ------------------
  |  Branch (76:13): [True: 821, False: 88.5k]
  ------------------
   77|    821|                insert_len = (*input++) + 17 + length * 256;
   78|    821|                insert_byte = '@';
   79|    821|                break;
   80|    864|            case SAS_RLE_COMMAND_INSERT_BLANK17:
  ------------------
  |  |   18|    864|#define SAS_RLE_COMMAND_INSERT_BLANK17  6
  ------------------
  |  Branch (80:13): [True: 864, False: 88.5k]
  ------------------
   81|    864|                insert_len = (*input++) + 17 + length * 256;
   82|    864|                insert_byte = ' ';
   83|    864|                break;
   84|  42.8k|            case SAS_RLE_COMMAND_INSERT_ZERO17:
  ------------------
  |  |   19|  42.8k|#define SAS_RLE_COMMAND_INSERT_ZERO17   7
  ------------------
  |  Branch (84:13): [True: 42.8k, False: 46.4k]
  ------------------
   85|  42.8k|                insert_len = (*input++) + 17 + length * 256;
   86|  42.8k|                insert_byte = '\0';
   87|  42.8k|                break;
   88|  1.05k|            case SAS_RLE_COMMAND_COPY1:  copy_len = length + 1; break;
  ------------------
  |  |   20|  1.05k|#define SAS_RLE_COMMAND_COPY1           8
  ------------------
  |  Branch (88:13): [True: 1.05k, False: 88.3k]
  ------------------
   89|    930|            case SAS_RLE_COMMAND_COPY17: copy_len = length + 17; break;
  ------------------
  |  |   21|    930|#define SAS_RLE_COMMAND_COPY17          9
  ------------------
  |  Branch (89:13): [True: 930, False: 88.4k]
  ------------------
   90|    549|            case SAS_RLE_COMMAND_COPY33: copy_len = length + 33; break;
  ------------------
  |  |   22|    549|#define SAS_RLE_COMMAND_COPY33         10
  ------------------
  |  Branch (90:13): [True: 549, False: 88.8k]
  ------------------
   91|    384|            case SAS_RLE_COMMAND_COPY49: copy_len = length + 49; break;
  ------------------
  |  |   23|    384|#define SAS_RLE_COMMAND_COPY49         11
  ------------------
  |  Branch (91:13): [True: 384, False: 89.0k]
  ------------------
   92|    963|            case SAS_RLE_COMMAND_INSERT_BYTE3:
  ------------------
  |  |   24|    963|#define SAS_RLE_COMMAND_INSERT_BYTE3   12
  ------------------
  |  Branch (92:13): [True: 963, False: 88.4k]
  ------------------
   93|    963|                insert_byte = *input++;
   94|    963|                insert_len = length + 3;
   95|    963|                break;
   96|  2.02k|            case SAS_RLE_COMMAND_INSERT_AT2:
  ------------------
  |  |   25|  2.02k|#define SAS_RLE_COMMAND_INSERT_AT2     13
  ------------------
  |  Branch (96:13): [True: 2.02k, False: 87.3k]
  ------------------
   97|  2.02k|                insert_byte = '@';
   98|  2.02k|                insert_len = length + 2;
   99|  2.02k|                break;
  100|  18.4k|            case SAS_RLE_COMMAND_INSERT_BLANK2:
  ------------------
  |  |   26|  18.4k|#define SAS_RLE_COMMAND_INSERT_BLANK2  14
  ------------------
  |  Branch (100:13): [True: 18.4k, False: 70.8k]
  ------------------
  101|  18.4k|                insert_byte = ' ';
  102|  18.4k|                insert_len = length + 2;
  103|  18.4k|                break;
  104|  7.24k|            case SAS_RLE_COMMAND_INSERT_ZERO2:
  ------------------
  |  |   27|  7.24k|#define SAS_RLE_COMMAND_INSERT_ZERO2   15
  ------------------
  |  Branch (104:13): [True: 7.24k, False: 82.1k]
  ------------------
  105|  7.24k|                insert_byte = '\0';
  106|  7.24k|                insert_len = length + 2;
  107|  7.24k|                break;
  108|  4.24k|            default:
  ------------------
  |  Branch (108:13): [True: 4.24k, False: 85.1k]
  ------------------
  109|       |                /* error out here? */
  110|  4.24k|                break;
  111|  89.3k|        }
  112|  89.3k|        if (copy_len) {
  ------------------
  |  Branch (112:13): [True: 10.9k, False: 78.4k]
  ------------------
  113|  10.9k|            if (output_written + copy_len > output_len) {
  ------------------
  |  Branch (113:17): [True: 18, False: 10.9k]
  ------------------
  114|     18|                return -1;
  115|     18|            }
  116|  10.9k|            if (input + copy_len > (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (116:17): [True: 97, False: 10.8k]
  ------------------
  117|     97|                return -1;
  118|     97|            }
  119|  10.8k|            if (output) {
  ------------------
  |  Branch (119:17): [True: 10.8k, False: 0]
  ------------------
  120|  10.8k|                memcpy(&output[output_written], input, copy_len);
  121|  10.8k|            }
  122|  10.8k|            input += copy_len;
  123|  10.8k|            output_written += copy_len;
  124|  10.8k|        }
  125|  89.2k|        if (insert_len) {
  ------------------
  |  Branch (125:13): [True: 74.1k, False: 15.0k]
  ------------------
  126|  74.1k|            if (output_written + insert_len > output_len) {
  ------------------
  |  Branch (126:17): [True: 14, False: 74.1k]
  ------------------
  127|     14|                return -1;
  128|     14|            }
  129|  74.1k|            if (output) {
  ------------------
  |  Branch (129:17): [True: 74.1k, False: 0]
  ------------------
  130|  74.1k|                memset(&output[output_written], insert_byte, insert_len);
  131|  74.1k|            }
  132|  74.1k|            output_written += insert_len;
  133|  74.1k|        }
  134|  89.2k|    }
  135|       |
  136|    360|    return output_written;
  137|    495|}

rt_open_handler:
    8|  4.49k|int rt_open_handler(const char *path, void *io_ctx) {
    9|  4.49k|    return 0;
   10|  4.49k|}
rt_close_handler:
   12|  4.49k|int rt_close_handler(void *io_ctx) {
   13|  4.49k|    return 0;
   14|  4.49k|}
rt_seek_handler:
   17|  40.2k|        readstat_io_flags_t whence, void *io_ctx) {
   18|  40.2k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   19|  40.2k|    readstat_off_t newpos = -1;
   20|  40.2k|    if (whence == READSTAT_SEEK_SET) {
  ------------------
  |  Branch (20:9): [True: 26.7k, False: 13.5k]
  ------------------
   21|  26.7k|        newpos = offset;
   22|  26.7k|    } else if (whence == READSTAT_SEEK_CUR) {
  ------------------
  |  Branch (22:16): [True: 9.03k, False: 4.49k]
  ------------------
   23|  9.03k|        newpos = buffer_ctx->pos + offset;
   24|  9.03k|    } else if (whence == READSTAT_SEEK_END) {
  ------------------
  |  Branch (24:16): [True: 4.49k, False: 0]
  ------------------
   25|  4.49k|        newpos = buffer_ctx->buffer->used + offset;
   26|  4.49k|    }
   27|       |
   28|  40.2k|    if (newpos < 0)
  ------------------
  |  Branch (28:9): [True: 235, False: 40.0k]
  ------------------
   29|    235|        return -1;
   30|       |
   31|  40.0k|    if (newpos > buffer_ctx->buffer->used)
  ------------------
  |  Branch (31:9): [True: 256, False: 39.7k]
  ------------------
   32|    256|        return -1;
   33|       |
   34|  39.7k|    buffer_ctx->pos = newpos;
   35|  39.7k|    return newpos;
   36|  40.0k|}
rt_read_handler:
   38|  70.0k|ssize_t rt_read_handler(void *buf, size_t nbytes, void *io_ctx) {
   39|  70.0k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   40|  70.0k|    ssize_t bytes_copied = 0;
   41|  70.0k|    ssize_t bytes_left = buffer_ctx->buffer->used - buffer_ctx->pos;
   42|  70.0k|    if (nbytes <= bytes_left) {
  ------------------
  |  Branch (42:9): [True: 69.6k, False: 476]
  ------------------
   43|  69.6k|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, nbytes);
   44|  69.6k|        bytes_copied = nbytes;
   45|  69.6k|    } else if (bytes_left > 0) {
  ------------------
  |  Branch (45:16): [True: 262, False: 214]
  ------------------
   46|    262|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, bytes_left);
   47|    262|        bytes_copied = bytes_left;
   48|    262|    }
   49|  70.0k|    buffer_ctx->pos += bytes_copied;
   50|  70.0k|    return bytes_copied;
   51|  70.0k|}
rt_update_handler:
   54|  7.39k|        void *user_ctx, void *io_ctx) {
   55|  7.39k|    if (!progress_handler)
  ------------------
  |  Branch (55:9): [True: 7.39k, False: 0]
  ------------------
   56|  7.39k|        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|}

