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

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

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

readstat_convert:
    7|  6.38M|readstat_error_t readstat_convert(char *dst, size_t dst_len, const char *src, size_t src_len, iconv_t converter) {
    8|       |    /* strip off spaces from the input because the programs use ASCII space
    9|       |     * padding even with non-ASCII encoding. */
   10|  6.42M|    while (src_len && (src[src_len-1] == ' ' || src[src_len-1] == '\0')) {
  ------------------
  |  Branch (10:12): [True: 56.6k, False: 6.36M]
  |  Branch (10:24): [True: 3.64k, False: 53.0k]
  |  Branch (10:49): [True: 34.9k, False: 18.0k]
  ------------------
   11|  38.5k|        src_len--;
   12|  38.5k|    }
   13|  6.38M|    if (dst_len == 0) {
  ------------------
  |  Branch (13:9): [True: 0, False: 6.38M]
  ------------------
   14|      0|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   15|  6.38M|    } else if (converter) {
  ------------------
  |  Branch (15:16): [True: 228k, False: 6.15M]
  ------------------
   16|   228k|        size_t dst_left = dst_len - 1;
   17|   228k|        char *dst_end = dst;
   18|   228k|        size_t status = iconv(converter, (readstat_iconv_inbuf_t)&src, &src_len, &dst_end, &dst_left);
   19|   228k|        if (status == (size_t)-1) {
  ------------------
  |  Branch (19:13): [True: 1.08k, False: 227k]
  ------------------
   20|  1.08k|            if (errno == E2BIG) {
  ------------------
  |  Branch (20:17): [True: 5, False: 1.08k]
  ------------------
   21|      5|                return READSTAT_ERROR_CONVERT_LONG_STRING;
   22|  1.08k|            } else if (errno == EILSEQ) {
  ------------------
  |  Branch (22:24): [True: 26, False: 1.05k]
  ------------------
   23|     26|                return READSTAT_ERROR_CONVERT_BAD_STRING;
   24|  1.05k|            } else if (errno != EINVAL) { /* EINVAL indicates improper truncation; accept it */
  ------------------
  |  Branch (24:24): [True: 0, False: 1.05k]
  ------------------
   25|      0|                return READSTAT_ERROR_CONVERT;
   26|      0|            }
   27|  1.08k|        }
   28|   228k|        dst[dst_len - dst_left - 1] = '\0';
   29|  6.15M|    } else if (src_len + 1 > dst_len) {
  ------------------
  |  Branch (29:16): [True: 12, False: 6.15M]
  ------------------
   30|     12|        return READSTAT_ERROR_CONVERT_LONG_STRING;
   31|  6.15M|    } else {
   32|  6.15M|        memcpy(dst, src, src_len);
   33|  6.15M|        dst[src_len] = '\0';
   34|  6.15M|    }
   35|  6.38M|    return READSTAT_OK;
   36|  6.38M|}

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

readstat_malloc:
   10|  4.38k|void *readstat_malloc(size_t len) {
   11|  4.38k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|  8.77k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (11:9): [True: 0, False: 4.38k]
  |  Branch (11:34): [True: 0, False: 4.38k]
  ------------------
   12|      0|        return NULL;
   13|      0|    }
   14|  4.38k|    return malloc(len);
   15|  4.38k|}
readstat_calloc:
   17|  1.42M|void *readstat_calloc(size_t count, size_t size) {
   18|  1.42M|    if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  2.85M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  2.85M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
                  if (count > MAX_MALLOC_SIZE || size > MAX_MALLOC_SIZE || count * size > MAX_MALLOC_SIZE) {
  ------------------
  |  |    3|  1.42M|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (18:9): [True: 0, False: 1.42M]
  |  Branch (18:36): [True: 0, False: 1.42M]
  |  Branch (18:62): [True: 0, False: 1.42M]
  ------------------
   19|      0|        return NULL;
   20|      0|    }
   21|  1.42M|    if (count == 0 || size == 0) {
  ------------------
  |  Branch (21:9): [True: 0, False: 1.42M]
  |  Branch (21:23): [True: 0, False: 1.42M]
  ------------------
   22|      0|        return NULL;
   23|      0|    }
   24|  1.42M|    return calloc(count, size);
   25|  1.42M|}
readstat_realloc:
   27|   283k|void *readstat_realloc(void *ptr, size_t len) {
   28|   283k|    if (len > MAX_MALLOC_SIZE || len == 0) {
  ------------------
  |  |    3|   567k|#define MAX_MALLOC_SIZE 0x1000000
  ------------------
  |  Branch (28:9): [True: 76, False: 283k]
  |  Branch (28:34): [True: 1, False: 283k]
  ------------------
   29|     77|        if (ptr)
  ------------------
  |  Branch (29:13): [True: 40, False: 37]
  ------------------
   30|     40|            free(ptr);
   31|     77|        return NULL;
   32|     77|    }
   33|   283k|    return realloc(ptr, len);
   34|   283k|}

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

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

sas_read8:
  139|  51.5k|uint64_t sas_read8(const char *data, int bswap) {
  140|  51.5k|    uint64_t tmp;
  141|  51.5k|    memcpy(&tmp, data, 8);
  142|  51.5k|    return bswap ? byteswap8(tmp) : tmp;
  ------------------
  |  Branch (142:12): [True: 47.7k, False: 3.84k]
  ------------------
  143|  51.5k|}
sas_read4:
  145|   191k|uint32_t sas_read4(const char *data, int bswap) {
  146|   191k|    uint32_t tmp;
  147|   191k|    memcpy(&tmp, data, 4);
  148|   191k|    return bswap ? byteswap4(tmp) : tmp;
  ------------------
  |  Branch (148:12): [True: 130k, False: 61.0k]
  ------------------
  149|   191k|}
sas_read2:
  151|   147k|uint16_t sas_read2(const char *data, int bswap) {
  152|   147k|    uint16_t tmp;
  153|   147k|    memcpy(&tmp, data, 2);
  154|   147k|    return bswap ? byteswap2(tmp) : tmp;
  ------------------
  |  Branch (154:12): [True: 64.6k, False: 82.6k]
  ------------------
  155|   147k|}
sas_subheader_remainder:
  157|  6.53k|size_t sas_subheader_remainder(size_t len, size_t signature_len) {
  158|  6.53k|    return len - (4+2*signature_len);
  159|  6.53k|}
