<commit_before>#include <getopt.h>
#include "dlib/bam_util.h"

int usage(char **argv, int retcode=EXIT_FAILURE) {
    fprintf(stderr, "MASKRIPPER version %s.\n"
                    "Usage: maskripper <opts> in.bam out.bam\n"
                    "Flags:\n-m: Minimum trimmed read length. Default: 0.\n"
                    "-l: output compression level. Default: 6.\n"
                    "-S: output in sam format.\n"
                    "Use - for stdin or stdout.\n", MASKRIPPER_VERSION);
    return retcode;
}
struct opts_t {
    uint8_t *data;
    uint32_t l_data:16;
    uint32_t m_data:16;
    uint32_t n_cigar:8;
    uint32_t min_trimmed_len:8;
    ~opts_t() {if(data) free(data);}
    void resize(uint32_t new_min) {
        if(new_min > m_data) {
            m_data = new_min;
            kroundup32(m_data);
            data = (uint8_t *)realloc(data, m_data * sizeof(uint8_t));
        }
    }
};

static inline int aux_type2size(uint8_t type)
{
    switch (type) {
    case 'A': case 'c': case 'C':
        return 1;
    case 's': case 'S':
        return 2;
    case 'i': case 'I': case 'f':
        return 4;
    case 'd':
        return 8;
    case 'Z': case 'H': case 'B':
        return type;
    default:
        return 0;
    }
}

static inline uint8_t *skip_aux(uint8_t *s)
{
    int size = aux_type2size(*s); ++s; // skip type
    uint32_t n;
    switch (size) {
    case 'Z':
    case 'H':
        while (*s) ++s;
        return s + 1;
    case 'B':
        size = aux_type2size(*s); ++s;
        memcpy(&n, s, 4); s += 4;
        return s + size * n;
    case 0:
        abort();
        break;
    default:
        return s + size;
    }
}

uint8_t *aux_aux_get(uint8_t *s, const char tag[2], uint8_t *end)
{
    LOG_DEBUG("Trying\n");
    int y = tag[0]<<8 | tag[1];
    while (s < end) {
        int x = (int)s[0]<<8 | s[1];
        s += 2;
        if (x == y) return s;
        s = skip_aux(s);
    }
    return 0;
}

template<typename T>
inline void trim_array_tag(bam1_t *b, const char *tag, const int n_start, const int n_end, const int final_len) {
    T tmp[300];
    if((n_start + n_end) == 0) return;
    LOG_DEBUG("Trying for tag %s.\n", tag);
    uint8_t *data = bam_aux_get(b, tag);
    uint8_t *td = data + 4;
    LOG_DEBUG("Len: %i.\n", *(int *)td);
    LOG_DEBUG("Type: %c, %i.\n", (char)*(data + 3), *(data + 3));
    LOG_DEBUG("n_start, final_len: %i, %i.\n", n_start, final_len);
    for(int i = 0; i < n_start + n_end + final_len; ++i) fprintf(stderr, ",%u", ((int *)(data + 8))[i]);
    memcpy(tmp, data + 8 + n_start * sizeof(T), final_len * sizeof(T)); // 8 for the tag, len, type.
    LOG_DEBUG("Old len: %i. New len: %i.\n", final_len + n_start + n_end, final_len);
    bam_aux_del(b, data);
    assert((data = bam_aux_get(b, tag)) == nullptr);
    for(int i = 0; i < final_len; ++i) fprintf(stderr, ",%u", tmp[i]);
    dlib::bam_aux_array_append(b, tag, 'I', sizeof(uint32_t), final_len, (uint8_t *)tmp);
}

inline void trim_array_tags(bam1_t *b, const int n_start, const int n_end, const int final_len) {
    LOG_DEBUG("Getting PV.\n");
    trim_array_tag<uint32_t>(b, "PV", n_start, n_end, final_len);
    LOG_DEBUG("Getting FA.\n");
    trim_array_tag<uint32_t>(b, "FA", n_start, n_end, final_len);
}

static int trim_ns(bam1_t *b, void *data) {
    // Currently passes all reads to keep balanced pairs. TODO: rewrite for pairs of reads and filter if both fail.
    opts_t *op((opts_t *)data);
    assert(bam_aux_get(b, "PV"));
    std::vector<uint8_t> aux(bam_get_aux(b), bam_get_aux(b) + bam_get_l_aux(b));
    int tmp;
    uint8_t *const seq(bam_get_seq(b));
    uint32_t *const cigar(bam_get_cigar(b));
    op->n_cigar = b->core.n_cigar;
    op->resize(b->l_data); // Make sure it's big enough to hold everything.
    memcpy(op->data, b->data, b->core.l_qname);

    // Get #Ns at the beginning
    for(tmp = 0; bam_seqi(seq, tmp) == dlib::htseq::HTS_N; ++tmp);
    const int n_start(tmp);

    if(tmp == b->core.l_qseq - 1) // all bases are N -- garbage read
         return 0; // Currently outputting to avoid

    // Get #Ns at the end
    for(tmp = b->core.l_qseq - 1; bam_seqi(seq, tmp) == dlib::htseq::HTS_N; --tmp);
    const int n_end(b->core.l_qseq - 1 - tmp);

    // Get new length for read
    const int final_len(b->core.l_qseq - n_end - n_start);
    if(final_len < op->min_trimmed_len) // Too short.
        return 0;

    if(n_end) {
        if((tmp = bam_cigar_oplen(cigar[b->core.n_cigar - 1]) - n_end) == 0) {
            LOG_DEBUG("Entire cigar operation is the softclip. Decrease the number of new cigar operations.\n");
            --op->n_cigar;
        } else {
            LOG_DEBUG("Updating second cigar operation in-place.\n");
            cigar[b->core.n_cigar - 1] = bam_cigar_gen(tmp, BAM_CSOFT_CLIP);
        }
    }

    // Get new n_cigar.
    if((tmp = bam_cigar_oplen(*cigar) - n_start) == 0) {
        --op->n_cigar;
        memcpy(op->data + b->core.l_qname, cigar + 1, op->n_cigar << 2); // << 2 for 4 bit per cigar op
    } else {
        if(n_start) *cigar = bam_cigar_gen(tmp, BAM_CSOFT_CLIP);
        memcpy(op->data + b->core.l_qname, cigar, op->n_cigar << 2);
    }
    uint8_t *opseq(op->data + b->core.l_qname + (op->n_cigar << 2)); // Pointer to the seq region of new data field.
    for(tmp = 0; tmp < final_len >> 1; ++tmp) {
        opseq[tmp] = (bam_seqi(seq, ((tmp << 1) + n_start)) << 4) | (bam_seqi(seq, (tmp << 1) + n_start + 1));
        assert(bam_seqi(opseq, tmp * 2) == bam_seqi(seq, (2 * tmp + n_start)));
        assert(bam_seqi(opseq, tmp * 2 + 1) == bam_seqi(seq, (2 * tmp + n_start + 1)));
    }
#if 0
    char tmpbuf[300];
    for(tmp = 0; tmp < final_len; ++tmp) {
        tmpbuf[tmp] = seq_nt16_str[bam_seqi(opseq, tmp)];
    }
    tmpbuf[tmp] = '\0';
    assert(strcmp(tmpbuf, "ANNCNNNGNNNNTNNNNCNNGGNNNNNNNNNCNNNNCNNNNNNNNAAAANNTNNNAAAAAAAAAAGAGAGAGGGAGAGAGACTATACACAGGCACCACCACATTTGGCTAATTTTT") == 0);
#endif

    // Copy in qual and all of aux.
    tmp = bam_get_l_aux(b);
    memcpy(opseq + ((final_len + 1) >> 1), bam_get_qual(b) + n_start, final_len + tmp);
    // Switch data strings
    std::swap(op->data, b->data);
    b->core.n_cigar = op->n_cigar;
    //b->l_data = b->core.l_qname + (op->n_cigar << 2) + ((final_len + 1) >> 1) + final_len + tmp;
    b->core.l_qseq = final_len;
    memcpy(bam_get_aux(b), aux.data(), aux.size());
    b->l_data = (bam_get_aux(b) - b->data) + aux.size();
    //trim_array_tags(b, n_start, n_end, final_len);
    bam_aux_append(b, "NE", 'i', sizeof(int), (uint8_t *)&n_end);
    bam_aux_append(b, "NS", 'i', sizeof(int), (uint8_t *)&n_start);
    return 0;
}

int main(int argc, char *argv[]) {
    if(argc < 3)
        return usage(argv);

    if(strcmp(argv[1], "--help") == 0)
        return usage(argv, EXIT_SUCCESS);

    int c;
    char out_mode[4] = "wb";
    opts_t opts{0};
    while((c = getopt(argc, argv, "m:l:h?S")) > -1) {
        switch(c) {
        case 'm': opts.min_trimmed_len = (uint32_t)atoi(optarg); break;
        case 'l': out_mode[2] = atoi(optarg) % 10 + '0'; break;
        case 'S': sprintf(out_mode, "w"); break;
        case 'h': case '?': return usage(argv, EXIT_SUCCESS);
        }
    }
    if(argc - 2 != optind)
        LOG_EXIT("Required: precisely two positional arguments (in bam, out bam).\n");

    LOG_DEBUG("ZOUNDS\n");
    // Actually this function. You can't really apply a null function....
    dlib::BamHandle inHandle(argv[optind]);
    dlib::BamHandle outHandle(argv[optind + 1], inHandle.header, out_mode);
    dlib::abstract_single_iter(inHandle.fp, inHandle.header, outHandle.fp,
                               &trim_ns, (void *)&opts);
    return EXIT_SUCCESS;
}
<commit_msg>Seqs are properly handled for both ends. Can't fix the tags yet. Moving on for a minute.lt<commit_after>#include <getopt.h>
#include "dlib/bam_util.h"

int usage(char **argv, int retcode=EXIT_FAILURE) {
    fprintf(stderr, "MASKRIPPER version %s.\n"
                    "Usage: maskripper <opts> in.bam out.bam\n"
                    "Flags:\n-m: Minimum trimmed read length. Default: 0.\n"
                    "-l: output compression level. Default: 6.\n"
                    "-S: output in sam format.\n"
                    "Use - for stdin or stdout.\n", MASKRIPPER_VERSION);
    return retcode;
}
struct opts_t {
    uint8_t *data;
    uint32_t l_data:16;
    uint32_t m_data:16;
    uint32_t n_cigar:8;
    uint32_t min_trimmed_len:8;
    ~opts_t() {if(data) free(data);}
    void resize(uint32_t new_min) {
        if(new_min > m_data) {
            m_data = new_min;
            kroundup32(m_data);
            data = (uint8_t *)realloc(data, m_data * sizeof(uint8_t));
        }
    }
};

static inline int aux_type2size(uint8_t type)
{
    switch (type) {
    case 'A': case 'c': case 'C':
        return 1;
    case 's': case 'S':
        return 2;
    case 'i': case 'I': case 'f':
        return 4;
    case 'd':
        return 8;
    case 'Z': case 'H': case 'B':
        return type;
    default:
        return 0;
    }
}

