tlog_grc tlog_source_read(struct tlog_source *source, struct tlog_pkt *pkt) { tlog_grc grc; struct tlog_msg *msg; assert(tlog_source_is_valid(source)); assert(tlog_pkt_is_valid(pkt)); assert(tlog_pkt_is_void(pkt)); msg = &source->msg; while (true) { if (tlog_msg_is_void(msg)) { grc = tlog_source_read_msg(source); if (grc != TLOG_RC_OK) return grc; if (tlog_msg_is_void(msg)) return TLOG_RC_OK; if (source->got_msg) { if (msg->id != (source->last_msg_id + 1)) { tlog_msg_cleanup(msg); return TLOG_RC_SOURCE_MSG_ID_OUT_OF_ORDER; } } else { source->got_msg = true; } source->last_msg_id = msg->id; } grc = tlog_msg_read(msg, pkt, source->io_buf, source->io_size); if (grc != TLOG_RC_OK) { tlog_msg_cleanup(msg); return grc; } if (tlog_pkt_is_void(pkt)) { tlog_msg_cleanup(msg); } else { if (source->got_pkt) { if (tlog_timespec_cmp(&pkt->timestamp, &source->last_pkt_ts) < 0) { tlog_pkt_cleanup(pkt); tlog_msg_cleanup(msg); return TLOG_RC_SOURCE_PKT_TS_OUT_OF_ORDER; } } else { source->got_pkt = true; } source->last_pkt_ts = pkt->timestamp; return TLOG_RC_OK; } } }
/** Get log data set item code for a (non-void) packet */ static enum tlog_log_item tlog_log_item_from_pkt(const struct tlog_pkt *pkt) { assert(tlog_pkt_is_valid(pkt)); assert(!tlog_pkt_is_void(pkt)); return pkt->type == TLOG_PKT_TYPE_WINDOW ? TLOG_LOG_ITEM_WINDOW : (pkt->data.io.output ? TLOG_LOG_ITEM_OUTPUT : TLOG_LOG_ITEM_INPUT); }
tlog_grc tlog_json_msg_read(struct tlog_json_msg *msg, struct tlog_pkt *pkt, uint8_t *io_buf, size_t io_size) { const char *timing_ptr; size_t io_len = 0; bool io_full = false; bool pkt_output; assert(tlog_json_msg_is_valid(msg)); assert(!tlog_json_msg_is_void(msg)); assert(tlog_pkt_is_valid(pkt)); assert(tlog_pkt_is_void(pkt)); assert(io_buf != NULL); assert(io_size >= TLOG_JSON_MSG_IO_SIZE_MIN); /* Until the I/O buffer (io_buf/io_size) is full */ do { /* * Read next timing record if the current one is spent */ if (msg->rem == 0) { char type_buf[2]; char type; int read; uint64_t first_val; uint64_t second_val; struct timespec delay; /* Skip leading whitespace */ while (true) { switch (*msg->timing_ptr) { case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': msg->timing_ptr++; continue; } break; } /* Modify timing pointer on the side to be able to rollback */ timing_ptr = msg->timing_ptr; /* If reached the end of timing and so the end of data */ if (*timing_ptr == 0) { /* Return whatever we have */ break; } if (sscanf(timing_ptr, "%1[][><+=]%" SCNu64 "%n", type_buf, &first_val, &read) < 2) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } type = *type_buf; timing_ptr += read; if (type == '[' || type == ']') { if (sscanf(timing_ptr, "/%" SCNu64 "%n", &second_val, &read) < 1) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } timing_ptr += read; } else if (type == '=') { if (sscanf(timing_ptr, "x%" SCNu64 "%n", &second_val, &read) < 1) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } timing_ptr += read; } else { second_val = 0; } /* If it is a delay record */ if (type == '+') { if (first_val != 0) { if (first_val > TLOG_DELAY_MAX_MS_NUM) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } /* If there was I/O already */ if (io_len > 0) { /* * We gotta return the old pos packet and re-read * delay record next time */ break; } delay.tv_sec = first_val / 1000; delay.tv_nsec = first_val % 1000 * 1000000; tlog_timespec_add(&msg->pos, &delay, &msg->pos); } /* Timing record consumed */ msg->timing_ptr = timing_ptr; /* Read next timing record - no I/O from this one */ continue; /* If it is a window record */ } else if (type == '=') { /* If there was I/O already */ if (io_len > 0) { /* * We gotta return the I/O packet and re-read * window record next time */ break; } /* Check extents */ if (first_val > USHRT_MAX || second_val > USHRT_MAX) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } /* Return window packet */ tlog_pkt_init_window(pkt, &msg->pos, (unsigned short int)first_val, (unsigned short int)second_val); /* Timing record consumed */ msg->timing_ptr = timing_ptr; return TLOG_RC_OK; /* If it is a text input record */ } else if (type == '<') { if (first_val > SIZE_MAX) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } msg->output = false; msg->binary = false; msg->rem = first_val; msg->ptxt_ptr = &msg->in_txt_ptr; msg->ptxt_len = &msg->in_txt_len; /* If it is a binary input record */ } else if (type == '[') { if (first_val > SIZE_MAX || second_val > SIZE_MAX) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } msg->output = false; msg->binary = true; msg->rem = second_val; msg->ptxt_ptr = &msg->in_txt_ptr; msg->ptxt_len = &msg->in_txt_len; msg->bin_obj = msg->in_bin_obj; msg->pbin_pos = &msg->in_bin_pos; /* If it is a text output record */ } else if (type == '>') { if (first_val > SIZE_MAX) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } msg->output = true; msg->binary = false; msg->rem = first_val; msg->ptxt_ptr = &msg->out_txt_ptr; msg->ptxt_len = &msg->out_txt_len; /* If it is a binary output record */ } else if (type == ']') { if (first_val > SIZE_MAX || second_val > SIZE_MAX) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } msg->output = true; msg->binary = true; msg->rem = second_val; msg->ptxt_ptr = &msg->out_txt_ptr; msg->ptxt_len = &msg->out_txt_len; msg->bin_obj = msg->out_bin_obj; msg->pbin_pos = &msg->out_bin_pos; } else { assert(false); return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TIMING; } /* Timing record consumed */ msg->timing_ptr = timing_ptr; if (msg->binary) { size_t l; /* Skip replacement characters */ for (; first_val > 0; first_val--) { /* If not enough text */ if (*msg->ptxt_len == 0) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TXT; } l = tlog_json_msg_utf8_len(*(uint8_t *)*msg->ptxt_ptr); /* If found invalid UTF-8 character in text */ if (l == 0) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TXT; } /* If character crosses text boundary */ if (l > *msg->ptxt_len) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TXT; } *msg->ptxt_len -= l; *msg->ptxt_ptr += l; } } /* Ignore zero I/O */ if (msg->rem == 0) { continue; } } /* Stop if the packet I/O is in different direction */ if (io_len == 0) { pkt_output = msg->output; } else if (pkt_output != msg->output) { /* Return whatever we have */ break; } /* * Append (a piece of) I/O to the output buffer */ if (msg->binary) { struct json_object *o; int32_t n; for (; msg->rem > 0; msg->rem--, io_len++, (*msg->pbin_pos)++) { o = json_object_array_get_idx(msg->bin_obj, *msg->pbin_pos); /* If not enough bytes */ if (o == NULL) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_BIN; } /* If supposed byte is not an int */ if (json_object_get_type(o) != json_type_int) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_BIN; } n = json_object_get_int(o); /* If supposed byte value is out of range */ if (n < 0 || n > UINT8_MAX) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_BIN; } io_buf[io_len] = (uint8_t)n; /* If the I/O buffer is full */ if (io_len >= io_size) { io_full = true; break; } } } else { uint8_t b; size_t l; while (msg->rem > 0) { b = *(uint8_t *)*msg->ptxt_ptr; l = tlog_json_msg_utf8_len(b); /* If found invalid UTF-8 character in text */ if (l == 0) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TXT; } /* If character crosses text boundary */ if (l > *msg->ptxt_len) { return TLOG_RC_JSON_MSG_FIELD_INVALID_VALUE_TXT; } /* If character crosses the I/O buffer boundary */ if (io_len + l > io_size) { io_full = true; break; } while (true) { io_buf[io_len] = b; io_len++; (*msg->ptxt_len)--; (*msg->ptxt_ptr)++; l--; if (l == 0) { break; } b = *(uint8_t *)*msg->ptxt_ptr; } msg->rem--; } } } while (!io_full); if (io_len > 0) { tlog_pkt_init_io(pkt, &msg->pos, pkt_output, io_buf, false, io_len); } return TLOG_RC_OK; }