sas_read_header:
  162|  2.79k|        readstat_error_handler error_handler, void *user_ctx) {
  163|  2.79k|    sas_header_start_t  header_start;
  164|  2.79k|    sas_header_end_t    header_end;
  165|  2.79k|    int retval = READSTAT_OK;
  166|  2.79k|    char error_buf[1024];
  167|  2.79k|    time_t epoch = sas_epoch();
  168|       |
  169|  2.79k|    if (io->read(&header_start, sizeof(sas_header_start_t), io->io_ctx) < sizeof(sas_header_start_t)) {
  ------------------
  |  Branch (169:9): [True: 14, False: 2.78k]
  ------------------
  170|     14|        retval = READSTAT_ERROR_READ;
  171|     14|        goto cleanup;
  172|     14|    }
  173|  2.78k|    if (memcmp(header_start.magic, sas7bdat_magic_number, sizeof(sas7bdat_magic_number)) != 0 &&
  ------------------
  |  Branch (173:9): [True: 1.20k, False: 1.58k]
  ------------------
  174|  1.20k|            memcmp(header_start.magic, sas7bcat_magic_number, sizeof(sas7bcat_magic_number)) != 0) {
  ------------------
  |  Branch (174:13): [True: 75, False: 1.12k]
  ------------------
  175|     75|        retval = READSTAT_ERROR_PARSE;
  176|     75|        goto cleanup;
  177|     75|    }
  178|  2.70k|    if (header_start.a1 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   87|  2.70k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (178:9): [True: 11, False: 2.69k]
  ------------------
  179|     11|        hinfo->pad1 = 4;
  180|     11|    }
  181|  2.70k|    if (header_start.a2 == SAS_ALIGNMENT_OFFSET_4) {
  ------------------
  |  |   87|  2.70k|#define SAS_ALIGNMENT_OFFSET_4  0x33
  ------------------
  |  Branch (181:9): [True: 1.25k, False: 1.45k]
  ------------------
  182|  1.25k|        hinfo->u64 = 1;
  183|  1.25k|    }
  184|  2.70k|    int bswap = 0;
  185|  2.70k|    if (header_start.endian == SAS_ENDIAN_BIG) {
  ------------------
  |  |   80|  2.70k|#define SAS_ENDIAN_BIG       0x00
  ------------------
  |  Branch (185:9): [True: 2.08k, False: 625]
  ------------------
  186|  2.08k|        bswap = machine_is_little_endian();
  187|  2.08k|        hinfo->little_endian = 0;
  188|  2.08k|    } else if (header_start.endian == SAS_ENDIAN_LITTLE) {
  ------------------
  |  |   81|    625|#define SAS_ENDIAN_LITTLE    0x01
  ------------------
  |  Branch (188:16): [True: 618, False: 7]
  ------------------
  189|    618|        bswap = !machine_is_little_endian();
  190|    618|        hinfo->little_endian = 1;
  191|    618|    } else {
  192|      7|        retval = READSTAT_ERROR_PARSE;
  193|      7|        goto cleanup;
  194|      7|    }
  195|  2.70k|    int i;
  196|  17.2k|    for (i=0; i<sizeof(_charset_table)/sizeof(_charset_table[0]); i++) {
  ------------------
  |  Branch (196:15): [True: 17.2k, False: 6]
  ------------------
  197|  17.2k|        if (header_start.encoding == _charset_table[i].code) {
  ------------------
  |  Branch (197:13): [True: 2.69k, False: 14.5k]
  ------------------
  198|  2.69k|            hinfo->encoding = _charset_table[i].name;
  199|  2.69k|            break;
  200|  2.69k|        }
  201|  17.2k|    }
  202|  2.70k|    if (hinfo->encoding == NULL) {
  ------------------
  |  Branch (202:9): [True: 6, False: 2.69k]
  ------------------
  203|      6|        if (error_handler) {
  ------------------
  |  Branch (203:13): [True: 0, False: 6]
  ------------------
  204|      0|            snprintf(error_buf, sizeof(error_buf), "Unsupported character set code: %d", header_start.encoding);
  205|      0|            error_handler(error_buf, user_ctx);
  206|      0|        }
  207|      6|        retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
  208|      6|        goto cleanup;
  209|      6|    }
  210|  2.69k|    memcpy(hinfo->table_name, header_start.table_name, sizeof(header_start.table_name));
  211|  2.69k|    if (io->seek(hinfo->pad1, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (211:9): [True: 8, False: 2.68k]
  ------------------
  212|      8|        retval = READSTAT_ERROR_SEEK;
  213|      8|        goto cleanup;
  214|      8|    }
  215|       |
  216|  2.68k|    double creation_time, modification_time, creation_time_diff, modification_time_diff;
  217|       |
  218|  2.68k|    if (io->read(&creation_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (218:9): [True: 9, False: 2.67k]
  ------------------
  219|      9|        retval = READSTAT_ERROR_READ;
  220|      9|        goto cleanup;
  221|      9|    }
  222|  2.67k|    if (bswap)
  ------------------
  |  Branch (222:9): [True: 2.06k, False: 614]
  ------------------
  223|  2.06k|        creation_time = byteswap_double(creation_time);
  224|       |
  225|  2.67k|    if (io->read(&modification_time, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (225:9): [True: 6, False: 2.67k]
  ------------------
  226|      6|        retval = READSTAT_ERROR_READ;
  227|      6|        goto cleanup;
  228|      6|    }
  229|  2.67k|    if (bswap)
  ------------------
  |  Branch (229:9): [True: 2.06k, False: 612]
  ------------------
  230|  2.06k|        modification_time = byteswap_double(modification_time);
  231|       |
  232|  2.67k|    if (io->read(&creation_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (232:9): [True: 6, False: 2.66k]
  ------------------
  233|      6|        retval = READSTAT_ERROR_READ;
  234|      6|        goto cleanup;
  235|      6|    }
  236|  2.66k|    if (bswap)
  ------------------
  |  Branch (236:9): [True: 2.05k, False: 611]
  ------------------
  237|  2.05k|        creation_time_diff = byteswap_double(creation_time_diff);
  238|       |    
  239|  2.66k|    if (io->read(&modification_time_diff, sizeof(double), io->io_ctx) < sizeof(double)) {
  ------------------
  |  Branch (239:9): [True: 6, False: 2.66k]
  ------------------
  240|      6|        retval = READSTAT_ERROR_READ;
  241|      6|        goto cleanup;
  242|      6|    }
  243|  2.66k|    if (bswap)
  ------------------
  |  Branch (243:9): [True: 2.05k, False: 609]
  ------------------
  244|  2.05k|        modification_time_diff = byteswap_double(modification_time_diff);
  245|       |    
  246|  2.66k|    hinfo->creation_time = sas_convert_time(creation_time, creation_time_diff, epoch);
  247|  2.66k|    hinfo->modification_time = sas_convert_time(modification_time, modification_time_diff, epoch);
  248|       |
  249|  2.66k|    uint32_t header_size, page_size;
  250|       |
  251|  2.66k|    if (io->read(&header_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (251:9): [True: 8, False: 2.65k]
  ------------------
  252|      8|        retval = READSTAT_ERROR_READ;
  253|      8|        goto cleanup;
  254|      8|    }
  255|  2.65k|    if (io->read(&page_size, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (255:9): [True: 3, False: 2.64k]
  ------------------
  256|      3|        retval = READSTAT_ERROR_READ;
  257|      3|        goto cleanup;
  258|      3|    }
  259|       |
  260|  2.64k|    hinfo->header_size = bswap ? byteswap4(header_size) : header_size;
  ------------------
  |  Branch (260:26): [True: 2.04k, False: 605]
  ------------------
  261|  2.64k|    hinfo->page_size = bswap ? byteswap4(page_size) : page_size;
  ------------------
  |  Branch (261:24): [True: 2.04k, False: 605]
  ------------------
  262|       |
  263|  2.64k|    if (hinfo->header_size < 1024 || hinfo->page_size < 1024) {
  ------------------
  |  Branch (263:9): [True: 10, False: 2.63k]
  |  Branch (263:38): [True: 7, False: 2.63k]
  ------------------
  264|     17|        retval = READSTAT_ERROR_PARSE;
  265|     17|        goto cleanup;
  266|     17|    }
  267|  2.63k|    if (hinfo->header_size > (1<<24) || hinfo->page_size > (1<<24)) {
  ------------------
  |  Branch (267:9): [True: 18, False: 2.61k]
  |  Branch (267:41): [True: 22, False: 2.59k]
  ------------------
  268|     40|        retval = READSTAT_ERROR_PARSE;
  269|     40|        goto cleanup;
  270|     40|    }
  271|       |
  272|  2.59k|    if (hinfo->u64) {
  ------------------
  |  Branch (272:9): [True: 1.22k, False: 1.36k]
  ------------------
  273|  1.22k|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_64BIT;
  ------------------
  |  |  122|  1.22k|#define SAS_PAGE_HEADER_SIZE_64BIT  40
  ------------------
  274|  1.22k|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_64BIT;
  ------------------
  |  |  119|  1.22k|#define SAS_SUBHEADER_POINTER_SIZE_64BIT    24
  ------------------
  275|  1.36k|    } else {
  276|  1.36k|        hinfo->page_header_size = SAS_PAGE_HEADER_SIZE_32BIT;
  ------------------
  |  |  121|  1.36k|#define SAS_PAGE_HEADER_SIZE_32BIT  24
  ------------------
  277|  1.36k|        hinfo->subheader_pointer_size = SAS_SUBHEADER_POINTER_SIZE_32BIT;
  ------------------
  |  |  118|  1.36k|#define SAS_SUBHEADER_POINTER_SIZE_32BIT    12
  ------------------
  278|  1.36k|    }
  279|       |
  280|  2.59k|    if (hinfo->u64) {
  ------------------
  |  Branch (280:9): [True: 1.22k, False: 1.36k]
  ------------------
  281|  1.22k|        uint64_t page_count;
  282|  1.22k|        if (io->read(&page_count, sizeof(uint64_t), io->io_ctx) < sizeof(uint64_t)) {
  ------------------
  |  Branch (282:13): [True: 12, False: 1.21k]
  ------------------
  283|     12|            retval = READSTAT_ERROR_READ;
  284|     12|            goto cleanup;
  285|     12|        }
  286|  1.21k|        hinfo->page_count = bswap ? byteswap8(page_count) : page_count;
  ------------------
  |  Branch (286:29): [True: 1.13k, False: 81]
  ------------------
  287|  1.36k|    } else {
  288|  1.36k|        uint32_t page_count;
  289|  1.36k|        if (io->read(&page_count, sizeof(uint32_t), io->io_ctx) < sizeof(uint32_t)) {
  ------------------
  |  Branch (289:13): [True: 16, False: 1.35k]
  ------------------
  290|     16|            retval = READSTAT_ERROR_READ;
  291|     16|            goto cleanup;
  292|     16|        }
  293|  1.35k|        hinfo->page_count = bswap ? byteswap4(page_count) : page_count;
  ------------------
  |  Branch (293:29): [True: 839, False: 514]
  ------------------
  294|  1.35k|    }
  295|  2.56k|    if (hinfo->page_count > (1<<24)) {
  ------------------
  |  Branch (295:9): [True: 12, False: 2.55k]
  ------------------
  296|     12|        retval = READSTAT_ERROR_PARSE;
  297|     12|        goto cleanup;
  298|     12|    }
  299|       |    
  300|  2.55k|    if (io->seek(8, READSTAT_SEEK_CUR, io->io_ctx) == -1) {
  ------------------
  |  Branch (300:9): [True: 64, False: 2.48k]
  ------------------
  301|     64|        retval = READSTAT_ERROR_SEEK;
  302|     64|        if (error_handler) {
  ------------------
  |  Branch (302:13): [True: 0, False: 64]
  ------------------
  303|      0|            snprintf(error_buf, sizeof(error_buf), "ReadStat: Failed to seek forward by %d", 8);
  304|      0|            error_handler(error_buf, user_ctx);
  305|      0|        }
  306|     64|        goto cleanup;
  307|     64|    }
  308|  2.48k|    if (io->read(&header_end, sizeof(sas_header_end_t), io->io_ctx) < sizeof(sas_header_end_t)) {
  ------------------
  |  Branch (308:9): [True: 15, False: 2.47k]
  ------------------
  309|     15|        retval = READSTAT_ERROR_READ;
  310|     15|        goto cleanup;
  311|     15|    }
  312|  2.47k|    char major, revision_tag;
  313|  2.47k|    int minor, revision;
  314|  2.47k|    if (sscanf(header_end.release, "%c.%04d%c%1d", &major, &minor, &revision_tag, &revision) != 4) {
  ------------------
  |  Branch (314:9): [True: 3, False: 2.47k]
  ------------------
  315|      3|        retval = READSTAT_ERROR_PARSE;
  316|      3|        goto cleanup;
  317|      3|    }
  318|       |
  319|  2.47k|    if (major >= '1' && major <= '9') {
  ------------------
  |  Branch (319:9): [True: 2.46k, False: 10]
  |  Branch (319:25): [True: 2.44k, False: 11]
  ------------------
  320|  2.44k|        hinfo->major_version = major - '0';
  321|  2.44k|    } else if (major == 'V') {
  ------------------
  |  Branch (321:16): [True: 3, False: 18]
  ------------------
  322|       |        // It appears that SAS Visual Forecaster reports the major version as "V"
  323|       |        // Treat it as version 9 for all intents and purposes
  324|      3|        hinfo->major_version = 9;
  325|     18|    } else {
  326|     18|        retval = READSTAT_ERROR_PARSE;
  327|     18|        goto cleanup;
  328|     18|    }
  329|       |    // revision_tag is usually M, but J has been observed in the wild (not created with SAS?)
  330|  2.45k|    if (revision_tag != 'M' && revision_tag != 'J') {
  ------------------
  |  Branch (330:9): [True: 24, False: 2.42k]
  |  Branch (330:32): [True: 14, False: 10]
  ------------------
  331|     14|        retval = READSTAT_ERROR_PARSE;
  332|     14|        goto cleanup;
  333|     14|    }
  334|  2.43k|    hinfo->minor_version = minor;
  335|  2.43k|    hinfo->revision = revision;
  336|       |
  337|  2.43k|    if ((major == '8' || major == '9') && minor == 0 && revision == 0) {
  ------------------
  |  Branch (337:10): [True: 231, False: 2.20k]
  |  Branch (337:26): [True: 140, False: 2.06k]
  |  Branch (337:43): [True: 73, False: 298]
  |  Branch (337:57): [True: 58, False: 15]
  ------------------
  338|       |        /* A bit of a hack, but most SAS installations are running a minor update */
  339|     58|        hinfo->vendor = READSTAT_VENDOR_STAT_TRANSFER;
  340|  2.38k|    } else {
  341|  2.38k|        hinfo->vendor = READSTAT_VENDOR_SAS;
  342|  2.38k|    }
  343|  2.43k|    if (io->seek(hinfo->header_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (343:9): [True: 38, False: 2.40k]
  ------------------
  344|     38|        retval = READSTAT_ERROR_SEEK;
  345|     38|        if (error_handler) {
  ------------------
  |  Branch (345:13): [True: 0, False: 38]
  ------------------
  346|      0|            snprintf(error_buf, sizeof(error_buf), 
  347|      0|                    "ReadStat: Failed to seek to position %" PRId64, hinfo->header_size);
  348|      0|            error_handler(error_buf, user_ctx);
  349|      0|        }
  350|     38|        goto cleanup;
  351|     38|    }
  352|       |
  353|  2.79k|cleanup:
  354|  2.79k|    return retval;
  355|  2.43k|}
sas_validate_tag:
  507|  7.65k|readstat_error_t sas_validate_tag(char tag) {
  508|  7.65k|    if (tag == '_' || (tag >= 'A' && tag <= 'Z'))
  ------------------
  |  Branch (508:9): [True: 3.97k, False: 3.68k]
  |  Branch (508:24): [True: 1.44k, False: 2.23k]
  |  Branch (508:38): [True: 825, False: 618]
  ------------------
  509|  4.80k|        return READSTAT_OK;
  510|       |
  511|  2.85k|    return READSTAT_ERROR_TAGGED_VALUE_IS_OUT_OF_RANGE;
  512|  7.65k|}
sas_assign_tag:
  514|  7.65k|void sas_assign_tag(readstat_value_t *value, uint8_t tag) {
  515|       |    /* We accommodate two tag schemes. In the first, the tag is an ASCII code
  516|       |     * given by uint8_t tag above. System missing is represented by an ASCII
  517|       |     * period. In the second scheme, (tag-2) is an offset from 'A', except when
  518|       |     * tag == 0, in which case it represents an underscore, or tag == 1, in
  519|       |     * which case it represents system-missing.
  520|       |     */
  521|  7.65k|    if (tag == 0) {
  ------------------
  |  Branch (521:9): [True: 3.97k, False: 3.68k]
  ------------------
  522|  3.97k|        tag = '_';
  523|  3.97k|    } else if (tag >= 2 && tag < 28) {
  ------------------
  |  Branch (523:16): [True: 3.24k, False: 437]
  |  Branch (523:28): [True: 720, False: 2.52k]
  ------------------
  524|    720|        tag = 'A' + (tag - 2);
  525|    720|    }
  526|  7.65k|    if (sas_validate_tag(tag) == READSTAT_OK) {
  ------------------
  |  Branch (526:9): [True: 4.80k, False: 2.85k]
  ------------------
  527|  4.80k|        value->tag = tag;
  528|  4.80k|        value->is_tagged_missing = 1;
  529|  4.80k|    } else {
  530|  2.85k|        value->tag = 0;
  531|  2.85k|        value->is_system_missing = 1;
  532|  2.85k|    }
  533|  7.65k|}
readstat_sas.c:sas_epoch:
  123|  2.79k|static time_t sas_epoch(void) {
  124|  2.79k|    return - 3653 * 86400; // seconds between 01-01-1960 and 01-01-1970
  125|  2.79k|}
readstat_sas.c:sas_convert_time:
  127|  5.32k|static time_t sas_convert_time(double time, double time_diff, time_t epoch) {
  128|  5.32k|    time -= time_diff;
  129|  5.32k|    time += epoch;
  130|  5.32k|    if (isnan(time))
  ------------------
  |  Branch (130:9): [True: 660, False: 4.66k]
  ------------------
  131|    660|        return 0;
  132|  4.66k|    if (time > (double)LONG_MAX)
  ------------------
  |  Branch (132:9): [True: 1.22k, False: 3.43k]
  ------------------
  133|  1.22k|        return LONG_MAX;
  134|  3.43k|    if (time < (double)LONG_MIN)
  ------------------
  |  Branch (134:9): [True: 888, False: 2.54k]
  ------------------
  135|    888|        return LONG_MIN;
  136|  2.54k|    return time;
  137|  3.43k|}

readstat_parse_sas7bdat:
 1282|  2.79k|readstat_error_t readstat_parse_sas7bdat(readstat_parser_t *parser, const char *path, void *user_ctx) {
 1283|  2.79k|    int64_t last_examined_page_pass1 = 0;
 1284|  2.79k|    readstat_error_t retval = READSTAT_OK;
 1285|  2.79k|    readstat_io_t *io = parser->io;
 1286|       |
 1287|  2.79k|    sas7bdat_ctx_t  *ctx = calloc(1, sizeof(sas7bdat_ctx_t));
 1288|  2.79k|    sas_header_info_t  *hinfo = calloc(1, sizeof(sas_header_info_t));
 1289|       |
 1290|  2.79k|    if (ctx == NULL || hinfo == NULL) {
  ------------------
  |  Branch (1290:9): [True: 0, False: 2.79k]
  |  Branch (1290:24): [True: 0, False: 2.79k]
  ------------------
 1291|      0|        retval = READSTAT_ERROR_MALLOC;
 1292|      0|        goto cleanup;
 1293|      0|    }
 1294|       |
 1295|  2.79k|    ctx->handle = parser->handlers;
 1296|  2.79k|    ctx->input_encoding = parser->input_encoding;
 1297|  2.79k|    ctx->output_encoding = parser->output_encoding;
 1298|  2.79k|    ctx->user_ctx = user_ctx;
 1299|  2.79k|    ctx->io = parser->io;
 1300|  2.79k|    ctx->row_limit = parser->row_limit;
 1301|  2.79k|    if (parser->row_offset > 0)
  ------------------
  |  Branch (1301:9): [True: 0, False: 2.79k]
  ------------------
 1302|      0|        ctx->row_offset = parser->row_offset;
 1303|       |
 1304|  2.79k|    if (io->open(path, io->io_ctx) == -1) {
  ------------------
  |  Branch (1304:9): [True: 0, False: 2.79k]
  ------------------
 1305|      0|        retval = READSTAT_ERROR_OPEN;
 1306|      0|        goto cleanup;
 1307|      0|    }
 1308|       |
 1309|  2.79k|    if ((ctx->file_size = io->seek(0, READSTAT_SEEK_END, io->io_ctx)) == -1) {
  ------------------
  |  Branch (1309:9): [True: 0, False: 2.79k]
  ------------------
 1310|      0|        retval = READSTAT_ERROR_SEEK;
 1311|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1311:13): [True: 0, False: 0]
  ------------------
 1312|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to end of file");
 1313|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1314|      0|        }
 1315|      0|        goto cleanup;
 1316|      0|    }
 1317|       |
 1318|  2.79k|    if (io->seek(0, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1318:9): [True: 0, False: 2.79k]
  ------------------
 1319|      0|        retval = READSTAT_ERROR_SEEK;
 1320|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1320:13): [True: 0, False: 0]
  ------------------
 1321|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to beginning of file");
 1322|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1323|      0|        }
 1324|      0|        goto cleanup;
 1325|      0|    }
 1326|       |
 1327|  2.79k|    if ((retval = sas_read_header(io, hinfo, ctx->handle.error, user_ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1327:9): [True: 397, False: 2.40k]
  ------------------
 1328|    397|        goto cleanup;
 1329|    397|    }
 1330|       |
 1331|  2.40k|    ctx->u64 = hinfo->u64;
 1332|  2.40k|    ctx->little_endian = hinfo->little_endian;
 1333|  2.40k|    ctx->vendor = hinfo->vendor;
 1334|  2.40k|    ctx->bswap = machine_is_little_endian() ^ hinfo->little_endian;
 1335|  2.40k|    ctx->header_size = hinfo->header_size;
 1336|  2.40k|    ctx->page_count = hinfo->page_count;
 1337|  2.40k|    ctx->page_size = hinfo->page_size;
 1338|  2.40k|    ctx->page_header_size = hinfo->page_header_size;
 1339|  2.40k|    ctx->subheader_pointer_size = hinfo->subheader_pointer_size;
 1340|  2.40k|    ctx->subheader_signature_size = ctx->u64 ? 8 : 4;
  ------------------
  |  Branch (1340:37): [True: 1.16k, False: 1.23k]
  ------------------
 1341|  2.40k|    ctx->ctime = hinfo->creation_time;
 1342|  2.40k|    ctx->mtime = hinfo->modification_time;
 1343|  2.40k|    ctx->version = hinfo->major_version;
 1344|  2.40k|    if (ctx->input_encoding == NULL) {
  ------------------
  |  Branch (1344:9): [True: 2.40k, False: 0]
  ------------------
 1345|  2.40k|        ctx->input_encoding = hinfo->encoding;
 1346|  2.40k|    }
 1347|  2.40k|    if ((ctx->page = readstat_malloc(ctx->page_size)) == NULL) {
  ------------------
  |  Branch (1347:9): [True: 0, False: 2.40k]
  ------------------
 1348|      0|        retval = READSTAT_ERROR_MALLOC;
 1349|      0|        goto cleanup;
 1350|      0|    }
 1351|       |
 1352|  2.40k|    if (ctx->input_encoding && ctx->output_encoding && strcmp(ctx->input_encoding, ctx->output_encoding) != 0) {
  ------------------
  |  Branch (1352:9): [True: 2.40k, False: 0]
  |  Branch (1352:32): [True: 2.40k, False: 0]
  |  Branch (1352:56): [True: 1.73k, False: 666]
  ------------------
 1353|  1.73k|        iconv_t converter = iconv_open(ctx->output_encoding, ctx->input_encoding);
 1354|  1.73k|        if (converter == (iconv_t)-1) {
  ------------------
  |  Branch (1354:13): [True: 1, False: 1.73k]
  ------------------
 1355|      1|            retval = READSTAT_ERROR_UNSUPPORTED_CHARSET;
 1356|      1|            goto cleanup;
 1357|      1|        }
 1358|  1.73k|        ctx->converter = converter;
 1359|  1.73k|    }
 1360|       |
 1361|  2.39k|    if ((retval = readstat_convert(ctx->table_name, sizeof(ctx->table_name),
  ------------------
  |  Branch (1361:9): [True: 2, False: 2.39k]
  ------------------
 1362|  2.39k|                hinfo->table_name, sizeof(hinfo->table_name), ctx->converter)) != READSTAT_OK) {
 1363|      2|        goto cleanup;
 1364|      2|    }
 1365|       |
 1366|  2.39k|    if ((retval = sas7bdat_parse_meta_pages_pass1(ctx, &last_examined_page_pass1)) != READSTAT_OK) {
  ------------------
  |  Branch (1366:9): [True: 187, False: 2.21k]
  ------------------
 1367|    187|        goto cleanup;
 1368|    187|    }
 1369|       |
 1370|  2.21k|    if ((retval = sas7bdat_parse_amd_pages_pass1(last_examined_page_pass1, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1370:9): [True: 797, False: 1.41k]
  ------------------
 1371|    797|        goto cleanup;
 1372|    797|    }
 1373|       |
 1374|  1.41k|    if (io->seek(ctx->header_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1374:9): [True: 0, False: 1.41k]
  ------------------
 1375|      0|        retval = READSTAT_ERROR_SEEK;
 1376|      0|        if (ctx->handle.error) {
  ------------------
  |  Branch (1376:13): [True: 0, False: 0]
  ------------------
 1377|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64, 
 1378|      0|                    ctx->header_size);
 1379|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1380|      0|        }
 1381|      0|        goto cleanup;
 1382|      0|    }
 1383|       |
 1384|  1.41k|    if ((retval = sas7bdat_parse_all_pages_pass2(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1384:9): [True: 1.15k, False: 261]
  ------------------
 1385|  1.15k|        goto cleanup;
 1386|  1.15k|    }
 1387|       |    
 1388|    261|    if ((retval = sas7bdat_submit_columns_if_needed(ctx, 0)) != READSTAT_OK) {
  ------------------
  |  Branch (1388:9): [True: 66, False: 195]
  ------------------
 1389|     66|        goto cleanup;
 1390|     66|    }
 1391|       |
 1392|    195|    if (ctx->handle.value && ctx->parsed_row_count != ctx->row_limit) {
  ------------------
  |  Branch (1392:9): [True: 195, False: 0]
  |  Branch (1392:30): [True: 58, False: 137]
  ------------------
 1393|     58|        retval = READSTAT_ERROR_ROW_COUNT_MISMATCH;
 1394|     58|        if (ctx->handle.error) {
  ------------------
  |  Branch (1394:13): [True: 0, False: 58]
  ------------------
 1395|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Expected %d rows in file, found %d",
 1396|      0|                    ctx->row_limit, ctx->parsed_row_count);
 1397|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1398|      0|        }
 1399|     58|        goto cleanup;
 1400|     58|    }
 1401|       |
 1402|    137|    if ((retval = sas7bdat_update_progress(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1402:9): [True: 0, False: 137]
  ------------------
 1403|      0|        goto cleanup;
 1404|      0|    }
 1405|       |
 1406|  2.79k|cleanup:
 1407|  2.79k|    io->close(io->io_ctx);
 1408|       |
 1409|  2.79k|    if (retval == READSTAT_ERROR_OPEN ||
  ------------------
  |  Branch (1409:9): [True: 0, False: 2.79k]
  ------------------
 1410|  2.79k|            retval == READSTAT_ERROR_READ ||
  ------------------
  |  Branch (1410:13): [True: 399, False: 2.39k]
  ------------------
 1411|  2.39k|            retval == READSTAT_ERROR_SEEK) {
  ------------------
  |  Branch (1411:13): [True: 295, False: 2.10k]
  ------------------
 1412|    694|        if (ctx->handle.error) {
  ------------------
  |  Branch (1412:13): [True: 0, False: 694]
  ------------------
 1413|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: %s (retval = %d): %s (errno = %d)", 
 1414|      0|                    readstat_error_message(retval), retval, strerror(errno), errno);
 1415|      0|            ctx->handle.error(ctx->error_buf, user_ctx);
 1416|      0|        }
 1417|    694|    }
 1418|       |
 1419|  2.79k|    if (ctx)
  ------------------
  |  Branch (1419:9): [True: 2.79k, False: 0]
  ------------------
 1420|  2.79k|        sas7bdat_ctx_free(ctx);
 1421|  2.79k|    if (hinfo)
  ------------------
  |  Branch (1421:9): [True: 2.79k, False: 0]
  ------------------
 1422|  2.79k|        free(hinfo);
 1423|       |
 1424|  2.79k|    return retval;
 1425|    137|}
readstat_sas7bdat_read.c:sas7bdat_parse_meta_pages_pass1:
 1123|  2.39k|static readstat_error_t sas7bdat_parse_meta_pages_pass1(sas7bdat_ctx_t *ctx, int64_t *outLastExaminedPage) {
 1124|  2.39k|    readstat_error_t retval = READSTAT_OK;
 1125|  2.39k|    readstat_io_t *io = ctx->io;
 1126|  2.39k|    int64_t i;
 1127|       |
 1128|       |    /* look for META and MIX pages at beginning... */
 1129|  7.37k|    for (i=0; i<ctx->page_count; i++) {
  ------------------
  |  Branch (1129:15): [True: 5.70k, False: 1.67k]
  ------------------
 1130|  5.70k|        if (io->seek(ctx->header_size + i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1130:13): [True: 7, False: 5.69k]
  ------------------
 1131|      7|            retval = READSTAT_ERROR_SEEK;
 1132|      7|            if (ctx->handle.error) {
  ------------------
  |  Branch (1132:17): [True: 0, False: 7]
  ------------------
 1133|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64 
 1134|      0|                        " (= %" PRId64 " + %" PRId64 "*%" PRId64 ")",
 1135|      0|                        ctx->header_size + i*ctx->page_size, ctx->header_size, i, ctx->page_size);
 1136|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1137|      0|            }
 1138|      7|            goto cleanup;
 1139|      7|        }
 1140|       |
 1141|  5.69k|        readstat_off_t off = 0;
 1142|  5.69k|        if (ctx->u64)
  ------------------
  |  Branch (1142:13): [True: 3.79k, False: 1.89k]
  ------------------
 1143|  3.79k|            off = 16;
 1144|       |
 1145|  5.69k|        size_t head_len = off + 16 + 2;
 1146|  5.69k|        size_t tail_len = ctx->page_size - head_len;
 1147|       |
 1148|  5.69k|        if (io->read(ctx->page, head_len, io->io_ctx) < head_len) {
  ------------------
  |  Branch (1148:13): [True: 108, False: 5.58k]
  ------------------
 1149|    108|            retval = READSTAT_ERROR_READ;
 1150|    108|            goto cleanup;
 1151|    108|        }
 1152|       |
 1153|  5.58k|        uint16_t page_type = sas_read2(&ctx->page[off+16], ctx->bswap);
 1154|       |
 1155|  5.58k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA)
  ------------------
  |  |  112|  5.58k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA)
  ------------------
  |  |  109|  5.58k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1155:13): [True: 532, False: 5.05k]
  ------------------
 1156|    532|            break;
 1157|  5.05k|        if ((page_type & SAS_PAGE_TYPE_COMP))
  ------------------
  |  |  116|  5.05k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1157:13): [True: 1.33k, False: 3.71k]
  ------------------
 1158|  1.33k|            continue;
 1159|       |
 1160|  3.71k|        if (io->read(ctx->page + head_len, tail_len, io->io_ctx) < tail_len) {
  ------------------
  |  Branch (1160:13): [True: 28, False: 3.68k]
  ------------------
 1161|     28|            retval = READSTAT_ERROR_READ;
 1162|     28|            goto cleanup;
 1163|     28|        }
 1164|       |
 1165|  3.68k|        if ((retval = sas7bdat_parse_page_pass1(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1165:13): [True: 44, False: 3.64k]
  ------------------
 1166|     44|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1166:17): [True: 0, False: 44]
  |  Branch (1166:38): [True: 0, False: 0]
  ------------------
 1167|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1168|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1169|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1170|      0|                        i, pos - ctx->page_size, pos-1);
 1171|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1172|      0|            }
 1173|     44|            goto cleanup;
 1174|     44|        }
 1175|  3.68k|    }
 1176|       |
 1177|  2.39k|cleanup:
 1178|  2.39k|    if (outLastExaminedPage)
  ------------------
  |  Branch (1178:9): [True: 2.39k, False: 0]
  ------------------
 1179|  2.39k|        *outLastExaminedPage = i;
 1180|       |
 1181|  2.39k|    return retval;
 1182|  2.39k|}