static inline uint8_t *skip_aux(uint8_t *s)
{
    int size = aux_type2size(*s); ++s; // skip type
    uint32_t n;
    switch (size) {
    case 'Z':
    case 'H':
        while (*s) ++s;
        return s + 1;
    case 'B':
        size = aux_type2size(*s); ++s;
        memcpy(&n, s, 4); s += 4;
        return s + size * n;
    case 0:
        abort();
        break;
    default:
        return s + size;
    }
}

uint8_t *aux_aux_get(uint8_t *s, const char tag[2], uint8_t *end)
{
    LOG_DEBUG("Trying\n");
    int y = tag[0]<<8 | tag[1];
    while (s < end) {
        int x = (int)s[0]<<8 | s[1];
        s += 2;
        if (x == y) return s;
        s = skip_aux(s);
    }
    return 0;
}

template<typename T>
inline void trim_array_tag(bam1_t *b, const char *tag, const int n_start, const int n_end, const int final_len) {
    T tmp[300];
    if((n_start + n_end) == 0) return;
    LOG_DEBUG("Trying for tag %s.\n", tag);
    uint8_t *data = bam_aux_get(b, tag);
    uint8_t *td = data + 4;
    LOG_DEBUG("Len: %i.\n", *(int *)td);
    LOG_DEBUG("Type: %c, %i.\n", (char)*(data + 3), *(data + 3));
    LOG_DEBUG("n_start, final_len: %i, %i.\n", n_start, final_len);
    for(int i = 0; i < n_start + n_end + final_len; ++i) fprintf(stderr, ",%u", ((int *)(data + 8))[i]);
    memcpy(tmp, data + 8 + n_start * sizeof(T), final_len * sizeof(T)); // 8 for the tag, len, type.
    LOG_DEBUG("Old len: %i. New len: %i.\n", final_len + n_start + n_end, final_len);
    bam_aux_del(b, data);
    assert((data = bam_aux_get(b, tag)) == nullptr);
    for(int i = 0; i < final_len; ++i) fprintf(stderr, ",%u", tmp[i]);
    dlib::bam_aux_array_append(b, tag, 'I', sizeof(uint32_t), final_len, (uint8_t *)tmp);
}

inline void trim_array_tags(bam1_t *b, const int n_start, const int n_end, const int final_len) {
    LOG_DEBUG("Getting PV.\n");
    trim_array_tag<uint32_t>(b, "PV", n_start, n_end, final_len);
    LOG_DEBUG("Getting FA.\n");
    trim_array_tag<uint32_t>(b, "FA", n_start, n_end, final_len);
}

static int trim_ns(bam1_t *b, void *data) {
    // Currently passes all reads to keep balanced pairs. TODO: rewrite for pairs of reads and filter if both fail.
    opts_t *op((opts_t *)data);
    assert(bam_aux_get(b, "PV"));
    std::vector<uint8_t> aux(bam_get_aux(b), bam_get_aux(b) + bam_get_l_aux(b));
    int tmp;
    uint8_t *const seq(bam_get_seq(b));
    uint32_t *const cigar(bam_get_cigar(b));
    op->n_cigar = b->core.n_cigar;
    op->resize(b->l_data); // Make sure it's big enough to hold everything.
    memcpy(op->data, b->data, b->core.l_qname);

    // Get #Ns at the beginning
    for(tmp = 0; bam_seqi(seq, tmp) == dlib::htseq::HTS_N; ++tmp);
    const int n_start(tmp);

    if(tmp == b->core.l_qseq - 1) // all bases are N -- garbage read
         return 0; // Currently outputting to avoid

    // Get #Ns at the end
    for(tmp = b->core.l_qseq - 1; bam_seqi(seq, tmp) == dlib::htseq::HTS_N; --tmp);
    const int n_end(b->core.l_qseq - 1 - tmp);

    // Get new length for read
    const int final_len(b->core.l_qseq - n_end - n_start);
    if(final_len < op->min_trimmed_len) // Too short.
        return 0;

    if(n_end) {
        if((tmp = bam_cigar_oplen(cigar[b->core.n_cigar - 1]) - n_end) == 0) {
            LOG_DEBUG("Entire cigar operation is the softclip. Decrease the number of new cigar operations.\n");
            --op->n_cigar;
        } else {
            LOG_DEBUG("Updating second cigar operation in-place.\n");
            cigar[b->core.n_cigar - 1] = bam_cigar_gen(tmp, BAM_CSOFT_CLIP);
        }
    }

    // Get new n_cigar.
    if((tmp = bam_cigar_oplen(*cigar) - n_start) == 0) {
        --op->n_cigar;
        memcpy(op->data + b->core.l_qname, cigar + 1, op->n_cigar << 2); // << 2 for 4 bit per cigar op
    } else {
        if(n_start) *cigar = bam_cigar_gen(tmp, BAM_CSOFT_CLIP);
        memcpy(op->data + b->core.l_qname, cigar, op->n_cigar << 2);
    }
    uint8_t *opseq(op->data + b->core.l_qname + (op->n_cigar << 2)); // Pointer to the seq region of new data field.
    for(tmp = 0; tmp < final_len >> 1; ++tmp) {
        opseq[tmp] = (bam_seqi(seq, ((tmp << 1) + n_start)) << 4) | (bam_seqi(seq, (tmp << 1) + n_start + 1));
        assert(bam_seqi(opseq, tmp * 2) == bam_seqi(seq, (2 * tmp + n_start)));
        assert(bam_seqi(opseq, tmp * 2 + 1) == bam_seqi(seq, (2 * tmp + n_start + 1)));
    }
#if 0
    char tmpbuf[300];
    for(tmp = 0; tmp < final_len; ++tmp) {
        tmpbuf[tmp] = seq_nt16_str[bam_seqi(opseq, tmp)];
    }
    tmpbuf[tmp] = '\0';
    assert(strcmp(tmpbuf, "ANNCNNNGNNNNTNNNNCNNGGNNNNNNNNNCNNNNCNNNNNNNNAAAANNTNNNAAAAAAAAAAGAGAGAGGGAGAGAGACTATACACAGGCACCACCACATTTGGCTAATTTTT") == 0);
#endif

    // Copy in qual and all of aux.
    tmp = bam_get_l_aux(b);
    memcpy(opseq + ((final_len + 1) >> 1), bam_get_qual(b) + n_start, final_len + tmp);
    // Switch data strings
    std::swap(op->data, b->data);
    b->core.n_cigar = op->n_cigar;
    //b->l_data = b->core.l_qname + (op->n_cigar << 2) + ((final_len + 1) >> 1) + final_len + tmp;
    b->core.l_qseq = final_len;
    memcpy(bam_get_aux(b), aux.data(), aux.size());
    b->l_data = (bam_get_aux(b) - b->data) + aux.size();
    //trim_array_tags(b, n_start, n_end, final_len);
    //bam_aux_append(b, "NE", 'i', sizeof(int), (uint8_t *)&n_end);
    //bam_aux_append(b, "NS", 'i', sizeof(int), (uint8_t *)&n_start);
    return 0;
}