readstat_sas7bdat_read.c:sas7bdat_parse_page_pass1:
  949|  5.92k|static readstat_error_t sas7bdat_parse_page_pass1(const char *page, size_t page_size, sas7bdat_ctx_t *ctx) {
  950|  5.92k|    readstat_error_t retval = READSTAT_OK;
  951|       |
  952|  5.92k|    uint16_t subheader_count = sas_read2(&page[ctx->page_header_size-4], ctx->bswap);
  953|       |
  954|  5.92k|    int i;
  955|  5.92k|    const char *shp = &page[ctx->page_header_size];
  956|  5.92k|    int lshp = ctx->subheader_pointer_size;
  957|       |
  958|  5.92k|    if (ctx->page_header_size + subheader_count*lshp > page_size) {
  ------------------
  |  Branch (958:9): [True: 38, False: 5.88k]
  ------------------
  959|     38|        retval = READSTAT_ERROR_PARSE;
  960|     38|        goto cleanup;
  961|     38|    }
  962|       |
  963|  59.6k|    for (i=0; i<subheader_count; i++) {
  ------------------
  |  Branch (963:15): [True: 54.3k, False: 5.28k]
  ------------------
  964|  54.3k|        subheader_pointer_t shp_info = { 0 };
  965|  54.3k|        if ((retval = sas7bdat_parse_subheader_pointer(shp, page + page_size - shp, &shp_info, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (965:13): [True: 0, False: 54.3k]
  ------------------
  966|      0|            goto cleanup;
  967|      0|        }
  968|  54.3k|        if (shp_info.len > 0 && shp_info.compression != SAS_COMPRESSION_TRUNC) {
  ------------------
  |  |  125|  35.9k|#define SAS_COMPRESSION_TRUNC        0x01
  ------------------
  |  Branch (968:13): [True: 35.9k, False: 18.3k]
  |  Branch (968:33): [True: 30.9k, False: 4.95k]
  ------------------
  969|  30.9k|            if ((retval = sas7bdat_validate_subheader_pointer(&shp_info, page_size, subheader_count, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (969:17): [True: 526, False: 30.4k]
  ------------------
  970|    526|                goto cleanup;
  971|    526|            }
  972|  30.4k|            if (shp_info.compression == SAS_COMPRESSION_NONE) {
  ------------------
  |  |  124|  30.4k|#define SAS_COMPRESSION_NONE         0x00
  ------------------
  |  Branch (972:17): [True: 27.4k, False: 2.97k]
  ------------------
  973|  27.4k|                sas_subheader_type_t subheader_type = sas7bdat_parse_subheader_type(page + shp_info.offset, ctx);
  974|  27.4k|                if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (974:21): [True: 2.04k, False: 25.4k]
  ------------------
  975|  2.04k|                    if ((retval = sas7bdat_parse_subheader(subheader_type, page + shp_info.offset, shp_info.len, ctx))
  ------------------
  |  Branch (975:25): [True: 55, False: 1.98k]
  ------------------
  976|  2.04k|                            != READSTAT_OK) {
  977|     55|                        goto cleanup;
  978|     55|                    }
  979|  2.04k|                }
  980|  27.4k|            } else if (shp_info.compression == SAS_COMPRESSION_ROW || shp_info.compression == SAS_COMPRESSION_DELETED_ROW) {
  ------------------
  |  |  126|  5.94k|#define SAS_COMPRESSION_ROW          0x04
  ------------------
                          } else if (shp_info.compression == SAS_COMPRESSION_ROW || shp_info.compression == SAS_COMPRESSION_DELETED_ROW) {
  ------------------
  |  |  127|    259|#define SAS_COMPRESSION_DELETED_ROW  0x05
  ------------------
  |  Branch (980:24): [True: 2.71k, False: 259]
  |  Branch (980:71): [True: 240, False: 19]
  ------------------
  981|       |                /* void */
  982|  2.95k|            } else {
  983|     19|                retval = READSTAT_ERROR_UNSUPPORTED_COMPRESSION;
  984|     19|                goto cleanup;
  985|     19|            }
  986|  30.4k|        }
  987|       |
  988|  53.7k|        shp += lshp;
  989|  53.7k|    }
  990|       |
  991|  5.92k|cleanup:
  992|       |
  993|  5.92k|    return retval;
  994|  5.88k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_pointer:
  903|  85.8k|        subheader_pointer_t *info, sas7bdat_ctx_t *ctx) {
  904|  85.8k|    readstat_error_t retval = READSTAT_OK;
  905|  85.8k|    if (ctx->u64) {
  ------------------
  |  Branch (905:9): [True: 17.8k, False: 68.0k]
  ------------------
  906|  17.8k|        if (shp_size <= 17) {
  ------------------
  |  Branch (906:13): [True: 0, False: 17.8k]
  ------------------
  907|      0|            retval = READSTAT_ERROR_PARSE;
  908|      0|            goto cleanup;
  909|      0|        }
  910|  17.8k|        info->offset = sas_read8(&shp[0], ctx->bswap);
  911|  17.8k|        info->len = sas_read8(&shp[8], ctx->bswap);
  912|  17.8k|        info->compression = shp[16];
  913|  17.8k|        info->is_compressed_data = shp[17];
  914|  68.0k|    } else {
  915|  68.0k|        if (shp_size <= 9) {
  ------------------
  |  Branch (915:13): [True: 0, False: 68.0k]
  ------------------
  916|      0|            retval = READSTAT_ERROR_PARSE;
  917|      0|            goto cleanup;
  918|      0|        }
  919|  68.0k|        info->offset = sas_read4(&shp[0], ctx->bswap);
  920|  68.0k|        info->len = sas_read4(&shp[4], ctx->bswap);
  921|  68.0k|        info->compression = shp[8];
  922|  68.0k|        info->is_compressed_data = shp[9];
  923|  68.0k|    }
  924|  85.8k|cleanup:
  925|  85.8k|    return retval;
  926|  85.8k|}
readstat_sas7bdat_read.c:sas7bdat_validate_subheader_pointer:
  929|  48.0k|        uint16_t subheader_count, sas7bdat_ctx_t *ctx) {
  930|  48.0k|    if (shp_info->offset > page_size)
  ------------------
  |  Branch (930:9): [True: 417, False: 47.6k]
  ------------------
  931|    417|        return READSTAT_ERROR_PARSE;
  932|  47.6k|    if (shp_info->len > page_size)
  ------------------
  |  Branch (932:9): [True: 217, False: 47.4k]
  ------------------
  933|    217|        return READSTAT_ERROR_PARSE;
  934|  47.4k|    if (shp_info->offset + shp_info->len > page_size)
  ------------------
  |  Branch (934:9): [True: 26, False: 47.4k]
  ------------------
  935|     26|        return READSTAT_ERROR_PARSE;
  936|  47.4k|    if (shp_info->offset < ctx->page_header_size + subheader_count*ctx->subheader_pointer_size)
  ------------------
  |  Branch (936:9): [True: 14, False: 47.3k]
  ------------------
  937|     14|        return READSTAT_ERROR_PARSE;
  938|  47.3k|    if (shp_info->compression == SAS_COMPRESSION_NONE) {
  ------------------
  |  |  124|  47.3k|#define SAS_COMPRESSION_NONE         0x00
  ------------------
  |  Branch (938:9): [True: 42.9k, False: 4.47k]
  ------------------
  939|  42.9k|        if (shp_info->len < ctx->subheader_signature_size)
  ------------------
  |  Branch (939:13): [True: 5, False: 42.9k]
  ------------------
  940|      5|            return READSTAT_ERROR_PARSE;
  941|  42.9k|        if (shp_info->offset + ctx->subheader_signature_size > page_size)
  ------------------
  |  Branch (941:13): [True: 0, False: 42.9k]
  ------------------
  942|      0|            return READSTAT_ERROR_PARSE;
  943|  42.9k|    }
  944|       |    
  945|  47.3k|    return READSTAT_OK;
  946|  47.3k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_type:
  883|  42.9k|static sas_subheader_type_t sas7bdat_parse_subheader_type(const char* subheader, sas7bdat_ctx_t* ctx) {
  884|  42.9k|    if (!ctx->u64) {
  ------------------
  |  Branch (884:9): [True: 30.9k, False: 11.9k]
  ------------------
  885|  30.9k|        uint32_t signature_32 = sas_read4(subheader, ctx->bswap);
  886|  30.9k|        return sas7bdat_parse_subheader_type_32(signature_32);
  887|  30.9k|    }
  888|       |
  889|  11.9k|    uint64_t signature = sas_read8(subheader, ctx->bswap);
  890|  11.9k|    if (signature == SAS_SUBHEADER_SIGNATURE_ROW_SIZE) {
  ------------------
  |  |   92|  11.9k|#define SAS_SUBHEADER_SIGNATURE_ROW_SIZE       0xF7F7F7F7
  ------------------
  |  Branch (890:9): [True: 1.52k, False: 10.4k]
  ------------------
  891|  1.52k|        return SAS_SUBHEADER_TYPE_ROW_SIZE;
  892|  10.4k|    } else if (signature == SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE) {
  ------------------
  |  |   93|  10.4k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE    0xF6F6F6F6
  ------------------
  |  Branch (892:16): [True: 471, False: 9.93k]
  ------------------
  893|    471|        return SAS_SUBHEADER_TYPE_COLUMN_SIZE;
  894|  9.93k|    } else if ((signature & SAS_SUBHEADER_SIGNATURE_64BIT_MASK) != SAS_SUBHEADER_SIGNATURE_64BIT_MASK) {
  ------------------
  |  |  105|  9.93k|#define SAS_SUBHEADER_SIGNATURE_64BIT_MASK     0xFFFFFFFF00000000
  ------------------
                  } else if ((signature & SAS_SUBHEADER_SIGNATURE_64BIT_MASK) != SAS_SUBHEADER_SIGNATURE_64BIT_MASK) {
  ------------------
  |  |  105|  9.93k|#define SAS_SUBHEADER_SIGNATURE_64BIT_MASK     0xFFFFFFFF00000000
  ------------------
  |  Branch (894:16): [True: 1.36k, False: 8.57k]
  ------------------
  895|  1.36k|        return SAS_SUBHEADER_TYPE_DATA;
  896|  1.36k|    }
  897|       |
  898|  8.57k|    uint32_t lower_bytes = (uint32_t)(signature & SAS_SUBHEADER_SIGNATURE_32BIT_MASK);
  ------------------
  |  |  106|  8.57k|#define SAS_SUBHEADER_SIGNATURE_32BIT_MASK     0x00000000FFFFFFFF
  ------------------
  899|  8.57k|    return sas7bdat_parse_subheader_type_32(lower_bytes);
  900|  11.9k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_type_32:
  857|  39.5k|static sas_subheader_type_t sas7bdat_parse_subheader_type_32(uint32_t signature) {
  858|  39.5k|    switch (signature) {
  859|  5.29k|        case SAS_SUBHEADER_SIGNATURE_ROW_SIZE:
  ------------------
  |  |   92|  5.29k|#define SAS_SUBHEADER_SIGNATURE_ROW_SIZE       0xF7F7F7F7
  ------------------
  |  Branch (859:9): [True: 5.29k, False: 34.2k]
  ------------------
  860|  5.29k|            return SAS_SUBHEADER_TYPE_ROW_SIZE;
  861|  2.83k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE:
  ------------------
  |  |   93|  2.83k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE    0xF6F6F6F6
  ------------------
  |  Branch (861:9): [True: 2.83k, False: 36.7k]
  ------------------
  862|  2.83k|            return SAS_SUBHEADER_TYPE_COLUMN_SIZE;
  863|  2.71k|        case SAS_SUBHEADER_SIGNATURE_COUNTS:
  ------------------
  |  |   94|  2.71k|#define SAS_SUBHEADER_SIGNATURE_COUNTS         0xFFFFFC00
  ------------------
  |  Branch (863:9): [True: 2.71k, False: 36.8k]
  ------------------
  864|  2.71k|            return SAS_SUBHEADER_TYPE_COUNTS;
  865|  7.03k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_FORMAT:
  ------------------
  |  |   95|  7.03k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_FORMAT  0xFFFFFBFE
  ------------------
  |  Branch (865:9): [True: 7.03k, False: 32.5k]
  ------------------
  866|  7.03k|            return SAS_SUBHEADER_TYPE_COLUMN_FORMAT;
  867|  4.92k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_ATTRS:
  ------------------
  |  |  100|  4.92k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_ATTRS   0xFFFFFFFC
  ------------------
  |  Branch (867:9): [True: 4.92k, False: 34.6k]
  ------------------
  868|  4.92k|            return SAS_SUBHEADER_TYPE_COLUMN_ATTRS;
  869|  3.23k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_TEXT:
  ------------------
  |  |  101|  3.23k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_TEXT    0xFFFFFFFD
  ------------------
  |  Branch (869:9): [True: 3.23k, False: 36.3k]
  ------------------
  870|  3.23k|            return SAS_SUBHEADER_TYPE_COLUMN_TEXT;
  871|  2.75k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_LIST:
  ------------------
  |  |  102|  2.75k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_LIST    0xFFFFFFFE
  ------------------
  |  Branch (871:9): [True: 2.75k, False: 36.8k]
  ------------------
  872|  2.75k|            return SAS_SUBHEADER_TYPE_COLUMN_LIST;
  873|  6.36k|        case SAS_SUBHEADER_SIGNATURE_COLUMN_NAME:
  ------------------
  |  |  103|  6.36k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_NAME    0xFFFFFFFF
  ------------------
  |  Branch (873:9): [True: 6.36k, False: 33.1k]
  ------------------
  874|  6.36k|            return SAS_SUBHEADER_TYPE_COLUMN_NAME;
  875|  4.40k|        default:
  ------------------
  |  Branch (875:9): [True: 4.40k, False: 35.1k]
  ------------------
  876|  4.40k|            if ((signature & SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) == SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) {
  ------------------
  |  |   97|  4.40k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_MASK    0xFFFFFFF8
  ------------------
                          if ((signature & SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) == SAS_SUBHEADER_SIGNATURE_COLUMN_MASK) {
  ------------------
  |  |   97|  4.40k|#define SAS_SUBHEADER_SIGNATURE_COLUMN_MASK    0xFFFFFFF8
  ------------------
  |  Branch (876:17): [True: 1.64k, False: 2.75k]
  ------------------
  877|  1.64k|                return SAS_SUBHEADER_TYPE_UNKNOWN;
  878|  1.64k|            }
  879|  2.75k|            return SAS_SUBHEADER_TYPE_DATA;
  880|  39.5k|    }
  881|  39.5k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader:
  676|  15.8k|        size_t len, sas7bdat_ctx_t *ctx) {
  677|  15.8k|    readstat_error_t retval = READSTAT_OK;
  678|       |
  679|  15.8k|    if (len < 2 + ctx->subheader_signature_size) {
  ------------------
  |  Branch (679:9): [True: 5, False: 15.8k]
  ------------------
  680|      5|        retval = READSTAT_ERROR_PARSE;
  681|      5|        goto cleanup;
  682|      5|    }
  683|  15.8k|    if (subheader_type == SAS_SUBHEADER_TYPE_ROW_SIZE) {
  ------------------
  |  Branch (683:9): [True: 2.33k, False: 13.4k]
  ------------------
  684|  2.33k|        retval = sas7bdat_parse_row_size_subheader(subheader, len, ctx);
  685|  13.4k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_SIZE) {
  ------------------
  |  Branch (685:16): [True: 1.11k, False: 12.3k]
  ------------------
  686|  1.11k|        retval = sas7bdat_parse_column_size_subheader(subheader, len, ctx);
  687|  12.3k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COUNTS) {
  ------------------
  |  Branch (687:16): [True: 1.18k, False: 11.1k]
  ------------------
  688|       |        /* void */
  689|  11.1k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (689:16): [True: 2.04k, False: 9.13k]
  ------------------
  690|  2.04k|        retval = sas7bdat_parse_column_text_subheader(subheader, len, ctx);
  691|  9.13k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_NAME) {
  ------------------
  |  Branch (691:16): [True: 2.39k, False: 6.74k]
  ------------------
  692|  2.39k|        retval = sas7bdat_parse_column_name_subheader(subheader, len, ctx);
  693|  6.74k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_ATTRS) {
  ------------------
  |  Branch (693:16): [True: 2.10k, False: 4.63k]
  ------------------
  694|  2.10k|        retval = sas7bdat_parse_column_attributes_subheader(subheader, len, ctx);
  695|  4.63k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_FORMAT) {
  ------------------
  |  Branch (695:16): [True: 3.08k, False: 1.55k]
  ------------------
  696|  3.08k|        retval = sas7bdat_parse_column_format_subheader(subheader, len, ctx);
  697|  3.08k|    } else if (subheader_type == SAS_SUBHEADER_TYPE_COLUMN_LIST) {
  ------------------
  |  Branch (697:16): [True: 828, False: 726]
  ------------------
  698|       |        /* void */
  699|    828|    } else if (subheader_type == SAS_SUBHEADER_TYPE_UNKNOWN) {
  ------------------
  |  Branch (699:16): [True: 698, False: 28]
  ------------------
  700|       |        /* void */
  701|    698|    } else {
  702|     28|        retval = READSTAT_ERROR_PARSE;
  703|     28|    }
  704|       |
  705|  15.8k|cleanup:
  706|       |
  707|  15.8k|    return retval;
  708|  15.8k|}
readstat_sas7bdat_read.c:sas7bdat_parse_row_size_subheader:
  236|  2.33k|static readstat_error_t sas7bdat_parse_row_size_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  237|  2.33k|    readstat_error_t retval = READSTAT_OK;
  238|  2.33k|    uint64_t total_row_count;
  239|  2.33k|    uint64_t row_length, deleted_row_count, page_row_count;
  240|       |
  241|  2.33k|    if (len < (ctx->u64 ? 250: 190)) {
  ------------------
  |  Branch (241:9): [True: 3, False: 2.33k]
  |  Branch (241:16): [True: 661, False: 1.67k]
  ------------------
  242|      3|        retval = READSTAT_ERROR_PARSE;
  243|      3|        goto cleanup;
  244|      3|    }
  245|       |
  246|  2.33k|    if (ctx->u64) {
  ------------------
  |  Branch (246:9): [True: 661, False: 1.67k]
  ------------------
  247|    661|        row_length = sas_read8(&subheader[40], ctx->bswap);
  248|    661|        total_row_count = sas_read8(&subheader[48], ctx->bswap);
  249|    661|        deleted_row_count = sas_read8(&subheader[56], ctx->bswap);
  250|    661|        page_row_count = sas_read8(&subheader[120], ctx->bswap);
  251|  1.67k|    } else {
  252|  1.67k|        row_length = sas_read4(&subheader[20], ctx->bswap);
  253|  1.67k|        total_row_count = sas_read4(&subheader[24], ctx->bswap);
  254|  1.67k|        deleted_row_count = sas_read4(&subheader[28], ctx->bswap);
  255|  1.67k|        page_row_count = sas_read4(&subheader[60], ctx->bswap);
  256|  1.67k|    }
  257|       |
  258|  2.33k|    if (deleted_row_count > total_row_count) {
  ------------------
  |  Branch (258:9): [True: 72, False: 2.26k]
  ------------------
  259|     72|        retval = READSTAT_ERROR_PARSE;
  260|     72|        goto cleanup;
  261|     72|    }
  262|  2.26k|    ctx->total_row_count = total_row_count;
  263|  2.26k|    ctx->deleted_row_count = deleted_row_count;
  264|       |
  265|  2.26k|    sas_text_ref_t file_label_ref = sas7bdat_parse_text_ref(&subheader[len-130], ctx);
  266|  2.26k|    if (file_label_ref.length) {
  ------------------
  |  Branch (266:9): [True: 666, False: 1.59k]
  ------------------
  267|    666|        if ((retval = sas7bdat_copy_text_ref(ctx->file_label, sizeof(ctx->file_label),
  ------------------
  |  Branch (267:13): [True: 24, False: 642]
  ------------------
  268|    666|                        file_label_ref, ctx)) != READSTAT_OK) {
  269|     24|            goto cleanup;
  270|     24|        }
  271|    666|    }
  272|       |
  273|  2.23k|    sas_text_ref_t compression_ref = sas7bdat_parse_text_ref(&subheader[len-118], ctx);
  274|  2.23k|    if (compression_ref.length) {
  ------------------
  |  Branch (274:9): [True: 997, False: 1.24k]
  ------------------
  275|    997|        char compression[9];
  276|    997|        if ((retval = sas7bdat_copy_text_ref(compression, sizeof(compression),
  ------------------
  |  Branch (276:13): [True: 26, False: 971]
  ------------------
  277|    997|                        compression_ref, ctx)) != READSTAT_OK) {
  278|     26|            goto cleanup;
  279|     26|        }
  280|    971|        ctx->rdc_compression = (memcmp(compression, SAS_COMPRESSION_SIGNATURE_RDC, 8) == 0);
  ------------------
  |  |  130|    971|#define SAS_COMPRESSION_SIGNATURE_RDC  "SASYZCR2"
  ------------------
  281|    971|    }
  282|       |
  283|  2.21k|    ctx->row_length = row_length;
  284|  2.21k|    ctx->row = readstat_realloc(ctx->row, ctx->row_length);
  285|  2.21k|    if (ctx->row == NULL) {
  ------------------
  |  Branch (285:9): [True: 8, False: 2.20k]
  ------------------
  286|      8|        retval = READSTAT_ERROR_MALLOC;
  287|      8|        goto cleanup;
  288|      8|    }
  289|       |
  290|  2.20k|    ctx->page_row_count = page_row_count;
  291|  2.20k|    uint64_t live_row_count = total_row_count - deleted_row_count;
  292|  2.20k|    uint64_t total_row_count_after_skipping = live_row_count;
  293|  2.20k|    if (live_row_count > ctx->row_offset) {
  ------------------
  |  Branch (293:9): [True: 1.98k, False: 224]
  ------------------
  294|  1.98k|        total_row_count_after_skipping -= ctx->row_offset;
  295|  1.98k|    } else {
  296|    224|        total_row_count_after_skipping = 0;
  297|    224|        ctx->row_offset = live_row_count;
  298|    224|    }
  299|  2.20k|    if (ctx->row_limit == 0 || total_row_count_after_skipping < ctx->row_limit)
  ------------------
  |  Branch (299:9): [True: 911, False: 1.29k]
  |  Branch (299:32): [True: 40, False: 1.25k]
  ------------------
  300|    951|        ctx->row_limit = total_row_count_after_skipping;
  301|       |
  302|  2.33k|cleanup:
  303|  2.33k|    return retval;
  304|  2.20k|}
readstat_sas7bdat_read.c:sas7bdat_parse_text_ref:
  137|  36.6k|static sas_text_ref_t sas7bdat_parse_text_ref(const char *data, sas7bdat_ctx_t *ctx) {
  138|  36.6k|    sas_text_ref_t  ref;
  139|       |
  140|  36.6k|    ref.index = sas_read2(&data[0], ctx->bswap);
  141|  36.6k|    ref.offset = sas_read2(&data[2], ctx->bswap);
  142|  36.6k|    ref.length = sas_read2(&data[4], ctx->bswap);
  143|       |
  144|  36.6k|    return ref;
  145|  36.6k|}
readstat_sas7bdat_read.c:sas7bdat_copy_text_ref:
  147|  4.28M|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) {
  148|  4.28M|    if (text_ref.index >= ctx->text_blob_count)
  ------------------
  |  Branch (148:9): [True: 97, False: 4.28M]
  ------------------
  149|     97|        return READSTAT_ERROR_PARSE;
  150|       |    
  151|  4.28M|    if (text_ref.length == 0) {
  ------------------
  |  Branch (151:9): [True: 4.28M, False: 3.34k]
  ------------------
  152|  4.28M|        out_buffer[0] = '\0';
  153|  4.28M|        return READSTAT_OK;
  154|  4.28M|    }
  155|       |
  156|  3.34k|    char *blob = ctx->text_blobs[text_ref.index];
  157|       |
  158|  3.34k|    if (text_ref.offset + text_ref.length > ctx->text_blob_lengths[text_ref.index])
  ------------------
  |  Branch (158:9): [True: 60, False: 3.28k]
  ------------------
  159|     60|        return READSTAT_ERROR_PARSE;
  160|       |
  161|  3.28k|    return readstat_convert(out_buffer, out_buffer_len, &blob[text_ref.offset], text_ref.length,
  162|  3.28k|            ctx->converter);
  163|  3.34k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_size_subheader:
  208|  1.11k|static readstat_error_t sas7bdat_parse_column_size_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  209|  1.11k|    uint64_t col_count;
  210|  1.11k|    readstat_error_t retval = READSTAT_OK;
  211|       |
  212|  1.11k|    if (ctx->column_count || ctx->did_submit_columns) {
  ------------------
  |  Branch (212:9): [True: 6, False: 1.10k]
  |  Branch (212:30): [True: 3, False: 1.10k]
  ------------------
  213|      9|        retval = READSTAT_ERROR_PARSE;
  214|      9|        goto cleanup;
  215|      9|    }
  216|       |
  217|  1.10k|    if (len < (ctx->u64 ? 16 : 8)) {
  ------------------
  |  Branch (217:9): [True: 3, False: 1.10k]
  |  Branch (217:16): [True: 296, False: 810]
  ------------------
  218|      3|        retval = READSTAT_ERROR_PARSE;
  219|      3|        goto cleanup;
  220|      3|    }
  221|       |
  222|  1.10k|    if (ctx->u64) {
  ------------------
  |  Branch (222:9): [True: 295, False: 808]
  ------------------
  223|    295|        col_count = sas_read8(&subheader[8], ctx->bswap);
  224|    808|    } else {
  225|    808|        col_count = sas_read4(&subheader[4], ctx->bswap);
  226|    808|    }
  227|       |
  228|  1.10k|    ctx->column_count = col_count;
  229|       |
  230|  1.10k|    retval = sas7bdat_realloc_col_info(ctx, ctx->column_count);
  231|       |
  232|  1.11k|cleanup:
  233|  1.11k|    return retval;
  234|  1.10k|}
readstat_sas7bdat_read.c:sas7bdat_realloc_col_info:
  195|  8.60k|static readstat_error_t sas7bdat_realloc_col_info(sas7bdat_ctx_t *ctx, size_t count) {
  196|  8.60k|    if (ctx->col_info_count < count) {
  ------------------
  |  Branch (196:9): [True: 3.44k, False: 5.15k]
  ------------------
  197|  3.44k|        size_t old_count = ctx->col_info_count;
  198|  3.44k|        ctx->col_info_count = count;
  199|  3.44k|        ctx->col_info = readstat_realloc(ctx->col_info, ctx->col_info_count * sizeof(col_info_t));
  200|  3.44k|        if (ctx->col_info == NULL) {
  ------------------
  |  Branch (200:13): [True: 67, False: 3.37k]
  ------------------
  201|     67|            return READSTAT_ERROR_MALLOC;
  202|     67|        }
  203|  3.37k|        memset(ctx->col_info + old_count, 0, (count - old_count) * sizeof(col_info_t));
  204|  3.37k|    }
  205|  8.53k|    return READSTAT_OK;
  206|  8.60k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_text_subheader:
  165|  2.04k|static readstat_error_t sas7bdat_parse_column_text_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  166|  2.04k|    readstat_error_t retval = READSTAT_OK;
  167|  2.04k|    size_t signature_len = ctx->subheader_signature_size;
  168|  2.04k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  169|  2.04k|    char *blob = NULL;
  170|  2.04k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (170:9): [True: 53, False: 1.98k]
  ------------------
  171|     53|        retval = READSTAT_ERROR_PARSE;
  172|     53|        goto cleanup;
  173|     53|    }
  174|  1.98k|    ctx->text_blob_count++;
  175|  1.98k|    ctx->text_blobs = readstat_realloc(ctx->text_blobs, ctx->text_blob_count * sizeof(char *));
  176|  1.98k|    ctx->text_blob_lengths = readstat_realloc(ctx->text_blob_lengths,
  177|  1.98k|            ctx->text_blob_count * sizeof(ctx->text_blob_lengths[0]));
  178|  1.98k|    if (ctx->text_blobs == NULL || ctx->text_blob_lengths == NULL) {
  ------------------
  |  Branch (178:9): [True: 0, False: 1.98k]
  |  Branch (178:36): [True: 0, False: 1.98k]
  ------------------
  179|      0|        retval = READSTAT_ERROR_MALLOC;
  180|      0|        goto cleanup;
  181|      0|    }
  182|       |
  183|  1.98k|    if ((blob = readstat_malloc(len-signature_len)) == NULL) {
  ------------------
  |  Branch (183:9): [True: 0, False: 1.98k]
  ------------------
  184|      0|        retval = READSTAT_ERROR_MALLOC;
  185|      0|        goto cleanup;
  186|      0|    }
  187|  1.98k|    memcpy(blob, subheader+signature_len, len-signature_len);
  188|  1.98k|    ctx->text_blob_lengths[ctx->text_blob_count-1] = len-signature_len;
  189|  1.98k|    ctx->text_blobs[ctx->text_blob_count-1] = blob;
  190|       |
  191|  2.04k|cleanup:
  192|  2.04k|    return retval;
  193|  1.98k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_name_subheader:
  306|  2.39k|static readstat_error_t sas7bdat_parse_column_name_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  307|  2.39k|    readstat_error_t retval = READSTAT_OK;
  308|  2.39k|    size_t signature_len = ctx->subheader_signature_size;
  309|  2.39k|    int cmax = ctx->u64 ? (len-28)/8 : (len-20)/8;
  ------------------
  |  Branch (309:16): [True: 1.48k, False: 907]
  ------------------
  310|  2.39k|    int i;
  311|  2.39k|    const char *cnp = &subheader[signature_len+8];
  312|  2.39k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  313|       |
  314|  2.39k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (314:9): [True: 44, False: 2.35k]
  ------------------
  315|     44|        retval = READSTAT_ERROR_PARSE;
  316|     44|        goto cleanup;
  317|     44|    }
  318|       |
  319|  2.35k|    ctx->col_names_count += cmax;
  320|       |
  321|  2.35k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_names_count)) != READSTAT_OK)
  ------------------
  |  Branch (321:9): [True: 13, False: 2.33k]
  ------------------
  322|     13|        goto cleanup;
  323|       |
  324|  28.3k|    for (i=ctx->col_names_count-cmax; i<ctx->col_names_count; i++) {
  ------------------
  |  Branch (324:39): [True: 26.0k, False: 2.33k]
  ------------------
  325|  26.0k|        ctx->col_info[i].name_ref = sas7bdat_parse_text_ref(cnp, ctx);
  326|  26.0k|        cnp += 8;
  327|  26.0k|    }
  328|       |
  329|  2.39k|cleanup:
  330|       |
  331|  2.39k|    return retval;
  332|  2.33k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_attributes_subheader:
  334|  2.10k|static readstat_error_t sas7bdat_parse_column_attributes_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  335|  2.10k|    readstat_error_t retval = READSTAT_OK;
  336|  2.10k|    size_t signature_len = ctx->subheader_signature_size;
  337|  2.10k|    int cmax = ctx->u64 ? (len-28)/16 : (len-20)/12;
  ------------------
  |  Branch (337:16): [True: 297, False: 1.80k]
  ------------------
  338|  2.10k|    int i;
  339|  2.10k|    const char *cap = &subheader[signature_len+8];
  340|  2.10k|    uint16_t remainder = sas_read2(&subheader[signature_len], ctx->bswap);
  341|       |
  342|  2.10k|    if (remainder != sas_subheader_remainder(len, signature_len)) {
  ------------------
  |  Branch (342:9): [True: 36, False: 2.06k]
  ------------------
  343|     36|        retval = READSTAT_ERROR_PARSE;
  344|     36|        goto cleanup;
  345|     36|    }
  346|  2.06k|    ctx->col_attrs_count += cmax;
  347|  2.06k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_attrs_count)) != READSTAT_OK)
  ------------------
  |  Branch (347:9): [True: 11, False: 2.05k]
  ------------------
  348|     11|        goto cleanup;
  349|       |
  350|  10.6k|    for (i=ctx->col_attrs_count-cmax; i<ctx->col_attrs_count; i++) {
  ------------------
  |  Branch (350:39): [True: 8.58k, False: 2.04k]
  ------------------
  351|  8.58k|        if (ctx->u64) {
  ------------------
  |  Branch (351:13): [True: 546, False: 8.04k]
  ------------------
  352|    546|            ctx->col_info[i].offset = sas_read8(&cap[0], ctx->bswap);
  353|  8.04k|        } else {
  354|  8.04k|            ctx->col_info[i].offset = sas_read4(&cap[0], ctx->bswap);
  355|  8.04k|        }
  356|       |
  357|  8.58k|        readstat_off_t off=4;
  358|  8.58k|        if (ctx->u64)
  ------------------
  |  Branch (358:13): [True: 546, False: 8.04k]
  ------------------
  359|    546|            off=8;
  360|       |
  361|  8.58k|        ctx->col_info[i].width = sas_read4(&cap[off], ctx->bswap);
  362|  8.58k|        if (ctx->col_info[i].width > ctx->max_col_width)
  ------------------
  |  Branch (362:13): [True: 621, False: 7.96k]
  ------------------
  363|    621|            ctx->max_col_width = ctx->col_info[i].width;
  364|       |
  365|  8.58k|        if (cap[off+6] == SAS_COLUMN_TYPE_NUM) {
  ------------------
  |  |   89|  8.58k|#define SAS_COLUMN_TYPE_NUM  0x01
  ------------------
  |  Branch (365:13): [True: 5.48k, False: 3.10k]
  ------------------
  366|  5.48k|            ctx->col_info[i].type = READSTAT_TYPE_DOUBLE;
  367|  5.48k|        } else if (cap[off+6] == SAS_COLUMN_TYPE_CHR) {
  ------------------
  |  |   90|  3.10k|#define SAS_COLUMN_TYPE_CHR  0x02
  ------------------
  |  Branch (367:20): [True: 3.08k, False: 16]
  ------------------
  368|  3.08k|            ctx->col_info[i].type = READSTAT_TYPE_STRING;
  369|  3.08k|        } else {
  370|     16|            retval = READSTAT_ERROR_PARSE;
  371|     16|            goto cleanup;
  372|     16|        }
  373|  8.57k|        ctx->col_info[i].index = i;
  374|  8.57k|        cap += off+8;
  375|  8.57k|    }
  376|       |
  377|  2.10k|cleanup:
  378|       |
  379|  2.10k|    return retval;
  380|  2.05k|}
readstat_sas7bdat_read.c:sas7bdat_parse_column_format_subheader:
  382|  3.08k|static readstat_error_t sas7bdat_parse_column_format_subheader(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  383|  3.08k|    readstat_error_t retval = READSTAT_OK;
  384|       |
  385|  3.08k|    if (len < (ctx->u64 ? 58 : 46)) {
  ------------------
  |  Branch (385:9): [True: 3, False: 3.08k]
  |  Branch (385:16): [True: 227, False: 2.85k]
  ------------------
  386|      3|        retval = READSTAT_ERROR_PARSE;
  387|      3|        goto cleanup;
  388|      3|    }
  389|       |
  390|  3.08k|    ctx->col_formats_count++;
  391|  3.08k|    if ((retval = sas7bdat_realloc_col_info(ctx, ctx->col_formats_count)) != READSTAT_OK)
  ------------------
  |  Branch (391:9): [True: 0, False: 3.08k]
  ------------------
  392|      0|        goto cleanup;
  393|       |
  394|  3.08k|    if (ctx->u64) {
  ------------------
  |  Branch (394:9): [True: 226, False: 2.85k]
  ------------------
  395|    226|        ctx->col_info[ctx->col_formats_count-1].format_width = sas_read2(&subheader[24], ctx->bswap);
  396|    226|        ctx->col_info[ctx->col_formats_count-1].format_digits = sas_read2(&subheader[26], ctx->bswap);
  397|  2.85k|    } else {
  398|  2.85k|        ctx->col_info[ctx->col_formats_count-1].format_width = sas_read2(&subheader[12], ctx->bswap);
  399|  2.85k|        ctx->col_info[ctx->col_formats_count-1].format_digits = sas_read2(&subheader[14], ctx->bswap);
  400|  2.85k|    }
  401|  3.08k|    ctx->col_info[ctx->col_formats_count-1].format_ref = sas7bdat_parse_text_ref(
  402|  3.08k|            ctx->u64 ? &subheader[46] : &subheader[34], ctx);
  ------------------
  |  Branch (402:13): [True: 226, False: 2.85k]
  ------------------
  403|  3.08k|    ctx->col_info[ctx->col_formats_count-1].label_ref = sas7bdat_parse_text_ref(
  404|  3.08k|            ctx->u64 ? &subheader[52] : &subheader[40], ctx);
  ------------------
  |  Branch (404:13): [True: 226, False: 2.85k]
  ------------------
  405|       |
  406|  3.08k|cleanup:
  407|  3.08k|    return retval;
  408|  3.08k|}