int main(int argc, char *argv[]) {
    if(argc < 3)
        return usage(argv);

    if(strcmp(argv[1], "--help") == 0)
        return usage(argv, EXIT_SUCCESS);

    int c;
    char out_mode[4] = "wb";
    opts_t opts{0};
    while((c = getopt(argc, argv, "m:l:h?S")) > -1) {
        switch(c) {
        case 'm': opts.min_trimmed_len = (uint32_t)atoi(optarg); break;
        case 'l': out_mode[2] = atoi(optarg) % 10 + '0'; break;
        case 'S': sprintf(out_mode, "w"); break;
        case 'h': case '?': return usage(argv, EXIT_SUCCESS);
        }
    }
    if(argc - 2 != optind)
        LOG_EXIT("Required: precisely two positional arguments (in bam, out bam).\n");

    LOG_DEBUG("ZOUNDS\n");
    // Actually this function. You can't really apply a null function....
    dlib::BamHandle inHandle(argv[optind]);
    dlib::BamHandle outHandle(argv[optind + 1], inHandle.header, out_mode);
    dlib::abstract_single_iter(inHandle.fp, inHandle.header, outHandle.fp,
                               &trim_ns, (void *)&opts);
    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include "message_row.h"
#include <gdkmm/pixbufloader.h>
#include <glibmm/datetime.h>
#include <gtkmm/stock.h>
#include <libsoup/soup-uri.h>
#include <iostream>
#include "attachments_view.h"

MessageRow::MessageRow(const api_client &api_client, icon_loader &icon_loader,
                       emoji_loader &emoji_loader,
                       const users_store &users_store,
                       const channels_store &channels_store,
                       const Json::Value &payload)
    : hbox_(Gtk::ORIENTATION_HORIZONTAL),
      vbox_(Gtk::ORIENTATION_VERTICAL),
      info_hbox_(Gtk::ORIENTATION_HORIZONTAL),
      user_image_(Gtk::Stock::MISSING_IMAGE,
                  Gtk::IconSize(Gtk::ICON_SIZE_BUTTON)),
      user_label_("", Gtk::ALIGN_START, Gtk::ALIGN_CENTER),
      timestamp_label_("", Gtk::ALIGN_END, Gtk::ALIGN_CENTER),
      message_text_view_(users_store, channels_store, emoji_loader),
      file_image_(Gtk::Stock::MISSING_IMAGE,
                  Gtk::IconSize(Gtk::ICON_SIZE_BUTTON)),

      api_client_(api_client),
      icon_loader_(icon_loader),
      users_store_(users_store),
      channels_store_(channels_store) {
  add(hbox_);

  hbox_.pack_start(user_image_, Gtk::PACK_SHRINK);
  hbox_.pack_end(vbox_);

  vbox_.pack_start(info_hbox_, Gtk::PACK_SHRINK);

  info_hbox_.pack_start(user_label_, Gtk::PACK_SHRINK);
  info_hbox_.pack_end(timestamp_label_, Gtk::PACK_SHRINK);

  Pango::AttrList attrs;
  Pango::Attribute weight =
      Pango::Attribute::create_attr_weight(Pango::WEIGHT_BOLD);
  attrs.insert(weight);
  user_label_.set_attributes(attrs);

  const double ts = std::stof(payload["ts"].asString());
  const Glib::DateTime timestamp = Glib::DateTime::create_now_local(gint64(ts));
  timestamp_label_.set_text(timestamp.format("%F %R"));

  const Json::Value subtype_value = payload["subtype"];
  std::string text = payload["text"].asString();

  const std::string user_id = payload["user"].asString();
  const boost::optional<user> o_user = users_store_.find(user_id);
  if (o_user) {
    const user &user = o_user.get();
    user_label_.set_text(user.name);
    load_user_icon(user.profile.image_72);
  }

  bool is_message = false;

  if (subtype_value.isNull()) {
    is_message = true;
  } else {
    const std::string subtype = subtype_value.asString();
    if (subtype == "bot_message") {
      is_message = true;
      user_label_.set_text(payload["username"].asString() + " [BOT]");
      const Json::Value image64 = payload["icons"]["image_64"];
      const Json::Value image48 = payload["icons"]["image_48"];
      const Json::Value bot_id = payload["bot_id"];
      if (image64.isString()) {
        load_user_icon(image64.asString());
      } else if (image48.isString()) {
        load_user_icon(image48.asString());
      } else if (bot_id.isString()) {
        const boost::optional<user> ou = users_store_.find(bot_id.asString());
        if (ou) {
          load_user_icon(ou.get().icons.image_72);
        } else {
          std::cerr << "[MessageRow] cannot find bot user  " << bot_id
                    << std::endl;
        }
      } else {
        const std::string default_icon_url =
            "https://i0.wp.com/slack-assets2.s3-us-west-2.amazonaws.com/8390/"
            "img/avatars/ava_0002-48.png";
        load_user_icon(default_icon_url);
      }
    } else if (subtype == "channel_join") {
      const Json::Value inviter_value = payload["inviter"];
      if (inviter_value.isString()) {
        const boost::optional<user> o_inviter =
            users_store_.find(inviter_value.asString());
        if (o_inviter) {
          const user &inviter = o_inviter.get();
          text.append(" by invitation from <@")
              .append(inviter.id)
              .append("|")
              .append(inviter.name)
              .append(">");

        } else {
          std::cerr << "[MessageRow] cannot find channel_join inviter "
                    << inviter_value << std::endl;
        }
      }
    } else if (subtype == "channel_leave") {
      // nothing special
    } else if (subtype == "bot_add") {
      // nothing special
    } else if (subtype == "bot_remove") {
      // nothing special
    } else if (subtype == "file_share") {
      vbox_.pack_end(file_image_, Gtk::PACK_SHRINK);
      load_shared_file(payload);
    } else {
      std::cout << "Unhandled subtype " << subtype << ": \n"
                << payload << std::endl;
    }
  }
  if (payload["attachments"].isArray()) {
    auto attachments_view = Gtk::manage(new AttachmentsView(
        users_store, channels_store, emoji_loader, payload["attachments"]));
    vbox_.pack_end(*attachments_view);
  }
  vbox_.pack_end(message_text_view_);
  message_text_view_.set_text(text, is_message);

  show_all_children();
}

MessageRow::~MessageRow() {
}

void MessageRow::load_user_icon(const std::string &icon_url) {
  icon_loader_.load(icon_url, std::bind(&MessageRow::on_user_icon_loaded, this,
                                        std::placeholders::_1));
}

void MessageRow::on_user_icon_loaded(Glib::RefPtr<Gdk::Pixbuf> pixbuf) {
  user_image_.set(pixbuf->scale_simple(36, 36, Gdk::INTERP_BILINEAR));
}

void MessageRow::load_shared_file(const Json::Value &payload) {
  // TODO: Animated GIF support
  const Json::Value thumb_url = payload["file"]["thumb_360"];
  if (thumb_url.isString()) {
    api_client_.get_shared_file(thumb_url.asString(),
                                std::bind(&MessageRow::on_shared_file_loaded,
                                          this, std::placeholders::_1));
  } else {
    std::cerr << "[MessageRow] Invalid file_share payload " << payload
              << std::endl;
  }
}

void MessageRow::on_shared_file_loaded(Glib::RefPtr<Gdk::Pixbuf> pixbuf) {
  file_image_.set(pixbuf);
}

sigc::signal<void, const std::string &>
MessageRow::signal_channel_link_clicked() {
  return message_text_view_.signal_channel_link_clicked();
}

sigc::signal<void, const std::string &> MessageRow::signal_user_link_clicked() {
  return message_text_view_.signal_user_link_clicked();
}

std::string MessageRow::summary_for_notification() const {
  return user_label_.get_text() + ": " + message_text_view_.get_text();
}
<commit_msg>Fallback to bot's name<commit_after>#include "message_row.h"
#include <gdkmm/pixbufloader.h>
#include <glibmm/datetime.h>
#include <gtkmm/stock.h>
#include <libsoup/soup-uri.h>
#include <iostream>
#include "attachments_view.h"

MessageRow::MessageRow(const api_client &api_client, icon_loader &icon_loader,
                       emoji_loader &emoji_loader,
                       const users_store &users_store,
                       const channels_store &channels_store,
                       const Json::Value &payload)
    : hbox_(Gtk::ORIENTATION_HORIZONTAL),
      vbox_(Gtk::ORIENTATION_VERTICAL),
      info_hbox_(Gtk::ORIENTATION_HORIZONTAL),
      user_image_(Gtk::Stock::MISSING_IMAGE,
                  Gtk::IconSize(Gtk::ICON_SIZE_BUTTON)),
      user_label_("", Gtk::ALIGN_START, Gtk::ALIGN_CENTER),
      timestamp_label_("", Gtk::ALIGN_END, Gtk::ALIGN_CENTER),
      message_text_view_(users_store, channels_store, emoji_loader),
      file_image_(Gtk::Stock::MISSING_IMAGE,
                  Gtk::IconSize(Gtk::ICON_SIZE_BUTTON)),

      api_client_(api_client),
      icon_loader_(icon_loader),
      users_store_(users_store),
      channels_store_(channels_store) {
  add(hbox_);

  hbox_.pack_start(user_image_, Gtk::PACK_SHRINK);
  hbox_.pack_end(vbox_);

  vbox_.pack_start(info_hbox_, Gtk::PACK_SHRINK);

  info_hbox_.pack_start(user_label_, Gtk::PACK_SHRINK);
  info_hbox_.pack_end(timestamp_label_, Gtk::PACK_SHRINK);

  Pango::AttrList attrs;
  Pango::Attribute weight =
      Pango::Attribute::create_attr_weight(Pango::WEIGHT_BOLD);
  attrs.insert(weight);
  user_label_.set_attributes(attrs);

  const double ts = std::stof(payload["ts"].asString());
  const Glib::DateTime timestamp = Glib::DateTime::create_now_local(gint64(ts));
  timestamp_label_.set_text(timestamp.format("%F %R"));

  const Json::Value subtype_value = payload["subtype"];
  std::string text = payload["text"].asString();

  const std::string user_id = payload["user"].asString();
  const boost::optional<user> o_user = users_store_.find(user_id);
  if (o_user) {
    const user &user = o_user.get();
    user_label_.set_text(user.name);
    load_user_icon(user.profile.image_72);
  }

  bool is_message = false;

  if (subtype_value.isNull()) {
    is_message = true;
  } else {
    const std::string subtype = subtype_value.asString();
    if (subtype == "bot_message") {
      is_message = true;
      std::string username = payload["username"].asString();
      const Json::Value image64 = payload["icons"]["image_64"];
      const Json::Value image48 = payload["icons"]["image_48"];
      const Json::Value bot_id = payload["bot_id"];
      if (image64.isString()) {
        load_user_icon(image64.asString());
      } else if (image48.isString()) {
        load_user_icon(image48.asString());
      } else if (bot_id.isString()) {
        const boost::optional<user> ou = users_store_.find(bot_id.asString());
        if (ou) {
          const user &u = ou.get();
          load_user_icon(u.icons.image_72);
          if (username.empty()) {
            username = u.name;
          }
        } else {
          std::cerr << "[MessageRow] cannot find bot user  " << bot_id
                    << std::endl;
        }
      } else {
        const std::string default_icon_url =
            "https://i0.wp.com/slack-assets2.s3-us-west-2.amazonaws.com/8390/"
            "img/avatars/ava_0002-48.png";
        load_user_icon(default_icon_url);
      }
      user_label_.set_text(username);
    } else if (subtype == "channel_join") {
      const Json::Value inviter_value = payload["inviter"];
      if (inviter_value.isString()) {
        const boost::optional<user> o_inviter =
            users_store_.find(inviter_value.asString());
        if (o_inviter) {
          const user &inviter = o_inviter.get();
          text.append(" by invitation from <@")
              .append(inviter.id)
              .append("|")
              .append(inviter.name)
              .append(">");

        } else {
          std::cerr << "[MessageRow] cannot find channel_join inviter "
                    << inviter_value << std::endl;
        }
      }
    } else if (subtype == "channel_leave") {
      // nothing special
    } else if (subtype == "bot_add") {
      // nothing special
    } else if (subtype == "bot_remove") {
      // nothing special
    } else if (subtype == "file_share") {
      vbox_.pack_end(file_image_, Gtk::PACK_SHRINK);
      load_shared_file(payload);
    } else {
      std::cout << "Unhandled subtype " << subtype << ": \n"
                << payload << std::endl;
    }
  }
  if (payload["attachments"].isArray()) {
    auto attachments_view = Gtk::manage(new AttachmentsView(
        users_store, channels_store, emoji_loader, payload["attachments"]));
    vbox_.pack_end(*attachments_view);
  }
  vbox_.pack_end(message_text_view_);
  message_text_view_.set_text(text, is_message);

  show_all_children();
}

MessageRow::~MessageRow() {
}

void MessageRow::load_user_icon(const std::string &icon_url) {
  icon_loader_.load(icon_url, std::bind(&MessageRow::on_user_icon_loaded, this,
                                        std::placeholders::_1));
}

void MessageRow::on_user_icon_loaded(Glib::RefPtr<Gdk::Pixbuf> pixbuf) {
  user_image_.set(pixbuf->scale_simple(36, 36, Gdk::INTERP_BILINEAR));
}

void MessageRow::load_shared_file(const Json::Value &payload) {
  // TODO: Animated GIF support
  const Json::Value thumb_url = payload["file"]["thumb_360"];
  if (thumb_url.isString()) {
    api_client_.get_shared_file(thumb_url.asString(),
                                std::bind(&MessageRow::on_shared_file_loaded,
                                          this, std::placeholders::_1));
  } else {
    std::cerr << "[MessageRow] Invalid file_share payload " << payload
              << std::endl;
  }
}

void MessageRow::on_shared_file_loaded(Glib::RefPtr<Gdk::Pixbuf> pixbuf) {
  file_image_.set(pixbuf);
}

sigc::signal<void, const std::string &>
MessageRow::signal_channel_link_clicked() {
  return message_text_view_.signal_channel_link_clicked();
}

sigc::signal<void, const std::string &> MessageRow::signal_user_link_clicked() {
  return message_text_view_.signal_user_link_clicked();
}

std::string MessageRow::summary_for_notification() const {
  return user_label_.get_text() + ": " + message_text_view_.get_text();
}
<|endoftext|>


<commit_before>#include <common/defaults.h>
#include <common/list.h>
#include <common/map.h>
#include <common/log.h>
#include <common/string.h>
#include <common/dynarray.h>
#include <common/linemap.h>
#include <gui/gui.h>
#include <gdb/gdb.h>
#include <gdb/result.h>
#include <gdb/memory.h>
#include <gdb/variable.h>
#include <user_cmd/cmd.h>
#include <user_cmd/subcmd.hash.h>
#include <user_cmd/pertypes.h>
#include <user_cmd/per.tab.h>
#include <user_cmd/per.lex.h>
#include <string>

using namespace std;

/* macros */
#define CTOI(c) (unsigned int)((c) - ((c) >= 'a' ? 87 : 48))
#define ALIGN(val, base) ((val) & (~(base - 1)))
#define BYTE_SWAP_COND(reg_opt, glob_prop) \
	(((reg_opt) & REG_END_LITTLE) || ((glob_prop) == END_LITTLE && !((reg_opt) & REG_END_BIG)))

/* static variables */
static char *per_file = 0;
static per_range_t *range_lst = 0;
static per_prop_t props;
static line_map line_secs;
static line_map line_regs;

/* global functions */
int cmd_per_exec(int argc, char **argv){
	int r;
	const struct user_subcmd_t *scmd;
	FILE *fp;
	per_section_t *sec;
	per_range_t *range;
	per_register_t *reg;
	per_bits_t *bits;
	vector<line_map_t>::iterator line;

	if(argc < 2){
		USER("invalid number of arguments to command \"%s\"\n", argv[0]);
		cmd_per_help(1, argv);
		return 0;
	}

	scmd = user_subcmd::lookup(argv[1], strlen(argv[1]));

	if((scmd == 0 && argc < 2) || (scmd != 0 && (((scmd->id == FOLD || scmd->id == COMPLETE || scmd->id == EXPORT) && argc < 3) || (scmd->id == SET && argc < 4)))){
		USER("invalid number of arguments to command \"%s\"\n", argv[0]);
		cmd_per_help(2, argv);
		return 0;
	}

	if(scmd == 0){
		/* de-initialise */
		cmd_per_cleanup();

		/* parser peripheral file */
		fp = fopen(argv[1], "r");

		if(fp == 0){
			USER("cannot open file \"%s\"\n", argv[1]);
			return -1;
		}

		r = perparse(fp, &range_lst, &props);
		perlex_destroy();

		fclose(fp);

		if(r != 0){
			USER("error parsing peripheral file \"%s\"\n", argv[1]);
			return -1;
		}

		/* initialise memory segments */
		list_for_each(range_lst, range){
			// read respective memory
			range->mem = gdb_memory_t::acquire(range->base, range->size);

			if(range->mem == 0){
				USER("unable to create memory segment for section \"%s\" (%p, %u)\n", sec->name, range->base, range->size);

				cmd_per_cleanup();
				cmd_per_update();

				return -1;
			}

			list_for_each(range->sections, sec){
				list_for_each(sec->regs, reg){
					if(reg->name)
						strdeescape(reg->name);

					// check if the register is a headline and not an actual register
					if(reg->nbytes == 0)
						continue;

					// check integrity of register to range
					if(reg->offset + reg->nbytes >  range->size){
						USER("error: offset/size of register \"%s\" exceeds size of range (%p)\n", reg->name, range->base);

						cmd_per_cleanup();
						cmd_per_update();

						return -1;
					}

					// check integrity of bits to register
					list_for_each(reg->bits, bits){
						if(bits->idx + bits->nbits > reg->nbytes * 8){
							USER("error: index/size of bits \"%s\" exceeds size of register \"%s\"\n", bits->name, reg->name);

							cmd_per_cleanup();
							cmd_per_update();

							return -1;
						}
					}

					// update list of registers
					reg->parent = range;
				}

				USER("add peripheral section \"%s\"\n", sec->name);
			}
		}

		delete [] per_file;
		per_file = stralloc(argv[1], strlen(argv[1]));

		cmd_per_update();
	}
	else{
		switch(scmd->id){
		case SET:
			reg = (per_register_t*)line_regs.find(atoi(argv[2]));

			if(reg == 0){
				USER("no register at line %s\n", argv[2]);
				return 0;
			}

			if(BYTE_SWAP_COND(reg->opt, props.endian))
				strswap2(argv[3], strlen(argv[3]));

			if(gdb_memory_t::set((void*)((unsigned long long)reg->parent->base + reg->offset), argv[3], reg->nbytes) != 0)
				return -1;

			gdb->memory_update();
			break;

		case FOLD:
			sec = (per_section_t*)line_secs.find(atoi(argv[2]));

			if(sec == 0){
				USER("no peripheral section at line %s\n", argv[2]);
				return 0;
			}

			sec->expanded = sec->expanded ? false : true;

			cmd_per_update();
			break;

		case COMPLETE:
			fp = fopen(argv[2], "w");

			if(fp == 0)
				return -1;

			for(line=line_secs.lines()->begin(); line!=line_secs.lines()->end(); line++)
				fprintf(fp, "%d\\n", line->line);

			fprintf(fp, "<regs>");

			for(line=line_regs.lines()->begin(); line!=line_regs.lines()->end(); line++)
				fprintf(fp, "%d\\n", line->line);

			fclose(fp);
			break;

		case EXPORT:
			fp = fopen(argv[2], "w");

			if(fp == 0)
				return 0;

			if(per_file)		fprintf(fp, "Per %s\n\n", per_file);

			fclose(fp);

			USER("export peripheral commands to \"%s\"\n", argv[2]);
			break;

		case VIEW:
			cmd_per_update();
			break;

		default:
			USER("unhandled sub command \"%s\" to \"%s\"\n", argv[1], argv[0]);
		};
	}

	return 0;
}

void cmd_per_cleanup(){
	per_range_t *range;

	delete [] per_file;
	per_file = 0;

	line_secs.clear();
	line_regs.clear();

	list_for_each(range_lst, range){
		list_rm(&range_lst, range);
		delete range;
	}
}

void cmd_per_help(int argc, char **argv){
	int i;
	const struct user_subcmd_t *scmd;

	ui->win_atomic(0, true);

	if(argc == 1){
		USER("usage: %s [sub-command] <args>...\n", argv[0]);
		USER("   sub-commands:\n");
		USER("      <file>                          set peripheral file\n");
		USER("      set <register> <value> [<cnt>]  set register\n");
		USER("      fold <line>                     fold/unfold peripheral segment\n");
		USER("      complete <file> <sync>          get list of peripheral segments and addresses\n");
		USER("      export <file> <sync>            export used peripheral file to vim script\n");
		USER("      view                            update per window\n");
		USER("\n");
	}
	else{
		for(i=1; i<argc; i++){
			scmd = user_subcmd::lookup(argv[i], strlen(argv[i]));

			if(scmd == 0){
				USER("invalid sub-command \"%s\" to command \"%s\"\n", argv[i], argv[0]);
				continue;
			}

			switch(scmd->id){
			case SET:
				USER("usage %s %s <register> <value> [<count>]\n", argv[0], argv[i]);
				USER("   set register <register> to <value>\n");
				USER("\n");
				break;

			case FOLD:
				USER("usage %s %s <line>\n", argv[0], argv[i]);
				USER("   fold/unfold peripheral segment at line <line>\n");
				USER("\n");
				break;

			case COMPLETE:
				USER("usage %s %s <file> <sync>\n", argv[0], argv[i]);
				USER("   print list of line numbers and addresses to file <file>, using file <sync> to sync with vim\n");
				USER("   both lists are separated by '<addr>'\n");
				USER("   the items of each list are '\\n' separated\n");
				USER("\n");
				break;

			case EXPORT:
				USER("usage %s %s <file> <sync>\n", argv[0], argv[1]);
				USER("   export peripheral file to vim script <file>, using file <sync> to sync with vim\n");
				USER("\n");
				break;

			case VIEW:
				break;

			default:
				USER("invalid sub-command \"%s\" to command \"%s\"\n", argv[i], argv[0]);
			};
		}
	}

	ui->win_cursor_set(ui->win_getid(USERLOG_NAME), -1);
	ui->win_atomic(0, false);
}

int cmd_per_update(){
	static dynarray obuf;
	char c;
	int win_id;
	unsigned int line, i;
	unsigned long int reg_val, bit_val;
	bool modified;
	gdb_memory_t *mem;
	per_section_t *sec;
	per_range_t *range;
	per_register_t *reg;
	per_bits_t *bits;

	win_id = ui->win_getid(PER_NAME);

	if(win_id < 0)
		return 0;

	line_secs.clear();
	line_regs.clear();
	obuf.clear();
	line = 1;

	/* generate output buffer */
	list_for_each(range_lst, range){
		// get memory content
		mem = range->mem;

		if(mem->update() != 0)
			return -1;

		list_for_each(range->sections, sec){
			// print header
			obuf.add("[%c] ´h0%s`h0\n", (sec->expanded ? '-' : '+'), sec->name);
			line_secs.add(line, sec);
			line++;

			if(!sec->expanded){
				obuf.add("\n");
				line++;
				continue;
			}

			// print register values
			list_for_each(sec->regs, reg){
				line_regs.add(line, reg);

				if(reg->nbytes == 0){
					obuf.add(" ´h1%s%s%s`h1\n", (reg->name ? reg->name : ""), (reg->desc && reg->desc[0] ? " - " : ""), (reg->desc ? reg->desc : ""));
					line++;

					continue;
				}

				modified = memcmp(mem->content + reg->offset * 2, mem->content_old + reg->offset * 2, reg->nbytes * 2);

				if(BYTE_SWAP_COND(reg->opt, props.endian))
					strswap2(mem->content + reg->offset * 2, reg->nbytes * 2);

				obuf.add("  ´h2%s%s%s`h2 = %s%.*s%s\n", reg->name, (reg->desc && reg->desc[0] ? " - " : ""), (reg->desc ? reg->desc : ""), (modified ? "´c" : ""), reg->nbytes * 2, mem->content + reg->offset * 2, (modified ? "`c" : ""));

				if(BYTE_SWAP_COND(reg->opt, props.endian))
					strswap2(mem->content + reg->offset * 2, reg->nbytes * 2);

				line++;

				// print bits
				if(reg->bits){
					c = mem->content[reg->offset * 2 + reg->nbytes * 2];
					mem->content[reg->offset * 2 + reg->nbytes * 2] = 0;

					reg_val = strtoll(mem->content + reg->offset * 2, 0, 16);
					mem->content[reg->offset * 2 + reg->nbytes * 2] = c;

					i = 0;
					list_for_each(reg->bits, bits){
						if(i && i % 4 == 0){
							obuf.add("\n");
							line++;
						}

						bit_val = (reg_val & (bits->mask << bits->idx)) >> bits->idx;

						modified = (bit_val == bits->value) ? false : true;
						bits->value = bit_val;

						obuf.add("   ´h3%s`h3 %s%.*lx%s", bits->name, (modified ? "´c" : ""), (bits->nbits + 3) / 4, bit_val, (modified ? "`c" : ""));
						i++;
					}

					obuf.add("\n\n");
					line += 2;
				}
			}

			obuf.add("\n");
			line++;
		}
	}

	/* update ui */
	ui->win_atomic(win_id, true);

	ui->win_clear(win_id);
	ui->win_print(win_id, obuf.data());

	ui->win_atomic(win_id, false);

	return 0;
}
<commit_msg>[fix per bit display error]<commit_after>#include <common/defaults.h>
#include <common/list.h>
#include <common/map.h>
#include <common/log.h>
#include <common/string.h>
#include <common/dynarray.h>
#include <common/linemap.h>
#include <gui/gui.h>
#include <gdb/gdb.h>
#include <gdb/result.h>
#include <gdb/memory.h>
#include <gdb/variable.h>
#include <user_cmd/cmd.h>
#include <user_cmd/subcmd.hash.h>
#include <user_cmd/pertypes.h>
#include <user_cmd/per.tab.h>
#include <user_cmd/per.lex.h>
#include <string>

using namespace std;

/* macros */
#define CTOI(c) (unsigned int)((c) - ((c) >= 'a' ? 87 : 48))
#define ALIGN(val, base) ((val) & (~(base - 1)))
#define BYTE_SWAP_COND(reg_opt, glob_prop) \
	(((reg_opt) & REG_END_LITTLE) || ((glob_prop) == END_LITTLE && !((reg_opt) & REG_END_BIG)))

/* static variables */
static char *per_file = 0;
static per_range_t *range_lst = 0;
static per_prop_t props;
static line_map line_secs;
static line_map line_regs;

/* global functions */
int cmd_per_exec(int argc, char **argv){
	int r;
	const struct user_subcmd_t *scmd;
	FILE *fp;
	per_section_t *sec;
	per_range_t *range;
	per_register_t *reg;
	per_bits_t *bits;
	vector<line_map_t>::iterator line;

	if(argc < 2){
		USER("invalid number of arguments to command \"%s\"\n", argv[0]);
		cmd_per_help(1, argv);
		return 0;
	}

	scmd = user_subcmd::lookup(argv[1], strlen(argv[1]));

	if((scmd == 0 && argc < 2) || (scmd != 0 && (((scmd->id == FOLD || scmd->id == COMPLETE || scmd->id == EXPORT) && argc < 3) || (scmd->id == SET && argc < 4)))){
		USER("invalid number of arguments to command \"%s\"\n", argv[0]);
		cmd_per_help(2, argv);
		return 0;
	}

	if(scmd == 0){
		/* de-initialise */
		cmd_per_cleanup();

		/* parser peripheral file */
		fp = fopen(argv[1], "r");

		if(fp == 0){
			USER("cannot open file \"%s\"\n", argv[1]);
			return -1;
		}

		r = perparse(fp, &range_lst, &props);
		perlex_destroy();

		fclose(fp);

		if(r != 0){
			USER("error parsing peripheral file \"%s\"\n", argv[1]);
			return -1;
		}

		/* initialise memory segments */
		list_for_each(range_lst, range){
			// read respective memory
			range->mem = gdb_memory_t::acquire(range->base, range->size);

			if(range->mem == 0){
				USER("unable to create memory segment for section \"%s\" (%p, %u)\n", sec->name, range->base, range->size);

				cmd_per_cleanup();
				cmd_per_update();

				return -1;
			}

			list_for_each(range->sections, sec){
				list_for_each(sec->regs, reg){
					if(reg->name)
						strdeescape(reg->name);

					// check if the register is a headline and not an actual register
					if(reg->nbytes == 0)
						continue;

					// check integrity of register to range
					if(reg->offset + reg->nbytes >  range->size){
						USER("error: offset/size of register \"%s\" exceeds size of range (%p)\n", reg->name, range->base);

						cmd_per_cleanup();
						cmd_per_update();

						return -1;
					}

					// check integrity of bits to register
					list_for_each(reg->bits, bits){
						if(bits->idx + bits->nbits > reg->nbytes * 8){
							USER("error: index/size of bits \"%s\" exceeds size of register \"%s\"\n", bits->name, reg->name);

							cmd_per_cleanup();
							cmd_per_update();

							return -1;
						}
					}

					// update list of registers
					reg->parent = range;
				}

				USER("add peripheral section \"%s\"\n", sec->name);
			}
		}

		delete [] per_file;
		per_file = stralloc(argv[1], strlen(argv[1]));

		cmd_per_update();
	}
	else{
		switch(scmd->id){
		case SET:
			reg = (per_register_t*)line_regs.find(atoi(argv[2]));

			if(reg == 0){
				USER("no register at line %s\n", argv[2]);
				return 0;
			}

			if(BYTE_SWAP_COND(reg->opt, props.endian))
				strswap2(argv[3], strlen(argv[3]));

			if(gdb_memory_t::set((void*)((unsigned long long)reg->parent->base + reg->offset), argv[3], reg->nbytes) != 0)
				return -1;

			gdb->memory_update();
			break;

		case FOLD:
			sec = (per_section_t*)line_secs.find(atoi(argv[2]));

			if(sec == 0){
				USER("no peripheral section at line %s\n", argv[2]);
				return 0;
			}

			sec->expanded = sec->expanded ? false : true;

			cmd_per_update();
			break;

		case COMPLETE:
			fp = fopen(argv[2], "w");

			if(fp == 0)
				return -1;

			for(line=line_secs.lines()->begin(); line!=line_secs.lines()->end(); line++)
				fprintf(fp, "%d\\n", line->line);

			fprintf(fp, "<regs>");

			for(line=line_regs.lines()->begin(); line!=line_regs.lines()->end(); line++)
				fprintf(fp, "%d\\n", line->line);

			fclose(fp);
			break;

		case EXPORT:
			fp = fopen(argv[2], "w");

			if(fp == 0)
				return 0;

			if(per_file)		fprintf(fp, "Per %s\n\n", per_file);

			fclose(fp);

			USER("export peripheral commands to \"%s\"\n", argv[2]);
			break;

		case VIEW:
			cmd_per_update();
			break;

		default:
			USER("unhandled sub command \"%s\" to \"%s\"\n", argv[1], argv[0]);
		};
	}

	return 0;
}

void cmd_per_cleanup(){
	per_range_t *range;

	delete [] per_file;
	per_file = 0;

	line_secs.clear();
	line_regs.clear();

	list_for_each(range_lst, range){
		list_rm(&range_lst, range);
		delete range;
	}
}

void cmd_per_help(int argc, char **argv){
	int i;
	const struct user_subcmd_t *scmd;

	ui->win_atomic(0, true);

	if(argc == 1){
		USER("usage: %s [sub-command] <args>...\n", argv[0]);
		USER("   sub-commands:\n");
		USER("      <file>                          set peripheral file\n");
		USER("      set <register> <value> [<cnt>]  set register\n");
		USER("      fold <line>                     fold/unfold peripheral segment\n");
		USER("      complete <file> <sync>          get list of peripheral segments and addresses\n");
		USER("      export <file> <sync>            export used peripheral file to vim script\n");
		USER("      view                            update per window\n");
		USER("\n");
	}
	else{
		for(i=1; i<argc; i++){
			scmd = user_subcmd::lookup(argv[i], strlen(argv[i]));

			if(scmd == 0){
				USER("invalid sub-command \"%s\" to command \"%s\"\n", argv[i], argv[0]);
				continue;
			}

			switch(scmd->id){
			case SET:
				USER("usage %s %s <register> <value> [<count>]\n", argv[0], argv[i]);
				USER("   set register <register> to <value>\n");
				USER("\n");
				break;

			case FOLD:
				USER("usage %s %s <line>\n", argv[0], argv[i]);
				USER("   fold/unfold peripheral segment at line <line>\n");
				USER("\n");
				break;

			case COMPLETE:
				USER("usage %s %s <file> <sync>\n", argv[0], argv[i]);
				USER("   print list of line numbers and addresses to file <file>, using file <sync> to sync with vim\n");
				USER("   both lists are separated by '<addr>'\n");
				USER("   the items of each list are '\\n' separated\n");
				USER("\n");
				break;

			case EXPORT:
				USER("usage %s %s <file> <sync>\n", argv[0], argv[1]);
				USER("   export peripheral file to vim script <file>, using file <sync> to sync with vim\n");
				USER("\n");
				break;

			case VIEW:
				break;

			default:
				USER("invalid sub-command \"%s\" to command \"%s\"\n", argv[i], argv[0]);
			};
		}
	}

	ui->win_cursor_set(ui->win_getid(USERLOG_NAME), -1);
	ui->win_atomic(0, false);
}

int cmd_per_update(){
	static dynarray obuf;
	char c;
	int win_id;
	unsigned int line, i;
	unsigned long int reg_val, bit_val;
	bool modified;
	gdb_memory_t *mem;
	per_section_t *sec;
	per_range_t *range;
	per_register_t *reg;
	per_bits_t *bits;

	win_id = ui->win_getid(PER_NAME);

	if(win_id < 0)
		return 0;

	line_secs.clear();
	line_regs.clear();
	obuf.clear();
	line = 1;

	/* generate output buffer */
	list_for_each(range_lst, range){
		// get memory content
		mem = range->mem;

		if(mem->update() != 0)
			return -1;

		list_for_each(range->sections, sec){
			// print header
			obuf.add("[%c] ´h0%s`h0\n", (sec->expanded ? '-' : '+'), sec->name);
			line_secs.add(line, sec);
			line++;

			if(!sec->expanded){
				obuf.add("\n");
				line++;
				continue;
			}

			// print register values
			list_for_each(sec->regs, reg){
				line_regs.add(line, reg);

				if(reg->nbytes == 0){
					obuf.add(" ´h1%s%s%s`h1\n", (reg->name ? reg->name : ""), (reg->desc && reg->desc[0] ? " - " : ""), (reg->desc ? reg->desc : ""));
					line++;

					continue;
				}

				modified = memcmp(mem->content + reg->offset * 2, mem->content_old + reg->offset * 2, reg->nbytes * 2);

				if(BYTE_SWAP_COND(reg->opt, props.endian))
					strswap2(mem->content + reg->offset * 2, reg->nbytes * 2);

				obuf.add("  ´h2%s%s%s`h2 = %s%.*s%s\n", reg->name, (reg->desc && reg->desc[0] ? " - " : ""), (reg->desc ? reg->desc : ""), (modified ? "´c" : ""), reg->nbytes * 2, mem->content + reg->offset * 2, (modified ? "`c" : ""));
				line++;

				// print bits
				if(reg->bits){
					c = mem->content[reg->offset * 2 + reg->nbytes * 2];
					mem->content[reg->offset * 2 + reg->nbytes * 2] = 0;

					reg_val = strtoll(mem->content + reg->offset * 2, 0, 16);
					mem->content[reg->offset * 2 + reg->nbytes * 2] = c;

					i = 0;
					list_for_each(reg->bits, bits){
						if(i && i % 4 == 0){
							obuf.add("\n");
							line++;
						}

						bit_val = (reg_val & (bits->mask << bits->idx)) >> bits->idx;

						modified = (bit_val == bits->value) ? false : true;
						bits->value = bit_val;

						obuf.add("   ´h3%s`h3 %s%.*lx%s", bits->name, (modified ? "´c" : ""), (bits->nbits + 3) / 4, bit_val, (modified ? "`c" : ""));
						i++;
					}

					obuf.add("\n\n");
					line += 2;
				}

				if(BYTE_SWAP_COND(reg->opt, props.endian))
					strswap2(mem->content + reg->offset * 2, reg->nbytes * 2);
			}

			obuf.add("\n");
			line++;
		}
	}

	/* update ui */
	ui->win_atomic(win_id, true);

	ui->win_clear(win_id);
	ui->win_print(win_id, obuf.data());

	ui->win_atomic(win_id, false);

	return 0;
}
<|endoftext|>


<commit_before>/**
 * \file delta.cpp
 * \brief Implementations of descritized delta functions.
 * \copyright Copyright (c) 2016-2018, Barba group. All rights reserved.
 * \license BSD 3-Clause License.
 */

#include <cmath>

#include <petibm/delta.h>

namespace petibm
{
namespace delta
{
// Regularized delta function from Roma et al. (1999).
PetscReal Roma_et_al_1999(const PetscReal &r, const PetscReal &dr)
{
    PetscReal x = std::abs(r) / dr;

    if (x > 1.5) return 0.0;

    if (x > 0.5 && x <= 1.5)
        return (5 - 3 * x - std::sqrt(-3 * (1 - x) * (1 - x) + 1)) / (6 * dr);

    return (1 + std::sqrt(-3 * x * x + 1)) / (3 * dr);
}  // Roma_et_al_1999

// Regularized delta function Peskin (2002).
PetscReal Peskin_2002(const PetscReal &r, const PetscReal &dr)
{
    PetscReal x = r / dr;

    if (x >= -2.0 && x <= -1.0)
        return (5 + 2 * x - std::sqrt(-7 - 12 * x - 4 * x * x)) / (8 * dr);
    else if (x >= -1.0 && x <= 0.0)
        return (3 + 2 * x + std::sqrt(1 - 4 * x - 4 * x * x)) / (8 * dr);
    else if (x >= 0.0 && x <= 1.0)
        return (3 - 2 * x + std::sqrt(1 + 4 * x - 4 * x * x)) / (8 * dr);
    else if (x >= 1.0 && x <= 2.0)
        return (5 - 2 * x - std::sqrt(-7 + 12 * x - 4 * x * x)) / (8 * dr);
    else
        return 0.0;
}  // Peskin_2002

// Get the delta kernel and size to use
PetscErrorCode getKernel(const std::string &name,
                         DeltaKernel &kernel, PetscInt &size)
{
    PetscFunctionBeginUser;

    if (name == "ROMA_ET_AL_1999")
    {
        kernel = Roma_et_al_1999;
        size = 2;
    }
    else if (name == "PESKIN_2002")
    {
        kernel = Peskin_2002;
        size = 3;
    }
    else
        SETERRQ1(PETSC_COMM_WORLD, PETSC_ERR_ARG_UNKNOWN_TYPE,
                 "No support for delta kernel `%s`.\n", name.c_str());

    PetscFunctionReturn(0);
}  // getKernel

// Discrete delta function.
PetscReal delta(const std::vector<PetscReal> &source,
                const std::vector<PetscReal> &target,
                const std::vector<PetscReal> &widths,
                const DeltaKernel &kernel)
{
    PetscReal phi = 1.0;
    for (unsigned int d = 0; d < widths.size(); ++d)
        phi *= kernel(source[d] - target[d], widths[d]);
    return phi;
}  // delta

}  // end of namespace delta
}  // end of namespace petibm
<commit_msg>Simplify code because Peskin's delta is symmetric<commit_after>/**
 * \file delta.cpp
 * \brief Implementations of descritized delta functions.
 * \copyright Copyright (c) 2016-2018, Barba group. All rights reserved.
 * \license BSD 3-Clause License.
 */

#include <cmath>

#include <petibm/delta.h>

namespace petibm
{
namespace delta
{
// Regularized delta function from Roma et al. (1999).
PetscReal Roma_et_al_1999(const PetscReal &r, const PetscReal &dr)
{
    PetscReal x = std::abs(r) / dr;

    if (x > 1.5) return 0.0;

    if (x > 0.5 && x <= 1.5)
        return (5 - 3 * x - std::sqrt(-3 * (1 - x) * (1 - x) + 1)) / (6 * dr);

    return (1 + std::sqrt(-3 * x * x + 1)) / (3 * dr);
}  // Roma_et_al_1999

// Regularized delta function Peskin (2002).
PetscReal Peskin_2002(const PetscReal &r, const PetscReal &dr)
{
    PetscReal x = std::abs(r) / dr;

    if (x >= 0.0 && x <= 1.0)
        return (3 - 2 * x + std::sqrt(1 + 4 * x - 4 * x * x)) / (8 * dr);
    else if (x >= 1.0 && x <= 2.0)
        return (5 - 2 * x - std::sqrt(-7 + 12 * x - 4 * x * x)) / (8 * dr);
    return 0.0;
}  // Peskin_2002

// Get the delta kernel and size to use
PetscErrorCode getKernel(const std::string &name,
                         DeltaKernel &kernel, PetscInt &size)
{
    PetscFunctionBeginUser;

    if (name == "ROMA_ET_AL_1999")
    {
        kernel = Roma_et_al_1999;
        size = 2;
    }
    else if (name == "PESKIN_2002")
    {
        kernel = Peskin_2002;
        size = 3;
    }
    else
        SETERRQ1(PETSC_COMM_WORLD, PETSC_ERR_ARG_UNKNOWN_TYPE,
                 "No support for delta kernel `%s`.\n", name.c_str());

    PetscFunctionReturn(0);
}  // getKernel

// Discrete delta function.
PetscReal delta(const std::vector<PetscReal> &source,
                const std::vector<PetscReal> &target,
                const std::vector<PetscReal> &widths,
                const DeltaKernel &kernel)
{
    PetscReal phi = 1.0;
    for (unsigned int d = 0; d < widths.size(); ++d)
        phi *= kernel(source[d] - target[d], widths[d]);
    return phi;
}  // delta

}  // end of namespace delta
}  // end of namespace petibm
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2013-2016.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://www.opensource.org/licenses/MIT)
//=======================================================================

#ifndef SCHEDULER_H
#define SCHEDULER_H

#include <vector.hpp>
#include <string.hpp>
#include <expected.hpp>

#include "process.hpp"
#include "net/socket.hpp"

#include "vfs/path.hpp"

namespace scheduler {

constexpr const size_t MAX_PROCESS = 128;

/*!
 * \brief Return the id of the current process
 */
pid_t get_pid();

/*!
 * \brief Return the process with the given ID
 */
scheduler::process_t& get_process(pid_t pid);

/*!
 * \brief Returns the state of the process with the given ID
 */
scheduler::process_state get_process_state(pid_t pid);

/*!
 * \brief Block the given process and immediately reschedule it
 */
void block_process(pid_t pid);

/*!
 * \brief Unblock a blocked process
 */
void unblock_process(pid_t pid);

/*!
 * \brief Unblock a process if it is a blocked
 */
void unblock_process_hint(pid_t pid);

/*!
 * \brief Init the scheduler
 */
void init();

/*!
 * \brief Start the scheduler and starts the first process
 */
void start() __attribute__((noreturn));

/*!
 * \brief Indicates if the scheduler is started or not
 */
bool is_started();

/*!
 * \brief Execute the given file in a new process
 */
std::expected<pid_t> exec(const std::string& path, const std::vector<std::string>& params);

/*!
 * \brief Kill the current process
 */
void kill_current_process() __attribute__((noreturn));

/*!
 * \brief Wait for the given process to terminate
 */
void await_termination(pid_t pid);

/*!
 * \brief Allocate more memory for the process
 * \param inc The amount of memory to add
 */
void sbrk(size_t inc);

/*!
 * \brief Let the scheduler know of a timer tick
 */
void tick();

/*!
 * \brief Let another process run.
 */
void yield();

/*!
 * \brief Rescedule another process, if this one is not running
 */
void reschedule();

/*!
 * \brief Indicates a fault in the current process
 */
void fault();

/*!
 * \brief Make the current process sleep for the given amount of milliseconds
 * \param time The number of milliseconds to wait
 */
void sleep_ms(size_t time);

/*!
 * \brief Make the given process sleep for the given amount of milliseconds
 * \param time The number of milliseconds to wait
 */
void sleep_ms(pid_t pid, size_t time);

/*!
 * \brief Register a new handle (file descriptor) for the current process
 */
size_t register_new_handle(const path& p);

/*!
 * \brief Get the path of the given file descriptor
 */
const path& get_handle(size_t fd);

/*!
 * \brief Indicates if the current process has the given file descriptor
 */
bool has_handle(size_t fd);

/*!
 * \brief Release the given handle from the current process
 */
void release_handle(size_t fd);

/*!
 * \brief Register a new socket for the current process
 * \param domain The socket domain
 * \param type The socket type
 * \param protocol The socket protocol
 */
size_t register_new_socket(network::socket_domain domain, network::socket_type type, network::socket_protocol protocol);

/*!
 * \brief Get the socket of the given file descriptor
 */
network::socket& get_socket(size_t fd);

/*!
 * \brief Indicates if the current process has the given socket
 */
bool has_socket(size_t fd);

/*!
 * \brief Release the given socket from the current process
 */
void release_socket(size_t fd);

/*!
 * \brief Returns the sockets of the current process
 */
std::deque<network::socket>& get_sockets();

/*!
 * \brief Returns the sockets of the given process
 */
std::deque<network::socket>& get_sockets(pid_t pid);

/*!
 * \brief Returns the working directory of the current process
 */
const path& get_working_directory();

/*!
 * \brief Changes the working directory of the current process
 */
void set_working_directory(const path& directory);

/*!
 * \brief Block the given process, but do not reschedule
 */
void block_process_light(pid_t pid);

/*!
 * \brief Block the given process, with a possible timeout, but do not reschedule
 */
void block_process_timeout_light(pid_t pid, size_t ms);

/*!
 * \brief Creat a kernel process
 * \param name The name of the process
 * \param user_stack Pointer to the user stack
 * \param kernel_stack Pointer to the kernel stack
 * \param fun The function to execute
 */
process_t& create_kernel_task(const char* name, char* user_stack, char* kernel_stack, void (*fun)());

/*!
 * \brief Creat a kernel process with some data
 * \param name The name of the process
 * \param user_stack Pointer to the user stack
 * \param kernel_stack Pointer to the kernel stack
 * \param fun The function to execute
 * \param data The data to pass to the function
 */
process_t& create_kernel_task_args(const char* name, char* user_stack, char* kernel_stack, void (*fun)(void*), void* data);

/*!
 * \brief Queue a created system process
 */
void queue_system_process(pid_t pid);

/*!
 * \brief Queue an initilization task that will be run after the
 * scheduler is started
 *
 * This must be used for drivers that needs scheduling to be started
 * or for drivers depending on others drivers asynchronously
 * started.
 */
void queue_async_init_task(void (*fun)());

/*!
 * \brief Lets the scheduler know that the timer frequency has been updated
 */
void frequency_updated(uint64_t old_frequency, uint64_t new_frequency);

} //end of namespace scheduler

#endif
<commit_msg>Improve documentation<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2013-2016.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://www.opensource.org/licenses/MIT)
//=======================================================================

#ifndef SCHEDULER_H
#define SCHEDULER_H

#include <vector.hpp>
#include <string.hpp>
#include <expected.hpp>

#include "process.hpp"
#include "net/socket.hpp"

#include "vfs/path.hpp"

namespace scheduler {

constexpr const size_t MAX_PROCESS = 128;

/*!
 * \brief Return the id of the current process
 */
pid_t get_pid();

/*!
 * \brief Return the process with the given ID
 */
scheduler::process_t& get_process(pid_t pid);

/*!
 * \brief Returns the state of the process with the given ID
 */
scheduler::process_state get_process_state(pid_t pid);

/*!
 * \brief Block the given process and immediately reschedule it
 */
void block_process(pid_t pid);

/*!
 * \brief Unblock a blocked process
 */
void unblock_process(pid_t pid);

/*!
 * \brief Unblock a process if it is a blocked
 */
void unblock_process_hint(pid_t pid);

/*!
 * \brief Init the scheduler
 */
void init();

/*!
 * \brief Start the scheduler and starts the first process
 */
void start() __attribute__((noreturn));

/*!
 * \brief Indicates if the scheduler is started or not
 */
bool is_started();

/*!
 * \brief Execute the given file in a new process
 */
std::expected<pid_t> exec(const std::string& path, const std::vector<std::string>& params);

/*!
 * \brief Kill the current process
 */
void kill_current_process() __attribute__((noreturn));

/*!
 * \brief Wait for the given process to terminate
 */
void await_termination(pid_t pid);

/*!
 * \brief Allocate more memory for the process
 * \param inc The amount of memory to add
 */
void sbrk(size_t inc);

/*!
 * \brief Let the scheduler know of a timer tick
 */
void tick();

/*!
 * \brief Let another process run.
 *
 * This may change the state of the current process state
 */
void yield();

/*!
 * \brief Reschedule to another process, if the current process is not running
 *
 * This will not change the state of the process!
 */
void reschedule();

/*!
 * \brief Indicates a fault in the current process
 */
void fault();

/*!
 * \brief Make the current process sleep for the given amount of milliseconds
 * \param time The number of milliseconds to wait
 */
void sleep_ms(size_t time);

/*!
 * \brief Make the given process sleep for the given amount of milliseconds
 * \param time The number of milliseconds to wait
 */
void sleep_ms(pid_t pid, size_t time);

/*!
 * \brief Register a new handle (file descriptor) for the current process
 */
size_t register_new_handle(const path& p);

/*!
 * \brief Get the path of the given file descriptor
 */
const path& get_handle(size_t fd);

/*!
 * \brief Indicates if the current process has the given file descriptor
 */
bool has_handle(size_t fd);

/*!
 * \brief Release the given handle from the current process
 */
void release_handle(size_t fd);

/*!
 * \brief Register a new socket for the current process
 * \param domain The socket domain
 * \param type The socket type
 * \param protocol The socket protocol
 */
size_t register_new_socket(network::socket_domain domain, network::socket_type type, network::socket_protocol protocol);

/*!
 * \brief Get the socket of the given file descriptor
 */
network::socket& get_socket(size_t fd);

/*!
 * \brief Indicates if the current process has the given socket
 */
bool has_socket(size_t fd);

/*!
 * \brief Release the given socket from the current process
 */
void release_socket(size_t fd);

/*!
 * \brief Returns the sockets of the current process
 */
std::deque<network::socket>& get_sockets();

/*!
 * \brief Returns the sockets of the given process
 */
std::deque<network::socket>& get_sockets(pid_t pid);

/*!
 * \brief Returns the working directory of the current process
 */
const path& get_working_directory();

/*!
 * \brief Changes the working directory of the current process
 */
void set_working_directory(const path& directory);

/*!
 * \brief Block the given process, but do not reschedule
 */
void block_process_light(pid_t pid);

/*!
 * \brief Block the given process, with a possible timeout, but do not reschedule
 */
void block_process_timeout_light(pid_t pid, size_t ms);

/*!
 * \brief Creat a kernel process
 * \param name The name of the process
 * \param user_stack Pointer to the user stack
 * \param kernel_stack Pointer to the kernel stack
 * \param fun The function to execute
 */
process_t& create_kernel_task(const char* name, char* user_stack, char* kernel_stack, void (*fun)());

/*!
 * \brief Creat a kernel process with some data
 * \param name The name of the process
 * \param user_stack Pointer to the user stack
 * \param kernel_stack Pointer to the kernel stack
 * \param fun The function to execute
 * \param data The data to pass to the function
 */
process_t& create_kernel_task_args(const char* name, char* user_stack, char* kernel_stack, void (*fun)(void*), void* data);

/*!
 * \brief Queue a created system process
 */
void queue_system_process(pid_t pid);

/*!
 * \brief Queue an initilization task that will be run after the
 * scheduler is started
 *
 * This must be used for drivers that needs scheduling to be started
 * or for drivers depending on others drivers asynchronously
 * started.
 */
void queue_async_init_task(void (*fun)());

/*!
 * \brief Lets the scheduler know that the timer frequency has been updated
 */
void frequency_updated(uint64_t old_frequency, uint64_t new_frequency);

} //end of namespace scheduler

#endif
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2013-2016.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://www.opensource.org/licenses/MIT)
//=======================================================================

#include <bit_field.hpp>
#include <atomic.hpp>
#include <list.hpp>
#include <semaphore.hpp>

#include "net/tcp_layer.hpp"
#include "net/dns_layer.hpp"
#include "net/checksum.hpp"

#include "kernel_utils.hpp"
#include "circular_buffer.hpp"

namespace {

using flag_data_offset = std::bit_field<uint16_t, uint8_t, 12, 4>;
using flag_reserved    = std::bit_field<uint16_t, uint8_t, 9, 3>;
using flag_ns          = std::bit_field<uint16_t, uint8_t, 8, 1>;
using flag_cwr         = std::bit_field<uint16_t, uint8_t, 7, 1>;
using flag_ece         = std::bit_field<uint16_t, uint8_t, 6, 1>;
using flag_urg         = std::bit_field<uint16_t, uint8_t, 5, 1>;
using flag_ack         = std::bit_field<uint16_t, uint8_t, 4, 1>;
using flag_psh         = std::bit_field<uint16_t, uint8_t, 3, 1>;
using flag_rst         = std::bit_field<uint16_t, uint8_t, 2, 1>;
using flag_syn         = std::bit_field<uint16_t, uint8_t, 1, 1>;
using flag_fin         = std::bit_field<uint16_t, uint8_t, 0, 1>;

struct tcp_listener {
    size_t source_port;
    size_t target_port;
    std::atomic<bool> active;
    semaphore sem;
    circular_buffer<network::ethernet::packet, 8> packets;

    tcp_listener(size_t source_port, size_t target_port)
            : source_port(source_port), target_port(target_port), active(false) {
        sem.init(0);
    }
};

// Note: We need a list to not invalidate the values during insertions
std::list<tcp_listener> listeners;

void compute_checksum(network::ethernet::packet& packet){
    auto* ip_header  = reinterpret_cast<network::ip::header*>(packet.payload + packet.tag(1));
    auto* tcp_header = reinterpret_cast<network::tcp::header*>(packet.payload + packet.index);

    tcp_header->checksum = 0;

    // Accumulate the Payload
    auto sum = network::checksum_add_bytes(packet.payload + packet.index, 20); // TODO What is the length

    // Accumulate the IP addresses
    sum += network::checksum_add_bytes(&ip_header->source_ip, 8);

    // Accumulate the IP Protocol
    sum += ip_header->protocol;

    // Accumulate the UDP length
    sum += 20;

    // Complete the 1-complement sum
    tcp_header->checksum = switch_endian_16(network::checksum_finalize_nz(sum));
}

uint16_t get_default_flags(){
    uint16_t flags = 0; // By default

    (flag_data_offset(&flags)) = 5; // No options

    //TODO

    return flags;
}

void prepare_packet(network::ethernet::packet& packet, size_t source, size_t target, size_t payload_size){
    packet.tag(2, packet.index);

    // Set the TCP header

    auto* tcp_header = reinterpret_cast<network::tcp::header*>(packet.payload + packet.index);

    tcp_header->source_port = switch_endian_16(source);
    tcp_header->target_port = switch_endian_16(target);
    tcp_header->window_size = 1024;

    packet.index += sizeof(network::tcp::header);

    //TODO
}

} //end of anonymous namespace

void network::tcp::decode(network::interface_descriptor& /*interface*/, network::ethernet::packet& packet){
    packet.tag(2, packet.index);

    auto* tcp_header = reinterpret_cast<header*>(packet.payload + packet.index);

    logging::logf(logging::log_level::TRACE, "tcp: Start TCP packet handling\n");

    auto source_port = switch_endian_16(tcp_header->source_port);
    auto target_port = switch_endian_16(tcp_header->target_port);
    auto sequence    = switch_endian_32(tcp_header->sequence_number);
    auto ack         = switch_endian_32(tcp_header->ack_number);

    logging::logf(logging::log_level::TRACE, "tcp: Source Port %u \n", size_t(source_port));
    logging::logf(logging::log_level::TRACE, "tcp: Target Port %u \n", size_t(target_port));
    logging::logf(logging::log_level::TRACE, "tcp: Seq Number %u \n", size_t(tcp_header->sequence_number));
    logging::logf(logging::log_level::TRACE, "tcp: Ack Number %u \n", size_t(tcp_header->ack_number));
    logging::logf(logging::log_level::TRACE, "tcp: Seq Number %u \n", size_t(sequence));
    logging::logf(logging::log_level::TRACE, "tcp: Ack Number %u \n", size_t(ack));

    // Propagate to kernel listeners

    auto end = listeners.end();
    auto it = listeners.begin();

    while(it != end){
        auto& listener = *it;

        if(listener.active.load() && listener.source_port == source_port && listener.target_port == target_port){
            auto copy    = packet;
            copy.payload = new char[copy.payload_size];
            std::copy_n(packet.payload, packet.payload_size, copy.payload);

            listener.packets.push(copy);
            listener.sem.release();
        }

        ++it;
    }

    packet.index += sizeof(header);

    //TODO
}

std::expected<network::ethernet::packet> network::tcp::prepare_packet(network::interface_descriptor& interface, network::ip::address target_ip, size_t source, size_t target, size_t payload_size){
    // Ask the IP layer to craft a packet
    auto packet = network::ip::prepare_packet(interface, sizeof(header) + payload_size, target_ip, 0x06);

    if(packet){
        ::prepare_packet(*packet, source, target, payload_size);
    }

    return packet;
}

std::expected<network::ethernet::packet> network::tcp::prepare_packet(char* buffer, network::interface_descriptor& interface, network::ip::address target_ip, size_t source, size_t target, size_t payload_size){
    // Ask the IP layer to craft a packet
    auto packet = network::ip::prepare_packet(buffer, interface, sizeof(header) + payload_size, target_ip, 0x06);

    if(packet){
        ::prepare_packet(*packet, source, target, payload_size);
    }

    return packet;
}

void network::tcp::finalize_packet(network::interface_descriptor& interface, network::ethernet::packet& p){
    p.index -= sizeof(header);

    // Compute the checksum
    compute_checksum(p);

    // Give the packet to the IP layer for finalization
    network::ip::finalize_packet(interface, p);
}

std::expected<void> network::tcp::connect(network::interface_descriptor& interface, network::ip::address target_ip, size_t source, size_t target){
    auto packet = tcp::prepare_packet(interface, target_ip, source, target, 0);

    if(!packet){
        return std::make_unexpected<void>(packet.error());
    }

    auto* tcp_header = reinterpret_cast<header*>(packet->payload + packet->tag(2));

    tcp_header->sequence_number = 0;
    tcp_header->ack_number      = 0;

    auto flags = get_default_flags();
    (flag_syn(&flags)) = 1;
    tcp_header->flags = switch_endian_16(flags);

    // Create the listener

    auto& listener = listeners.emplace_back(target, source);

    listener.active = true;

    logging::logf(logging::log_level::TRACE, "tcp: Send SYN\n");
    tcp::finalize_packet(interface, *packet);

    uint32_t seq;
    uint32_t ack;

    while(true){
        // TODO Need a timeout
        listener.sem.acquire();
        auto received_packet = listener.packets.pop();

        auto* tcp_header = reinterpret_cast<header*>(received_packet.payload + received_packet.index);
        auto flags = switch_endian_16(tcp_header->flags);

        logging::logf(logging::log_level::TRACE, "tcp: Received answer\n");

        if(*flag_syn(&flags) && *flag_ack(&flags)){
            logging::logf(logging::log_level::TRACE, "tcp: Received SYN/ACK\n");

            seq = switch_endian_32(tcp_header->sequence_number);
            ack = switch_endian_32(tcp_header->ack_number);

            //TODO Release packet
            break;
        }

        //TODO Release packet
    }

    listener.active = false;

    // At this point we have received the SYN/ACK, only remains to ACK

    {
        auto packet = tcp::prepare_packet(interface, target_ip, source, target, 0);

        if (!packet) {
            return std::make_unexpected<void>(packet.error());
        }

        auto* tcp_header = reinterpret_cast<header*>(packet->payload + packet->tag(2));

        tcp_header->sequence_number = switch_endian_32(ack);
        tcp_header->ack_number      = switch_endian_32(seq + 1);

        auto flags = get_default_flags();
        (flag_ack(&flags)) = 1;
        tcp_header->flags = switch_endian_16(flags);

        logging::logf(logging::log_level::TRACE, "tcp: Send ACK\n");
        tcp::finalize_packet(interface, *packet);
    }

    //TODO Remove the listener

    return {};
}
<commit_msg>Finalize<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2013-2016.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://www.opensource.org/licenses/MIT)
//=======================================================================

#include <bit_field.hpp>
#include <atomic.hpp>
#include <list.hpp>
#include <semaphore.hpp>

#include "net/tcp_layer.hpp"
#include "net/dns_layer.hpp"
#include "net/checksum.hpp"

#include "kernel_utils.hpp"
#include "circular_buffer.hpp"

namespace {

using flag_data_offset = std::bit_field<uint16_t, uint8_t, 12, 4>;
using flag_reserved    = std::bit_field<uint16_t, uint8_t, 9, 3>;
using flag_ns          = std::bit_field<uint16_t, uint8_t, 8, 1>;
using flag_cwr         = std::bit_field<uint16_t, uint8_t, 7, 1>;
using flag_ece         = std::bit_field<uint16_t, uint8_t, 6, 1>;
using flag_urg         = std::bit_field<uint16_t, uint8_t, 5, 1>;
using flag_ack         = std::bit_field<uint16_t, uint8_t, 4, 1>;
using flag_psh         = std::bit_field<uint16_t, uint8_t, 3, 1>;
using flag_rst         = std::bit_field<uint16_t, uint8_t, 2, 1>;
using flag_syn         = std::bit_field<uint16_t, uint8_t, 1, 1>;
using flag_fin         = std::bit_field<uint16_t, uint8_t, 0, 1>;

struct tcp_listener {
    size_t source_port;
    size_t target_port;
    std::atomic<bool> active;
    //TODO Use a sleep queue here
    semaphore sem;
    circular_buffer<network::ethernet::packet, 8> packets;

    tcp_listener(size_t source_port, size_t target_port)
            : source_port(source_port), target_port(target_port), active(false) {
        sem.init(0);
    }
};

// Note: We need a list to not invalidate the values during insertions
std::list<tcp_listener> listeners;

void compute_checksum(network::ethernet::packet& packet){
    auto* ip_header  = reinterpret_cast<network::ip::header*>(packet.payload + packet.tag(1));
    auto* tcp_header = reinterpret_cast<network::tcp::header*>(packet.payload + packet.index);

    tcp_header->checksum = 0;

    // Accumulate the Payload
    auto sum = network::checksum_add_bytes(packet.payload + packet.index, 20); // TODO What is the length

    // Accumulate the IP addresses
    sum += network::checksum_add_bytes(&ip_header->source_ip, 8);

    // Accumulate the IP Protocol
    sum += ip_header->protocol;

    // Accumulate the UDP length
    sum += 20;

    // Complete the 1-complement sum
    tcp_header->checksum = switch_endian_16(network::checksum_finalize_nz(sum));
}

uint16_t get_default_flags(){
    uint16_t flags = 0; // By default

    (flag_data_offset(&flags)) = 5; // No options

    //TODO

    return flags;
}

void prepare_packet(network::ethernet::packet& packet, size_t source, size_t target, size_t payload_size){
    packet.tag(2, packet.index);

    // Set the TCP header

    auto* tcp_header = reinterpret_cast<network::tcp::header*>(packet.payload + packet.index);

    tcp_header->source_port = switch_endian_16(source);
    tcp_header->target_port = switch_endian_16(target);
    tcp_header->window_size = 1024;

    packet.index += sizeof(network::tcp::header);

    //TODO
}

} //end of anonymous namespace

void network::tcp::decode(network::interface_descriptor& /*interface*/, network::ethernet::packet& packet){
    packet.tag(2, packet.index);

    auto* tcp_header = reinterpret_cast<header*>(packet.payload + packet.index);

    logging::logf(logging::log_level::TRACE, "tcp: Start TCP packet handling\n");

    auto source_port = switch_endian_16(tcp_header->source_port);
    auto target_port = switch_endian_16(tcp_header->target_port);
    auto sequence    = switch_endian_32(tcp_header->sequence_number);
    auto ack         = switch_endian_32(tcp_header->ack_number);

    logging::logf(logging::log_level::TRACE, "tcp: Source Port %u \n", size_t(source_port));
    logging::logf(logging::log_level::TRACE, "tcp: Target Port %u \n", size_t(target_port));
    logging::logf(logging::log_level::TRACE, "tcp: Seq Number %u \n", size_t(tcp_header->sequence_number));
    logging::logf(logging::log_level::TRACE, "tcp: Ack Number %u \n", size_t(tcp_header->ack_number));
    logging::logf(logging::log_level::TRACE, "tcp: Seq Number %u \n", size_t(sequence));
    logging::logf(logging::log_level::TRACE, "tcp: Ack Number %u \n", size_t(ack));

    // Propagate to kernel listeners

    auto end = listeners.end();
    auto it = listeners.begin();

    while(it != end){
        auto& listener = *it;

        if(listener.active.load() && listener.source_port == source_port && listener.target_port == target_port){
            auto copy    = packet;
            copy.payload = new char[copy.payload_size];
            std::copy_n(packet.payload, packet.payload_size, copy.payload);

            listener.packets.push(copy);
            listener.sem.release();
        }

        ++it;
    }

    packet.index += sizeof(header);

    //TODO
}

std::expected<network::ethernet::packet> network::tcp::prepare_packet(network::interface_descriptor& interface, network::ip::address target_ip, size_t source, size_t target, size_t payload_size){
    // Ask the IP layer to craft a packet
    auto packet = network::ip::prepare_packet(interface, sizeof(header) + payload_size, target_ip, 0x06);

    if(packet){
        ::prepare_packet(*packet, source, target, payload_size);
    }

    return packet;
}

std::expected<network::ethernet::packet> network::tcp::prepare_packet(char* buffer, network::interface_descriptor& interface, network::ip::address target_ip, size_t source, size_t target, size_t payload_size){
    // Ask the IP layer to craft a packet
    auto packet = network::ip::prepare_packet(buffer, interface, sizeof(header) + payload_size, target_ip, 0x06);

    if(packet){
        ::prepare_packet(*packet, source, target, payload_size);
    }

    return packet;
}

void network::tcp::finalize_packet(network::interface_descriptor& interface, network::ethernet::packet& p){
    p.index -= sizeof(header);

    // Compute the checksum
    compute_checksum(p);

    // Give the packet to the IP layer for finalization
    network::ip::finalize_packet(interface, p);
}

std::expected<void> network::tcp::connect(network::interface_descriptor& interface, network::ip::address target_ip, size_t source, size_t target){
    auto packet = tcp::prepare_packet(interface, target_ip, source, target, 0);

    if(!packet){
        return std::make_unexpected<void>(packet.error());
    }

    auto* tcp_header = reinterpret_cast<header*>(packet->payload + packet->tag(2));

    tcp_header->sequence_number = 0;
    tcp_header->ack_number      = 0;

    auto flags = get_default_flags();
    (flag_syn(&flags)) = 1;
    tcp_header->flags = switch_endian_16(flags);

    // Create the listener

    auto& listener = listeners.emplace_back(target, source);

    listener.active = true;

    logging::logf(logging::log_level::TRACE, "tcp: Send SYN\n");
    tcp::finalize_packet(interface, *packet);

    uint32_t seq;
    uint32_t ack;

    while(true){
        // TODO Need a timeout
        listener.sem.acquire();
        auto received_packet = listener.packets.pop();

        auto* tcp_header = reinterpret_cast<header*>(received_packet.payload + received_packet.index);
        auto flags = switch_endian_16(tcp_header->flags);

        logging::logf(logging::log_level::TRACE, "tcp: Received answer\n");

        if(*flag_syn(&flags) && *flag_ack(&flags)){
            logging::logf(logging::log_level::TRACE, "tcp: Received SYN/ACK\n");

            seq = switch_endian_32(tcp_header->sequence_number);
            ack = switch_endian_32(tcp_header->ack_number);

            delete[] received_packet.payload;

            break;
        }

        delete[] received_packet.payload;
    }

    listener.active = false;

    // At this point we have received the SYN/ACK, only remains to ACK

    {
        auto packet = tcp::prepare_packet(interface, target_ip, source, target, 0);

        if (!packet) {
            return std::make_unexpected<void>(packet.error());
        }

        auto* tcp_header = reinterpret_cast<header*>(packet->payload + packet->tag(2));

        tcp_header->sequence_number = switch_endian_32(ack);
        tcp_header->ack_number      = switch_endian_32(seq + 1);

        auto flags = get_default_flags();
        (flag_ack(&flags)) = 1;
        tcp_header->flags = switch_endian_16(flags);

        logging::logf(logging::log_level::TRACE, "tcp: Send ACK\n");
        tcp::finalize_packet(interface, *packet);
    }

    auto end = listeners.end();
    auto it = listeners.begin();

    while(it != end){
        if(&(*it) == &listener){
            listeners.erase(it);
            break;
        }

        ++it;
    }

    return {};
}
<|endoftext|>