readstat_sas7bdat_read.c:sas7bdat_parse_amd_pages_pass1:
 1184|  2.21k|static readstat_error_t sas7bdat_parse_amd_pages_pass1(int64_t last_examined_page_pass1, sas7bdat_ctx_t *ctx) {
 1185|  2.21k|    readstat_error_t retval = READSTAT_OK;
 1186|  2.21k|    readstat_io_t *io = ctx->io;
 1187|  2.21k|    uint64_t i;
 1188|  2.21k|    uint64_t amd_page_count = 0;
 1189|       |
 1190|       |    /* ...then AMD pages at the end */
 1191|  5.38k|    for (i=ctx->page_count-1; i>last_examined_page_pass1; i--) {
  ------------------
  |  Branch (1191:31): [True: 4.32k, False: 1.06k]
  ------------------
 1192|  4.32k|        if (io->seek(ctx->header_size + i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) {
  ------------------
  |  Branch (1192:13): [True: 178, False: 4.14k]
  ------------------
 1193|    178|            retval = READSTAT_ERROR_SEEK;
 1194|    178|            if (ctx->handle.error) {
  ------------------
  |  Branch (1194:17): [True: 0, False: 178]
  ------------------
 1195|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), "ReadStat: Failed to seek to position %" PRId64 
 1196|      0|                        " (= %" PRId64 " + %" PRId64 "*%" PRId64 ")",
 1197|      0|                        ctx->header_size + i*ctx->page_size, ctx->header_size, i, ctx->page_size);
 1198|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1199|      0|            }
 1200|    178|            goto cleanup;
 1201|    178|        }
 1202|       |
 1203|  4.14k|        readstat_off_t off = 0;
 1204|  4.14k|        if (ctx->u64)
  ------------------
  |  Branch (1204:13): [True: 3.17k, False: 972]
  ------------------
 1205|  3.17k|            off = 16;
 1206|       |
 1207|  4.14k|        size_t head_len = off + 16 + 2;
 1208|  4.14k|        size_t tail_len = ctx->page_size - head_len;
 1209|       |
 1210|  4.14k|        if (io->read(ctx->page, head_len, io->io_ctx) < head_len) {
  ------------------
  |  Branch (1210:13): [True: 6, False: 4.13k]
  ------------------
 1211|      6|            retval = READSTAT_ERROR_READ;
 1212|      6|            goto cleanup;
 1213|      6|        }
 1214|       |
 1215|  4.13k|        uint16_t page_type = sas_read2(&ctx->page[off+16], ctx->bswap);
 1216|       |
 1217|  4.13k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  112|  4.13k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  109|  4.13k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1217:13): [True: 1.09k, False: 3.04k]
  ------------------
 1218|       |            /* Usually AMD pages are at the end but sometimes data pages appear after them */
 1219|  1.09k|            if (amd_page_count > 0)
  ------------------
  |  Branch (1219:17): [True: 345, False: 752]
  ------------------
 1220|    345|                break;
 1221|    752|            continue;
 1222|  1.09k|        }
 1223|  3.04k|        if ((page_type & SAS_PAGE_TYPE_COMP))
  ------------------
  |  |  116|  3.04k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1223:13): [True: 786, False: 2.25k]
  ------------------
 1224|    786|            continue;
 1225|       |
 1226|  2.25k|        if (io->read(ctx->page + head_len, tail_len, io->io_ctx) < tail_len) {
  ------------------
  |  Branch (1226:13): [True: 19, False: 2.23k]
  ------------------
 1227|     19|            retval = READSTAT_ERROR_READ;
 1228|     19|            goto cleanup;
 1229|     19|        }
 1230|       |
 1231|  2.23k|        if ((retval = sas7bdat_parse_page_pass1(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1231:13): [True: 594, False: 1.64k]
  ------------------
 1232|    594|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1232:17): [True: 0, False: 594]
  |  Branch (1232:38): [True: 0, False: 0]
  ------------------
 1233|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1234|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1235|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1236|      0|                        i, pos - ctx->page_size, pos-1);
 1237|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1238|      0|            }
 1239|    594|            goto cleanup;
 1240|    594|        }
 1241|       |
 1242|  1.64k|        amd_page_count++;
 1243|  1.64k|    }
 1244|       |
 1245|  2.21k|cleanup:
 1246|       |
 1247|  2.21k|    return retval;
 1248|  2.21k|}
readstat_sas7bdat_read.c:sas7bdat_parse_all_pages_pass2:
 1250|  1.41k|static readstat_error_t sas7bdat_parse_all_pages_pass2(sas7bdat_ctx_t *ctx) {
 1251|  1.41k|    readstat_error_t retval = READSTAT_OK;
 1252|  1.41k|    readstat_io_t *io = ctx->io;
 1253|  1.41k|    int64_t i;
 1254|       |
 1255|  4.89k|    for (i=0; i<ctx->page_count; i++) {
  ------------------
  |  Branch (1255:15): [True: 4.82k, False: 69]
  ------------------
 1256|  4.82k|        if ((retval = sas7bdat_update_progress(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1256:13): [True: 0, False: 4.82k]
  ------------------
 1257|      0|            goto cleanup;
 1258|      0|        }
 1259|  4.82k|        if (io->read(ctx->page, ctx->page_size, io->io_ctx) < ctx->page_size) {
  ------------------
  |  Branch (1259:13): [True: 143, False: 4.68k]
  ------------------
 1260|    143|            retval = READSTAT_ERROR_READ;
 1261|    143|            goto cleanup;
 1262|    143|        }
 1263|       |
 1264|  4.68k|        if ((retval = sas7bdat_parse_page_pass2(ctx->page, ctx->page_size, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1264:13): [True: 1.00k, False: 3.67k]
  ------------------
 1265|  1.00k|            if (ctx->handle.error && retval != READSTAT_ERROR_USER_ABORT) {
  ------------------
  |  Branch (1265:17): [True: 0, False: 1.00k]
  |  Branch (1265:38): [True: 0, False: 0]
  ------------------
 1266|      0|                int64_t pos = io->seek(0, READSTAT_SEEK_CUR, io->io_ctx);
 1267|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
 1268|      0|                        "ReadStat: Error parsing page %" PRId64 ", bytes %" PRId64 "-%" PRId64, 
 1269|      0|                        i, pos - ctx->page_size, pos-1);
 1270|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
 1271|      0|            }
 1272|  1.00k|            goto cleanup;
 1273|  1.00k|        }
 1274|  3.67k|        if (ctx->parsed_row_count == ctx->row_limit)
  ------------------
  |  Branch (1274:13): [True: 192, False: 3.48k]
  ------------------
 1275|    192|            break;
 1276|  3.67k|    }
 1277|  1.41k|cleanup:
 1278|       |
 1279|  1.41k|    return retval;
 1280|  1.41k|}
readstat_sas7bdat_read.c:sas7bdat_parse_page_pass2:
 1015|  4.68k|static readstat_error_t sas7bdat_parse_page_pass2(const char *page, size_t page_size, sas7bdat_ctx_t *ctx) {
 1016|  4.68k|    uint16_t page_type;
 1017|       |
 1018|  4.68k|    readstat_error_t retval = READSTAT_OK;
 1019|       |
 1020|  4.68k|    page_type = sas_read2(&page[ctx->page_header_size-8], ctx->bswap);
 1021|       |
 1022|  4.68k|    const char *data = NULL;
 1023|       |
 1024|  4.68k|    if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  112|  4.68k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                  if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_DATA) {
  ------------------
  |  |  109|  4.68k|#define SAS_PAGE_TYPE_DATA   0x0100
  ------------------
  |  Branch (1024:9): [True: 1.14k, False: 3.54k]
  ------------------
 1025|  1.14k|        ctx->page_row_count = sas_read2(&page[ctx->page_header_size-6], ctx->bswap);
 1026|  1.14k|        data = &page[ctx->page_header_size];
 1027|  3.54k|    } else if (!(page_type & SAS_PAGE_TYPE_COMP)) {
  ------------------
  |  |  116|  3.54k|#define SAS_PAGE_TYPE_COMP          0x9000
  ------------------
  |  Branch (1027:16): [True: 3.08k, False: 459]
  ------------------
 1028|  3.08k|        uint16_t subheader_count = sas_read2(&page[ctx->page_header_size-4], ctx->bswap);
 1029|       |
 1030|  3.08k|        int i;
 1031|  3.08k|        const char *shp = &page[ctx->page_header_size];
 1032|  3.08k|        int lshp = ctx->subheader_pointer_size;
 1033|       |
 1034|  3.08k|        if (ctx->page_header_size + subheader_count*lshp > page_size) {
  ------------------
  |  Branch (1034:13): [True: 14, False: 3.07k]
  ------------------
 1035|     14|            retval = READSTAT_ERROR_PARSE;
 1036|     14|            goto cleanup;
 1037|     14|        }
 1038|       |
 1039|  33.8k|        for (i=0; i<subheader_count; i++) {
  ------------------
  |  Branch (1039:19): [True: 31.5k, False: 2.29k]
  ------------------
 1040|  31.5k|            subheader_pointer_t shp_info = { 0 };
 1041|  31.5k|            if ((retval = sas7bdat_parse_subheader_pointer(shp, page + page_size - shp, &shp_info, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1041:17): [True: 0, False: 31.5k]
  ------------------
 1042|      0|                goto cleanup;
 1043|      0|            }
 1044|  31.5k|            if (shp_info.len > 0 && shp_info.compression != SAS_COMPRESSION_TRUNC) {
  ------------------
  |  |  125|  20.0k|#define SAS_COMPRESSION_TRUNC        0x01
  ------------------
  |  Branch (1044:17): [True: 20.0k, False: 11.5k]
  |  Branch (1044:37): [True: 17.0k, False: 2.95k]
  ------------------
 1045|  17.0k|                if ((retval = sas7bdat_validate_subheader_pointer(&shp_info, page_size, subheader_count, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1045:21): [True: 153, False: 16.9k]
  ------------------
 1046|    153|                    goto cleanup;
 1047|    153|                }
 1048|  16.9k|                if (shp_info.compression == SAS_COMPRESSION_NONE) {
  ------------------
  |  |  124|  16.9k|#define SAS_COMPRESSION_NONE         0x00
  ------------------
  |  Branch (1048:21): [True: 15.4k, False: 1.50k]
  ------------------
 1049|  15.4k|                    sas_subheader_type_t subheader_type = sas7bdat_parse_subheader_type(page + shp_info.offset, ctx);
 1050|  15.4k|                    if (shp_info.is_compressed_data && subheader_type == SAS_SUBHEADER_TYPE_DATA) {
  ------------------
  |  Branch (1050:25): [True: 11.1k, False: 4.25k]
  |  Branch (1050:56): [True: 441, False: 10.7k]
  ------------------
 1051|    441|                        if (shp_info.len != ctx->row_length) {
  ------------------
  |  Branch (1051:29): [True: 87, False: 354]
  ------------------
 1052|     87|                            retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
 1053|     87|                            goto cleanup;
 1054|     87|                        }
 1055|    354|                        if ((retval = sas7bdat_submit_columns_if_needed(ctx, 1)) != READSTAT_OK) {
  ------------------
  |  Branch (1055:29): [True: 6, False: 348]
  ------------------
 1056|      6|                            goto cleanup;
 1057|      6|                        }
 1058|    348|                        if ((retval = sas7bdat_parse_single_row(page + shp_info.offset, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1058:29): [True: 4, False: 344]
  ------------------
 1059|      4|                            goto cleanup;
 1060|      4|                        }
 1061|  14.9k|                    } else {
 1062|  14.9k|                        if (subheader_type != SAS_SUBHEADER_TYPE_COLUMN_TEXT) {
  ------------------
  |  Branch (1062:29): [True: 13.7k, False: 1.19k]
  ------------------
 1063|  13.7k|                            if ((retval = sas7bdat_parse_subheader(subheader_type, page + shp_info.offset, shp_info.len, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1063:33): [True: 342, False: 13.4k]
  ------------------
 1064|    342|                                goto cleanup;
 1065|    342|                            }
 1066|  13.7k|                        }
 1067|  14.9k|                    }
 1068|  15.4k|                } else if (shp_info.compression == SAS_COMPRESSION_ROW) {
  ------------------
  |  |  126|  1.50k|#define SAS_COMPRESSION_ROW          0x04
  ------------------
  |  Branch (1068:28): [True: 1.36k, False: 141]
  ------------------
 1069|  1.36k|                    if ((retval = sas7bdat_submit_columns_if_needed(ctx, 1)) != READSTAT_OK) {
  ------------------
  |  Branch (1069:25): [True: 21, False: 1.34k]
  ------------------
 1070|     21|                        goto cleanup;
 1071|     21|                    }
 1072|  1.34k|                    if ((retval = sas7bdat_parse_subheader_compressed(page + shp_info.offset, shp_info.len, ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1072:25): [True: 161, False: 1.18k]
  ------------------
 1073|    161|                        goto cleanup;
 1074|    161|                    }
 1075|  1.34k|                } else if (shp_info.compression == SAS_COMPRESSION_DELETED_ROW) {
  ------------------
  |  |  127|    141|#define SAS_COMPRESSION_DELETED_ROW  0x05
  ------------------
  |  Branch (1075:28): [True: 137, False: 4]
  ------------------
 1076|    137|                    if ((retval = sas7bdat_register_deleted_row(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (1076:25): [True: 1, False: 136]
  ------------------
 1077|      1|                        goto cleanup;
 1078|      1|                    }
 1079|    137|                } else {
 1080|      4|                    retval = READSTAT_ERROR_UNSUPPORTED_COMPRESSION;
 1081|      4|                    goto cleanup;
 1082|      4|                }
 1083|  16.9k|            }
 1084|       |
 1085|  30.7k|            shp += lshp;
 1086|  30.7k|        }
 1087|       |
 1088|  2.29k|        if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_MIX) {
  ------------------
  |  |  112|  2.29k|#define SAS_PAGE_TYPE_MASK   0x0F00
  ------------------
                      if ((page_type & SAS_PAGE_TYPE_MASK) == SAS_PAGE_TYPE_MIX) {
  ------------------
  |  |  110|  2.29k|#define SAS_PAGE_TYPE_MIX    0x0200
  ------------------
  |  Branch (1088:13): [True: 1.03k, False: 1.25k]
  ------------------
 1089|       |            /* HACK - this is supposed to obey 8-byte boundaries but
 1090|       |             * some files created by Stat/Transfer don't. So verify that the
 1091|       |             * padding is { 0, 0, 0, 0 } or { ' ', ' ', ' ', ' ' } (or that
 1092|       |             * the file is not from Stat/Transfer) before skipping it */
 1093|  1.03k|            if ((shp-page)%8 == 4 && shp + 4 <= page + page_size &&
  ------------------
  |  Branch (1093:17): [True: 365, False: 671]
  |  Branch (1093:38): [True: 362, False: 3]
  ------------------
 1094|    362|                    (*(uint32_t *)shp == 0x00000000 ||
  ------------------
  |  Branch (1094:22): [True: 114, False: 248]
  ------------------
 1095|    248|                     *(uint32_t *)shp == 0x20202020 ||
  ------------------
  |  Branch (1095:22): [True: 37, False: 211]
  ------------------
 1096|    339|                     ctx->vendor != READSTAT_VENDOR_STAT_TRANSFER)) {
  ------------------
  |  Branch (1096:22): [True: 188, False: 23]
  ------------------
 1097|    339|                data = shp + 4;
 1098|    697|            } else {
 1099|    697|                data = shp;
 1100|    697|            }
 1101|  1.03k|        }
 1102|  2.29k|    }
 1103|  3.89k|    if (data) {
  ------------------
  |  Branch (1103:9): [True: 2.17k, False: 1.71k]
  ------------------
 1104|  2.17k|        if ((retval = sas7bdat_submit_columns_if_needed(ctx, 0)) != READSTAT_OK) {
  ------------------
  |  Branch (1104:13): [True: 101, False: 2.07k]
  ------------------
 1105|    101|            goto cleanup;
 1106|    101|        }
 1107|  2.07k|        if (ctx->handle.value) {
  ------------------
  |  Branch (1107:13): [True: 2.07k, False: 0]
  ------------------
 1108|  2.07k|            const unsigned char *deleted_row_bitmap = NULL;
 1109|  2.07k|            if (page_type & SAS_PAGE_TYPE_DELETED_ROWS) {
  ------------------
  |  |  114|  2.07k|#define SAS_PAGE_TYPE_DELETED_ROWS  0x0080
  ------------------
  |  Branch (1109:17): [True: 521, False: 1.55k]
  ------------------
 1110|    521|                if ((retval = sas7bdat_parse_deleted_row_bitmap(page, data, page_size,
  ------------------
  |  Branch (1110:21): [True: 19, False: 502]
  ------------------
 1111|    521|                        &deleted_row_bitmap, ctx)) != READSTAT_OK) {
 1112|     19|                    goto cleanup;
 1113|     19|                }
 1114|    521|            }
 1115|  2.05k|            retval = sas7bdat_parse_rows(data, page + page_size - data, deleted_row_bitmap, ctx);
 1116|  2.05k|        }
 1117|  2.07k|    } 
 1118|  4.68k|cleanup:
 1119|       |
 1120|  4.68k|    return retval;
 1121|  3.89k|}
readstat_sas7bdat_read.c:sas7bdat_parse_single_row:
  482|   274k|static readstat_error_t sas7bdat_parse_single_row(const char *data, sas7bdat_ctx_t *ctx) {
  483|   274k|    if (ctx->parsed_row_count == ctx->row_limit)
  ------------------
  |  Branch (483:9): [True: 256, False: 273k]
  ------------------
  484|    256|        return READSTAT_OK;
  485|   273k|    if (ctx->row_offset) {
  ------------------
  |  Branch (485:9): [True: 0, False: 273k]
  ------------------
  486|      0|        ctx->row_offset--;
  487|      0|        return READSTAT_OK;
  488|      0|    }
  489|       |
  490|   273k|    readstat_error_t retval = READSTAT_OK;
  491|   273k|    int j;
  492|   273k|    if (ctx->handle.value) {
  ------------------
  |  Branch (492:9): [True: 273k, False: 0]
  ------------------
  493|   273k|        ctx->scratch_buffer_len = 4*ctx->max_col_width+1;
  494|   273k|        ctx->scratch_buffer = readstat_realloc(ctx->scratch_buffer, ctx->scratch_buffer_len);
  495|   273k|        if (ctx->scratch_buffer == NULL) {
  ------------------
  |  Branch (495:13): [True: 2, False: 273k]
  ------------------
  496|      2|            retval = READSTAT_ERROR_MALLOC;
  497|      2|            goto cleanup;
  498|      2|        }
  499|       |
  500|  6.80M|        for (j=0; j<ctx->column_count; j++) {
  ------------------
  |  Branch (500:19): [True: 6.53M, False: 273k]
  ------------------
  501|  6.53M|            col_info_t *col_info = &ctx->col_info[j];
  502|  6.53M|            readstat_variable_t *variable = ctx->variables[j];
  503|  6.53M|            if (variable->skip)
  ------------------
  |  Branch (503:17): [True: 0, False: 6.53M]
  ------------------
  504|      0|                continue;
  505|       |
  506|  6.53M|            if (col_info->offset > ctx->row_length || col_info->offset + col_info->width > ctx->row_length) {
  ------------------
  |  Branch (506:17): [True: 49, False: 6.53M]
  |  Branch (506:55): [True: 14, False: 6.53M]
  ------------------
  507|     63|                retval = READSTAT_ERROR_PARSE;
  508|     63|                goto cleanup;
  509|     63|            }
  510|  6.53M|            retval = sas7bdat_handle_data_value(variable, col_info, &data[col_info->offset], ctx);
  511|  6.53M|            if (retval != READSTAT_OK) {
  ------------------
  |  Branch (511:17): [True: 20, False: 6.53M]
  ------------------
  512|     20|                goto cleanup;
  513|     20|            }
  514|  6.53M|        }
  515|   273k|    }
  516|   273k|    ctx->parsed_row_count++;
  517|       |
  518|   273k|cleanup:
  519|   273k|    return retval;
  520|   273k|}
readstat_sas7bdat_read.c:sas7bdat_handle_data_value:
  419|  6.53M|        col_info_t *col_info, const char *col_data, sas7bdat_ctx_t *ctx) {
  420|  6.53M|    readstat_error_t retval = READSTAT_OK;
  421|  6.53M|    int cb_retval = 0;
  422|  6.53M|    readstat_value_t value;
  423|  6.53M|    memset(&value, 0, sizeof(readstat_value_t));
  424|       |
  425|  6.53M|    value.type = col_info->type;
  426|       |
  427|  6.53M|    if (col_info->type == READSTAT_TYPE_STRING) {
  ------------------
  |  Branch (427:9): [True: 6.37M, False: 154k]
  ------------------
  428|  6.37M|        retval = readstat_convert(ctx->scratch_buffer, ctx->scratch_buffer_len,
  429|  6.37M|                col_data, col_info->width, ctx->converter);
  430|  6.37M|        if (retval != READSTAT_OK) {
  ------------------
  |  Branch (430:13): [True: 19, False: 6.37M]
  ------------------
  431|     19|            if (ctx->handle.error) {
  ------------------
  |  Branch (431:17): [True: 0, False: 19]
  ------------------
  432|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf),
  433|      0|                        "ReadStat: Error converting string (row=%u, col=%u) to specified encoding: %.*s",
  434|      0|                        ctx->parsed_row_count+1, col_info->index+1, col_info->width, col_data);
  435|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  436|      0|            }
  437|     19|            goto cleanup;
  438|     19|        }
  439|       |
  440|  6.37M|        value.v.string_value = ctx->scratch_buffer;
  441|  6.37M|    } else if (col_info->type == READSTAT_TYPE_DOUBLE) {
  ------------------
  |  Branch (441:16): [True: 154k, False: 0]
  ------------------
  442|   154k|        uint64_t  val = 0;
  443|   154k|        double dval = NAN;
  444|   154k|        if (col_info->width < 1 || col_info->width > 8) {
  ------------------
  |  Branch (444:13): [True: 1, False: 154k]
  |  Branch (444:36): [True: 0, False: 154k]
  ------------------
  445|       |            /* A DOUBLE is 3-8 bytes on disk, but col_info can be mutated by a
  446|       |             * COLUMN_ATTRS subheader after the columns were validated. Guard
  447|       |             * here so a corrupt width can't produce an undefined shift below. */
  448|      1|            retval = READSTAT_ERROR_PARSE;
  449|      1|            goto cleanup;
  450|      1|        }
  451|   154k|        if (ctx->little_endian) {
  ------------------
  |  Branch (451:13): [True: 154k, False: 0]
  ------------------
  452|   154k|            int k;
  453|  1.09M|            for (k=0; k<col_info->width; k++) {
  ------------------
  |  Branch (453:23): [True: 944k, False: 154k]
  ------------------
  454|   944k|                val = (val << 8) | (unsigned char)col_data[col_info->width-1-k];
  455|   944k|            }
  456|   154k|        } else {
  457|      0|            int k;
  458|      0|            for (k=0; k<col_info->width; k++) {
  ------------------
  |  Branch (458:23): [True: 0, False: 0]
  ------------------
  459|      0|                val = (val << 8) | (unsigned char)col_data[k];
  460|      0|            }
  461|      0|        }
  462|   154k|        val <<= (8-col_info->width)*8;
  463|       |
  464|   154k|        memcpy(&dval, &val, 8);
  465|       |
  466|   154k|        if (isnan(dval)) {
  ------------------
  |  Branch (466:13): [True: 7.65k, False: 146k]
  ------------------
  467|  7.65k|            value.v.double_value = NAN;
  468|  7.65k|            sas_assign_tag(&value, ~((val >> 40) & 0xFF));
  469|   146k|        } else {
  470|   146k|            value.v.double_value = dval;
  471|   146k|        }
  472|   154k|    }
  473|  6.53M|    cb_retval = ctx->handle.value(ctx->parsed_row_count, variable, value, ctx->user_ctx);
  474|       |
  475|  6.53M|    if (cb_retval != READSTAT_HANDLER_OK)
  ------------------
  |  Branch (475:9): [True: 0, False: 6.53M]
  ------------------
  476|      0|        retval = READSTAT_ERROR_USER_ABORT;
  477|       |
  478|  6.53M|cleanup:
  479|  6.53M|    return retval;
  480|  6.53M|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_compressed:
  668|  1.34k|static readstat_error_t sas7bdat_parse_subheader_compressed(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  669|  1.34k|    if (ctx->rdc_compression)
  ------------------
  |  Branch (669:9): [True: 304, False: 1.04k]
  ------------------
  670|    304|        return sas7bdat_parse_subheader_rdc(subheader, len, ctx);
  671|       |
  672|  1.04k|    return sas7bdat_parse_subheader_rle(subheader, len, ctx);
  673|  1.34k|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_rdc:
  554|    304|static readstat_error_t sas7bdat_parse_subheader_rdc(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  555|    304|    readstat_error_t retval = READSTAT_OK;
  556|    304|    const unsigned char *input = (const unsigned char *)subheader;
  557|    304|    char *buffer = malloc(ctx->row_length);
  558|    304|    if (buffer == NULL) return READSTAT_ERROR_MALLOC;
  ------------------
  |  Branch (558:9): [True: 0, False: 304]
  ------------------
  559|    304|    char *output = buffer;
  560|  1.43k|    while (input + 2 <= (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (560:12): [True: 1.15k, False: 276]
  ------------------
  561|  1.15k|        int i;
  562|  1.15k|        unsigned short prefix = (input[0] << 8) + input[1];
  563|  1.15k|        input += 2;
  564|  16.4k|        for (i=0; i<16; i++) {
  ------------------
  |  Branch (564:19): [True: 15.6k, False: 863]
  ------------------
  565|  15.6k|            if ((prefix & (1 << (15 - i))) == 0) {
  ------------------
  |  Branch (565:17): [True: 11.8k, False: 3.81k]
  ------------------
  566|  11.8k|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (566:21): [True: 263, False: 11.5k]
  ------------------
  567|    263|                    break;
  568|    263|                }
  569|  11.5k|                if (output + 1 > buffer + ctx->row_length) {
  ------------------
  |  Branch (569:21): [True: 3, False: 11.5k]
  ------------------
  570|      3|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  571|      3|                    goto cleanup;
  572|      3|                }
  573|  11.5k|                *output++ = *input++;
  574|  11.5k|                continue;
  575|  11.5k|            }
  576|       |
  577|  3.81k|            if (input + 2 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (577:17): [True: 2, False: 3.81k]
  ------------------
  578|      2|                retval = READSTAT_ERROR_PARSE;
  579|      2|                goto cleanup;
  580|      2|            }
  581|       |
  582|  3.81k|            unsigned char marker_byte = *input++;
  583|  3.81k|            unsigned char next_byte = *input++;
  584|  3.81k|            size_t insert_len = 0, copy_len = 0;
  585|  3.81k|            unsigned char insert_byte = 0x00;
  586|  3.81k|            size_t back_offset = 0;
  587|       |
  588|  3.81k|            if (marker_byte <= 0x0F) {
  ------------------
  |  Branch (588:17): [True: 1.96k, False: 1.84k]
  ------------------
  589|  1.96k|                insert_len = 3 + marker_byte;
  590|  1.96k|                insert_byte = next_byte;
  591|  1.96k|            } else if ((marker_byte >> 4) == 1) {
  ------------------
  |  Branch (591:24): [True: 505, False: 1.34k]
  ------------------
  592|    505|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (592:21): [True: 1, False: 504]
  ------------------
  593|      1|                    retval = READSTAT_ERROR_PARSE;
  594|      1|                    goto cleanup;
  595|      1|                }
  596|    504|                insert_len = 19 + (marker_byte & 0x0F) + next_byte * 16;
  597|    504|                insert_byte = *input++;
  598|  1.34k|            } else if ((marker_byte >> 4) == 2) {
  ------------------
  |  Branch (598:24): [True: 191, False: 1.15k]
  ------------------
  599|    191|                if (input + 1 > (const unsigned char *)subheader + len) {
  ------------------
  |  Branch (599:21): [True: 1, False: 190]
  ------------------
  600|      1|                    retval = READSTAT_ERROR_PARSE;
  601|      1|                    goto cleanup;
  602|      1|                }
  603|    190|                copy_len = 16 + (*input++);
  604|    190|                back_offset = 3 + (marker_byte & 0x0F) + next_byte * 16;
  605|  1.15k|            } else {
  606|  1.15k|                copy_len = (marker_byte >> 4);
  607|  1.15k|                back_offset = 3 + (marker_byte & 0x0F) + next_byte * 16;
  608|  1.15k|            }
  609|       |
  610|  3.81k|            if (insert_len) {
  ------------------
  |  Branch (610:17): [True: 2.47k, False: 1.34k]
  ------------------
  611|  2.47k|                if (output + insert_len > buffer + ctx->row_length) {
  ------------------
  |  Branch (611:21): [True: 3, False: 2.46k]
  ------------------
  612|      3|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  613|      3|                    goto cleanup;
  614|      3|                }
  615|  2.46k|                memset(output, insert_byte, insert_len);
  616|  2.46k|                output += insert_len;
  617|  2.46k|            } else if (copy_len) {
  ------------------
  |  Branch (617:24): [True: 1.34k, False: 0]
  ------------------
  618|  1.34k|                if (output - buffer < back_offset || copy_len > back_offset) {
  ------------------
  |  Branch (618:21): [True: 9, False: 1.33k]
  |  Branch (618:54): [True: 8, False: 1.32k]
  ------------------
  619|     17|                    retval = READSTAT_ERROR_PARSE;
  620|     17|                    goto cleanup;
  621|     17|                }
  622|  1.32k|                if (output + copy_len > buffer + ctx->row_length) {
  ------------------
  |  Branch (622:21): [True: 1, False: 1.32k]
  ------------------
  623|      1|                    retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  624|      1|                    goto cleanup;
  625|      1|                }
  626|  1.32k|                memcpy(output, output - back_offset, copy_len);
  627|  1.32k|                output += copy_len;
  628|  1.32k|            }
  629|  3.81k|        }
  630|  1.15k|    }
  631|       |
  632|    276|    if (output - buffer != ctx->row_length) {
  ------------------
  |  Branch (632:9): [True: 12, False: 264]
  ------------------
  633|     12|        retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  634|     12|        goto cleanup;
  635|     12|    }
  636|    264|    retval = sas7bdat_parse_single_row(buffer, ctx);
  637|    304|cleanup:
  638|    304|    free(buffer);
  639|       |
  640|    304|    return retval;
  641|    264|}
readstat_sas7bdat_read.c:sas7bdat_parse_subheader_rle:
  643|  1.04k|static readstat_error_t sas7bdat_parse_subheader_rle(const char *subheader, size_t len, sas7bdat_ctx_t *ctx) {
  644|  1.04k|    if (ctx->row_limit == ctx->parsed_row_count)
  ------------------
  |  Branch (644:9): [True: 457, False: 584]
  ------------------
  645|    457|        return READSTAT_OK;
  646|       |
  647|    584|    readstat_error_t retval = READSTAT_OK;
  648|    584|    ssize_t bytes_decompressed = 0;
  649|       |
  650|    584|    bytes_decompressed = sas_rle_decompress(ctx->row, ctx->row_length, subheader, len);
  651|       |
  652|    584|    if (bytes_decompressed != ctx->row_length) {
  ------------------
  |  Branch (652:9): [True: 91, False: 493]
  ------------------
  653|     91|        retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  654|     91|        if (ctx->handle.error) {
  ------------------
  |  Branch (654:13): [True: 0, False: 91]
  ------------------
  655|      0|            snprintf(ctx->error_buf, sizeof(ctx->error_buf), 
  656|      0|                    "ReadStat: Row #%d decompressed to %ld bytes (expected %d bytes)",
  657|      0|                    ctx->parsed_row_count, (long)(bytes_decompressed), ctx->row_length);
  658|      0|            ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  659|      0|        }
  660|     91|        goto cleanup;
  661|     91|    }
  662|    493|    retval = sas7bdat_parse_single_row(ctx->row, ctx);
  663|       |
  664|    584|cleanup:
  665|    584|    return retval;
  666|    493|}
readstat_sas7bdat_read.c:sas7bdat_register_deleted_row:
  410|  32.5k|static readstat_error_t sas7bdat_register_deleted_row(sas7bdat_ctx_t *ctx) {
  411|  32.5k|    if (ctx->parsed_deleted_row_count >= ctx->deleted_row_count) {
  ------------------
  |  Branch (411:9): [True: 2, False: 32.5k]
  ------------------
  412|      2|        return READSTAT_ERROR_PARSE;
  413|      2|    }
  414|  32.5k|    ctx->parsed_deleted_row_count++;
  415|  32.5k|    return READSTAT_OK;
  416|  32.5k|}
readstat_sas7bdat_read.c:sas7bdat_parse_deleted_row_bitmap:
  997|    521|        size_t page_size, const unsigned char **deleted_row_bitmap, sas7bdat_ctx_t *ctx) {
  998|    521|    uint64_t page_unused_bytes;
  999|    521|    if (ctx->u64) {
  ------------------
  |  Branch (999:9): [True: 437, False: 84]
  ------------------
 1000|    437|        page_unused_bytes = sas_read8(&page[24], ctx->bswap);
 1001|    437|    } else {
 1002|     84|        page_unused_bytes = sas_read4(&page[12], ctx->bswap);
 1003|     84|    }
 1004|    521|    uint32_t row_count = ctx->page_row_count < ctx->total_row_count ? ctx->page_row_count : ctx->total_row_count;
  ------------------
  |  Branch (1004:26): [True: 500, False: 21]
  ------------------
 1005|    521|    uint64_t deleted_row_bitmap_offset = (uint64_t)row_count * ctx->row_length + page_unused_bytes;
 1006|    521|    uint32_t required_bytes = row_count / CHAR_BIT + (row_count % CHAR_BIT == 0 ? 0 : 1);
  ------------------
  |  Branch (1006:55): [True: 490, False: 31]
  ------------------
 1007|       |
 1008|    521|    if ((data - page) + deleted_row_bitmap_offset + required_bytes > page_size) {
  ------------------
  |  Branch (1008:9): [True: 19, False: 502]
  ------------------
 1009|     19|        return READSTAT_ERROR_PARSE;
 1010|     19|    }
 1011|    502|    *deleted_row_bitmap = (const unsigned char *)data + deleted_row_bitmap_offset;
 1012|    502|    return READSTAT_OK;
 1013|    521|}
readstat_sas7bdat_read.c:sas7bdat_parse_rows:
  530|  2.05k|        const unsigned char *deleted_bitmap, sas7bdat_ctx_t *ctx) {
  531|  2.05k|    readstat_error_t retval = READSTAT_OK;
  532|  2.05k|    int i;
  533|  2.05k|    size_t row_offset=0;
  534|   307k|    for (i=0; i<ctx->page_row_count && ctx->parsed_row_count < ctx->row_limit; i++) {
  ------------------
  |  Branch (534:15): [True: 305k, False: 1.94k]
  |  Branch (534:40): [True: 305k, False: 16]
  ------------------
  535|   305k|        if (row_offset + ctx->row_length > len) {
  ------------------
  |  Branch (535:13): [True: 44, False: 305k]
  ------------------
  536|     44|            retval = READSTAT_ERROR_ROW_WIDTH_MISMATCH;
  537|     44|            goto cleanup;
  538|     44|        }
  539|   305k|        if (deleted_bitmap != NULL && sas7bdat_read_bitmap(deleted_bitmap, i)) {
  ------------------
  |  Branch (539:13): [True: 105k, False: 199k]
  |  Branch (539:39): [True: 32.4k, False: 73.2k]
  ------------------
  540|  32.4k|            if ((retval = sas7bdat_register_deleted_row(ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (540:17): [True: 1, False: 32.4k]
  ------------------
  541|      1|                goto cleanup;
  542|      1|            }
  543|   273k|        } else if ((retval = sas7bdat_parse_single_row(&data[row_offset], ctx)) != READSTAT_OK) {
  ------------------
  |  Branch (543:20): [True: 51, False: 273k]
  ------------------
  544|     51|            goto cleanup;
  545|     51|        }
  546|       |
  547|   305k|        row_offset += ctx->row_length;
  548|   305k|    }
  549|       |
  550|  2.05k|cleanup:
  551|  2.05k|    return retval;
  552|  2.05k|}
readstat_sas7bdat_read.c:sas7bdat_read_bitmap:
  522|   105k|static unsigned char sas7bdat_read_bitmap(const unsigned char *bitmap, int index) {
  523|   105k|    unsigned char current_byte = bitmap[index / CHAR_BIT];
  524|   105k|    unsigned char mask = 1 << (CHAR_BIT - 1 - index % CHAR_BIT);
  525|       |
  526|   105k|    return current_byte & mask;
  527|   105k|}
readstat_sas7bdat_read.c:sas7bdat_submit_columns_if_needed:
  845|  4.15k|static readstat_error_t sas7bdat_submit_columns_if_needed(sas7bdat_ctx_t *ctx, int compressed) {
  846|  4.15k|    readstat_error_t retval = READSTAT_OK;
  847|  4.15k|    if (!ctx->did_submit_columns) {
  ------------------
  |  Branch (847:9): [True: 993, False: 3.16k]
  ------------------
  848|    993|        if ((retval = sas7bdat_submit_columns(ctx, compressed)) != READSTAT_OK) {
  ------------------
  |  Branch (848:13): [True: 194, False: 799]
  ------------------
  849|    194|            goto cleanup;
  850|    194|        }
  851|    799|        ctx->did_submit_columns = 1;
  852|    799|    }
  853|  4.15k|cleanup:
  854|  4.15k|    return retval;
  855|  4.15k|}
readstat_sas7bdat_read.c:sas7bdat_submit_columns:
  784|    993|static readstat_error_t sas7bdat_submit_columns(sas7bdat_ctx_t *ctx, int compressed) {
  785|    993|    readstat_error_t retval = READSTAT_OK;
  786|    993|    if (ctx->handle.metadata) {
  ------------------
  |  Branch (786:9): [True: 993, False: 0]
  ------------------
  787|    993|        readstat_metadata_t metadata = {
  788|    993|            .row_count = ctx->row_limit,
  789|    993|            .var_count = ctx->column_count,
  790|    993|            .table_name = ctx->table_name,
  791|    993|            .file_label = ctx->file_label,
  792|    993|            .file_encoding = ctx->input_encoding, /* orig encoding? */
  793|    993|            .creation_time = ctx->ctime,
  794|    993|            .modified_time = ctx->mtime,
  795|    993|            .file_format_version = ctx->version,
  796|    993|            .compression = READSTAT_COMPRESS_NONE,
  797|    993|            .endianness = ctx->little_endian ? READSTAT_ENDIAN_LITTLE : READSTAT_ENDIAN_BIG,
  ------------------
  |  Branch (797:27): [True: 210, False: 783]
  ------------------
  798|    993|            .is64bit = ctx->u64
  799|    993|        };
  800|    993|        if (compressed) {
  ------------------
  |  Branch (800:13): [True: 339, False: 654]
  ------------------
  801|    339|            if (ctx->rdc_compression) {
  ------------------
  |  Branch (801:17): [True: 38, False: 301]
  ------------------
  802|     38|                metadata.compression = READSTAT_COMPRESS_BINARY;
  803|    301|            } else {
  804|    301|                metadata.compression = READSTAT_COMPRESS_ROWS;
  805|    301|            }
  806|    339|        }
  807|    993|        if (ctx->handle.metadata(&metadata, ctx->user_ctx) != READSTAT_HANDLER_OK) {
  ------------------
  |  Branch (807:13): [True: 0, False: 993]
  ------------------
  808|      0|            retval = READSTAT_ERROR_USER_ABORT;
  809|      0|            goto cleanup;
  810|      0|        }
  811|    993|    }
  812|    993|    if (ctx->column_count == 0)
  ------------------
  |  Branch (812:9): [True: 640, False: 353]
  ------------------
  813|    640|        goto cleanup;
  814|       |
  815|    353|    if ((ctx->variables = readstat_calloc(ctx->column_count, sizeof(readstat_variable_t *))) == NULL) {
  ------------------
  |  Branch (815:9): [True: 0, False: 353]
  ------------------
  816|      0|        retval = READSTAT_ERROR_MALLOC;
  817|      0|        goto cleanup;
  818|      0|    }
  819|    353|    int i;
  820|    353|    int index_after_skipping = 0;
  821|  1.42M|    for (i=0; i<ctx->column_count; i++) {
  ------------------
  |  Branch (821:15): [True: 1.42M, False: 159]
  ------------------
  822|  1.42M|        ctx->variables[i] = sas7bdat_init_variable(ctx, i, index_after_skipping, &retval);
  823|  1.42M|        if (ctx->variables[i] == NULL)
  ------------------
  |  Branch (823:13): [True: 194, False: 1.42M]
  ------------------
  824|    194|            break;
  825|       |
  826|  1.42M|        int cb_retval = READSTAT_HANDLER_OK;
  827|  1.42M|        if (ctx->handle.variable) {
  ------------------
  |  Branch (827:13): [True: 1.42M, False: 0]
  ------------------
  828|  1.42M|            cb_retval = ctx->handle.variable(i, ctx->variables[i], ctx->variables[i]->format, ctx->user_ctx);
  829|  1.42M|        }
  830|  1.42M|        if (cb_retval == READSTAT_HANDLER_ABORT) {
  ------------------
  |  Branch (830:13): [True: 0, False: 1.42M]
  ------------------
  831|      0|            retval = READSTAT_ERROR_USER_ABORT;
  832|      0|            goto cleanup;
  833|      0|        }
  834|       |
  835|  1.42M|        if (cb_retval == READSTAT_HANDLER_SKIP_VARIABLE) {
  ------------------
  |  Branch (835:13): [True: 0, False: 1.42M]
  ------------------
  836|      0|            ctx->variables[i]->skip = 1;
  837|  1.42M|        } else {
  838|  1.42M|            index_after_skipping++;
  839|  1.42M|        }
  840|  1.42M|    }
  841|    993|cleanup:
  842|    993|    return retval;
  843|    353|}
readstat_sas7bdat_read.c:sas7bdat_init_variable:
  725|  1.42M|        int index_after_skipping, readstat_error_t *out_retval) {
  726|  1.42M|    readstat_error_t retval = READSTAT_OK;
  727|  1.42M|    readstat_variable_t *variable = readstat_calloc(1, sizeof(readstat_variable_t));
  728|       |
  729|  1.42M|    variable->index = i;
  730|  1.42M|    variable->index_after_skipping = index_after_skipping;
  731|  1.42M|    variable->type = ctx->col_info[i].type;
  732|  1.42M|    variable->storage_width = ctx->col_info[i].width;
  733|       |
  734|  1.42M|    if ((retval = sas7bdat_validate_column(&ctx->col_info[i])) != READSTAT_OK) {
  ------------------
  |  Branch (734:9): [True: 65, False: 1.42M]
  ------------------
  735|     65|        goto cleanup;
  736|     65|    }
  737|  1.42M|    if ((retval = sas7bdat_copy_text_ref(variable->name, sizeof(variable->name), 
  ------------------
  |  Branch (737:9): [True: 46, False: 1.42M]
  ------------------
  738|  1.42M|                    ctx->col_info[i].name_ref, ctx)) != READSTAT_OK) {
  739|     46|        goto cleanup;
  740|     46|    }
  741|  1.42M|    if ((retval = sas7bdat_copy_text_ref(variable->format, sizeof(variable->format),
  ------------------
  |  Branch (741:9): [True: 35, False: 1.42M]
  ------------------
  742|  1.42M|                    ctx->col_info[i].format_ref, ctx)) != READSTAT_OK) {
  743|     35|        goto cleanup;
  744|     35|    }
  745|  1.42M|    size_t len = strlen(variable->format);
  746|  1.42M|    if (ctx->col_info[i].format_width) {
  ------------------
  |  Branch (746:9): [True: 530, False: 1.42M]
  ------------------
  747|    530|        len += snprintf(variable->format + len, sizeof(variable->format) - len,
  748|    530|                "%d", ctx->col_info[i].format_width);
  749|    530|    }
  750|  1.42M|    if (len && ctx->col_info[i].format_digits) {
  ------------------
  |  Branch (750:9): [True: 555, False: 1.42M]
  |  Branch (750:16): [True: 349, False: 206]
  ------------------
  751|    349|        len += snprintf(variable->format + len, sizeof(variable->format) - len,
  752|    349|                ".%d", ctx->col_info[i].format_digits);
  753|    349|    }
  754|  1.42M|    if (len) { // TODO where is the informat saved?
  ------------------
  |  Branch (754:9): [True: 555, False: 1.42M]
  ------------------
  755|    555|        readstat_variable_set_informat(variable, variable->format);
  756|    555|    }
  757|  1.42M|    if ((retval = sas7bdat_copy_text_ref(variable->label, sizeof(variable->label), 
  ------------------
  |  Branch (757:9): [True: 48, False: 1.42M]
  ------------------
  758|  1.42M|                    ctx->col_info[i].label_ref, ctx)) != READSTAT_OK) {
  759|     48|        goto cleanup;
  760|     48|    }
  761|       |
  762|  1.42M|cleanup:
  763|  1.42M|    if (retval != READSTAT_OK) {
  ------------------
  |  Branch (763:9): [True: 194, False: 1.42M]
  ------------------
  764|    194|        if (out_retval)
  ------------------
  |  Branch (764:13): [True: 194, False: 0]
  ------------------
  765|    194|            *out_retval = retval;
  766|       |
  767|    194|        if (retval == READSTAT_ERROR_CONVERT_BAD_STRING) {
  ------------------
  |  Branch (767:13): [True: 5, False: 189]
  ------------------
  768|      5|            if (ctx->handle.error) {
  ------------------
  |  Branch (768:17): [True: 0, False: 5]
  ------------------
  769|      0|                snprintf(ctx->error_buf, sizeof(ctx->error_buf),
  770|      0|                        "ReadStat: Error converting variable #%d info to specified encoding: %s %s (%s)",
  771|      0|                        i, variable->name, variable->format, variable->label);
  772|      0|                ctx->handle.error(ctx->error_buf, ctx->user_ctx);
  773|      0|            }
  774|      5|        }
  775|       |
  776|    194|        free(variable);
  777|       |
  778|    194|        return NULL;
  779|    194|    }
  780|       |
  781|  1.42M|    return variable;
  782|  1.42M|}
readstat_sas7bdat_read.c:sas7bdat_validate_column:
  710|  1.42M|static readstat_error_t sas7bdat_validate_column(col_info_t *col_info) {
  711|  1.42M|    if (col_info->type == READSTAT_TYPE_DOUBLE) {
  ------------------
  |  Branch (711:9): [True: 1.26k, False: 1.42M]
  ------------------
  712|  1.26k|        if (col_info->width > 8 || col_info->width < 3) {
  ------------------
  |  Branch (712:13): [True: 39, False: 1.22k]
  |  Branch (712:36): [True: 0, False: 1.22k]
  ------------------
  713|     39|            return READSTAT_ERROR_PARSE;
  714|     39|        }
  715|  1.26k|    }
  716|  1.42M|    if (col_info->type == READSTAT_TYPE_STRING) {
  ------------------
  |  Branch (716:9): [True: 1.42M, False: 1.22k]
  ------------------
  717|  1.42M|        if (col_info->width > INT16_MAX) {
  ------------------
  |  Branch (717:13): [True: 26, False: 1.42M]
  ------------------
  718|     26|            return READSTAT_ERROR_PARSE;
  719|     26|        }
  720|  1.42M|    }
  721|  1.42M|    return READSTAT_OK;
  722|  1.42M|}
readstat_sas7bdat_read.c:sas7bdat_update_progress:
  132|  4.96k|static readstat_error_t sas7bdat_update_progress(sas7bdat_ctx_t *ctx) {
  133|  4.96k|    readstat_io_t *io = ctx->io;
  134|  4.96k|    return io->update(ctx->file_size, ctx->handle.progress, ctx->user_ctx, io->io_ctx);
  135|  4.96k|}
readstat_sas7bdat_read.c:sas7bdat_ctx_free:
   98|  2.79k|static void sas7bdat_ctx_free(sas7bdat_ctx_t *ctx) {
   99|  2.79k|    int i;
  100|  2.79k|    if (ctx->text_blobs) {
  ------------------
  |  Branch (100:9): [True: 626, False: 2.17k]
  ------------------
  101|  2.61k|        for (i=0; i<ctx->text_blob_count; i++) {
  ------------------
  |  Branch (101:19): [True: 1.98k, False: 626]
  ------------------
  102|  1.98k|            free(ctx->text_blobs[i]);
  103|  1.98k|        }
  104|    626|        free(ctx->text_blobs);
  105|    626|        free(ctx->text_blob_lengths);
  106|    626|    }
  107|  2.79k|    if (ctx->variables) {
  ------------------
  |  Branch (107:9): [True: 353, False: 2.44k]
  ------------------
  108|  11.6M|        for (i=0; i<ctx->column_count; i++) {
  ------------------
  |  Branch (108:19): [True: 11.6M, False: 353]
  ------------------
  109|  11.6M|            if (ctx->variables[i])
  ------------------
  |  Branch (109:17): [True: 1.42M, False: 10.2M]
  ------------------
  110|  1.42M|                free(ctx->variables[i]);
  111|  11.6M|        }
  112|    353|        free(ctx->variables);
  113|    353|    }
  114|  2.79k|    if (ctx->col_info)
  ------------------
  |  Branch (114:9): [True: 465, False: 2.33k]
  ------------------
  115|    465|        free(ctx->col_info);
  116|       |
  117|  2.79k|    if (ctx->scratch_buffer)
  ------------------
  |  Branch (117:9): [True: 447, False: 2.35k]
  ------------------
  118|    447|        free(ctx->scratch_buffer);
  119|       |
  120|  2.79k|    if (ctx->page)
  ------------------
  |  Branch (120:9): [True: 2.40k, False: 397]
  ------------------
  121|  2.40k|        free(ctx->page);
  122|       |
  123|  2.79k|    if (ctx->row)
  ------------------
  |  Branch (123:9): [True: 702, False: 2.09k]
  ------------------
  124|    702|        free(ctx->row);
  125|       |
  126|  2.79k|    if (ctx->converter)
  ------------------
  |  Branch (126:9): [True: 1.73k, False: 1.06k]
  ------------------
  127|  1.73k|        iconv_close(ctx->converter);
  128|       |
  129|  2.79k|    free(ctx);
  130|  2.79k|}

sas_rle_decompress:
   47|    584|        const void *input_buf, size_t input_len) {
   48|    584|    unsigned char *buffer = (unsigned char *)output_buf;
   49|    584|    unsigned char *output = buffer;
   50|    584|    size_t output_written = 0;
   51|       |
   52|    584|    const unsigned char *input = (const unsigned char *)input_buf;
   53|       |
   54|  50.6k|    while (input < (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (54:12): [True: 50.1k, False: 504]
  ------------------
   55|  50.1k|        unsigned char control = *input++;
   56|  50.1k|        unsigned char command = (control & 0xF0) >> 4;
   57|  50.1k|        unsigned char length = (control & 0x0F);
   58|  50.1k|        int copy_len = 0;
   59|  50.1k|        int insert_len = 0;
   60|  50.1k|        unsigned char insert_byte = '\0';
   61|  50.1k|        if (input + command_lengths[command] > (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (61:13): [True: 5, False: 50.1k]
  ------------------
   62|      5|            return -1;
   63|      5|        }
   64|  50.1k|        switch (command) {
   65|  4.76k|            case SAS_RLE_COMMAND_COPY64:
  ------------------
  |  |   13|  4.76k|#define SAS_RLE_COMMAND_COPY64          0
  ------------------
  |  Branch (65:13): [True: 4.76k, False: 45.4k]
  ------------------
   66|  4.76k|                copy_len = (*input++) + 64 + length * 256;
   67|  4.76k|                break;
   68|    192|            case SAS_RLE_COMMAND_COPY64_PLUS_4096:
  ------------------
  |  |   14|    192|#define SAS_RLE_COMMAND_COPY64_PLUS_4096 1
  ------------------
  |  Branch (68:13): [True: 192, False: 49.9k]
  ------------------
   69|    192|                copy_len = (*input++) + 64 + length * 256 + 4096;
   70|    192|                break;
   71|    564|            case SAS_RLE_COMMAND_COPY96: copy_len = length + 96; break;
  ------------------
  |  |   15|    564|#define SAS_RLE_COMMAND_COPY96          2
  ------------------
  |  Branch (71:13): [True: 564, False: 49.6k]
  ------------------
   72|    513|            case SAS_RLE_COMMAND_INSERT_BYTE18:
  ------------------
  |  |   16|    513|#define SAS_RLE_COMMAND_INSERT_BYTE18   4
  ------------------
  |  Branch (72:13): [True: 513, False: 49.6k]
  ------------------
   73|    513|                insert_len = (*input++) + 18 + length * 256;
   74|    513|                insert_byte = *input++;
   75|    513|                break;
   76|  1.16k|            case SAS_RLE_COMMAND_INSERT_AT17:
  ------------------
  |  |   17|  1.16k|#define SAS_RLE_COMMAND_INSERT_AT17     5
  ------------------
  |  Branch (76:13): [True: 1.16k, False: 49.0k]
  ------------------
   77|  1.16k|                insert_len = (*input++) + 17 + length * 256;
   78|  1.16k|                insert_byte = '@';
   79|  1.16k|                break;
   80|  1.53k|            case SAS_RLE_COMMAND_INSERT_BLANK17:
  ------------------
  |  |   18|  1.53k|#define SAS_RLE_COMMAND_INSERT_BLANK17  6
  ------------------
  |  Branch (80:13): [True: 1.53k, False: 48.6k]
  ------------------
   81|  1.53k|                insert_len = (*input++) + 17 + length * 256;
   82|  1.53k|                insert_byte = ' ';
   83|  1.53k|                break;
   84|  12.6k|            case SAS_RLE_COMMAND_INSERT_ZERO17:
  ------------------
  |  |   19|  12.6k|#define SAS_RLE_COMMAND_INSERT_ZERO17   7
  ------------------
  |  Branch (84:13): [True: 12.6k, False: 37.5k]
  ------------------
   85|  12.6k|                insert_len = (*input++) + 17 + length * 256;
   86|  12.6k|                insert_byte = '\0';
   87|  12.6k|                break;
   88|  1.34k|            case SAS_RLE_COMMAND_COPY1:  copy_len = length + 1; break;
  ------------------
  |  |   20|  1.34k|#define SAS_RLE_COMMAND_COPY1           8
  ------------------
  |  Branch (88:13): [True: 1.34k, False: 48.8k]
  ------------------
   89|  7.16k|            case SAS_RLE_COMMAND_COPY17: copy_len = length + 17; break;
  ------------------
  |  |   21|  7.16k|#define SAS_RLE_COMMAND_COPY17          9
  ------------------
  |  Branch (89:13): [True: 7.16k, False: 43.0k]
  ------------------
   90|    656|            case SAS_RLE_COMMAND_COPY33: copy_len = length + 33; break;
  ------------------
  |  |   22|    656|#define SAS_RLE_COMMAND_COPY33         10
  ------------------
  |  Branch (90:13): [True: 656, False: 49.5k]
  ------------------
   91|    380|            case SAS_RLE_COMMAND_COPY49: copy_len = length + 49; break;
  ------------------
  |  |   23|    380|#define SAS_RLE_COMMAND_COPY49         11
  ------------------
  |  Branch (91:13): [True: 380, False: 49.7k]
  ------------------
   92|    982|            case SAS_RLE_COMMAND_INSERT_BYTE3:
  ------------------
  |  |   24|    982|#define SAS_RLE_COMMAND_INSERT_BYTE3   12
  ------------------
  |  Branch (92:13): [True: 982, False: 49.1k]
  ------------------
   93|    982|                insert_byte = *input++;
   94|    982|                insert_len = length + 3;
   95|    982|                break;
   96|  1.67k|            case SAS_RLE_COMMAND_INSERT_AT2:
  ------------------
  |  |   25|  1.67k|#define SAS_RLE_COMMAND_INSERT_AT2     13
  ------------------
  |  Branch (96:13): [True: 1.67k, False: 48.5k]
  ------------------
   97|  1.67k|                insert_byte = '@';
   98|  1.67k|                insert_len = length + 2;
   99|  1.67k|                break;
  100|  5.02k|            case SAS_RLE_COMMAND_INSERT_BLANK2:
  ------------------
  |  |   26|  5.02k|#define SAS_RLE_COMMAND_INSERT_BLANK2  14
  ------------------
  |  Branch (100:13): [True: 5.02k, False: 45.1k]
  ------------------
  101|  5.02k|                insert_byte = ' ';
  102|  5.02k|                insert_len = length + 2;
  103|  5.02k|                break;
  104|  8.26k|            case SAS_RLE_COMMAND_INSERT_ZERO2:
  ------------------
  |  |   27|  8.26k|#define SAS_RLE_COMMAND_INSERT_ZERO2   15
  ------------------
  |  Branch (104:13): [True: 8.26k, False: 41.9k]
  ------------------
  105|  8.26k|                insert_byte = '\0';
  106|  8.26k|                insert_len = length + 2;
  107|  8.26k|                break;
  108|  3.31k|            default:
  ------------------
  |  Branch (108:13): [True: 3.31k, False: 46.8k]
  ------------------
  109|       |                /* error out here? */
  110|  3.31k|                break;
  111|  50.1k|        }
  112|  50.1k|        if (copy_len) {
  ------------------
  |  Branch (112:13): [True: 15.0k, False: 35.1k]
  ------------------
  113|  15.0k|            if (output_written + copy_len > output_len) {
  ------------------
  |  Branch (113:17): [True: 11, False: 15.0k]
  ------------------
  114|     11|                return -1;
  115|     11|            }
  116|  15.0k|            if (input + copy_len > (const unsigned char *)input_buf + input_len) {
  ------------------
  |  Branch (116:17): [True: 59, False: 14.9k]
  ------------------
  117|     59|                return -1;
  118|     59|            }
  119|  14.9k|            if (output) {
  ------------------
  |  Branch (119:17): [True: 14.9k, False: 0]
  ------------------
  120|  14.9k|                memcpy(&output[output_written], input, copy_len);
  121|  14.9k|            }
  122|  14.9k|            input += copy_len;
  123|  14.9k|            output_written += copy_len;
  124|  14.9k|        }
  125|  50.1k|        if (insert_len) {
  ------------------
  |  Branch (125:13): [True: 31.8k, False: 18.3k]
  ------------------
  126|  31.8k|            if (output_written + insert_len > output_len) {
  ------------------
  |  Branch (126:17): [True: 5, False: 31.7k]
  ------------------
  127|      5|                return -1;
  128|      5|            }
  129|  31.7k|            if (output) {
  ------------------
  |  Branch (129:17): [True: 31.7k, False: 0]
  ------------------
  130|  31.7k|                memset(&output[output_written], insert_byte, insert_len);
  131|  31.7k|            }
  132|  31.7k|            output_written += insert_len;
  133|  31.7k|        }
  134|  50.1k|    }
  135|       |
  136|    504|    return output_written;
  137|    584|}

rt_open_handler:
    8|  2.79k|int rt_open_handler(const char *path, void *io_ctx) {
    9|  2.79k|    return 0;
   10|  2.79k|}
rt_close_handler:
   12|  2.79k|int rt_close_handler(void *io_ctx) {
   13|  2.79k|    return 0;
   14|  2.79k|}
rt_seek_handler:
   17|  24.7k|        readstat_io_flags_t whence, void *io_ctx) {
   18|  24.7k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   19|  24.7k|    readstat_off_t newpos = -1;
   20|  24.7k|    if (whence == READSTAT_SEEK_SET) {
  ------------------
  |  Branch (20:9): [True: 16.6k, False: 8.04k]
  ------------------
   21|  16.6k|        newpos = offset;
   22|  16.6k|    } else if (whence == READSTAT_SEEK_CUR) {
  ------------------
  |  Branch (22:16): [True: 5.24k, False: 2.79k]
  ------------------
   23|  5.24k|        newpos = buffer_ctx->pos + offset;
   24|  5.24k|    } else if (whence == READSTAT_SEEK_END) {
  ------------------
  |  Branch (24:16): [True: 2.79k, False: 0]
  ------------------
   25|  2.79k|        newpos = buffer_ctx->buffer->used + offset;
   26|  2.79k|    }
   27|       |
   28|  24.7k|    if (newpos < 0)
  ------------------
  |  Branch (28:9): [True: 108, False: 24.6k]
  ------------------
   29|    108|        return -1;
   30|       |
   31|  24.6k|    if (newpos > buffer_ctx->buffer->used)
  ------------------
  |  Branch (31:9): [True: 187, False: 24.4k]
  ------------------
   32|    187|        return -1;
   33|       |
   34|  24.4k|    buffer_ctx->pos = newpos;
   35|  24.4k|    return newpos;
   36|  24.6k|}
rt_read_handler:
   38|  44.5k|ssize_t rt_read_handler(void *buf, size_t nbytes, void *io_ctx) {
   39|  44.5k|    rt_buffer_ctx_t *buffer_ctx = (rt_buffer_ctx_t *)io_ctx;
   40|  44.5k|    ssize_t bytes_copied = 0;
   41|  44.5k|    ssize_t bytes_left = buffer_ctx->buffer->used - buffer_ctx->pos;
   42|  44.5k|    if (nbytes <= bytes_left) {
  ------------------
  |  Branch (42:9): [True: 44.1k, False: 399]
  ------------------
   43|  44.1k|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, nbytes);
   44|  44.1k|        bytes_copied = nbytes;
   45|  44.1k|    } else if (bytes_left > 0) {
  ------------------
  |  Branch (45:16): [True: 234, False: 165]
  ------------------
   46|    234|        memcpy(buf, buffer_ctx->buffer->bytes + buffer_ctx->pos, bytes_left);
   47|    234|        bytes_copied = bytes_left;
   48|    234|    }
   49|  44.5k|    buffer_ctx->pos += bytes_copied;
   50|  44.5k|    return bytes_copied;
   51|  44.5k|}
rt_update_handler:
   54|  4.96k|        void *user_ctx, void *io_ctx) {
   55|  4.96k|    if (!progress_handler)
  ------------------
  |  Branch (55:9): [True: 4.96k, False: 0]
  ------------------
   56|  4.96k|        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|}

