<commit_before>#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <cstring>
#include <stdio.h>
#include <string>
#include <iostream>
#include <list>
#include <map>
#include <set>

#include "server.h"
#include "duckchat.h"

// TODO Handle domain
// Server can accept connections.
// Server handles Login and Logout from users, and keeps records of which users are logged in.
// Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

//std::string user;

class Channel {
public:
  std::string name;
  std::list<User *> users;

  Channel(std::string name): name(name) {};
};

class User {
public:
  std::string name;
  in_addr_t address;
  unsigned short port;
//  std::set<Channel *> channels;

  User(std::string name, in_addr_t address, unsigned short port): name(name), address(address), port(port) {};
};

struct sockaddr_in most_recent_client_addr;
std::map<std::string, User *> kUsers;
std::map<std::string, Channel *> kChannels;

void Error(const char *msg) {
  perror(msg);
  exit(1);
}

void ProcessRequest(int server_socket, void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request current_request;
  User *current_user;
  Channel *channel;
  std::string current_channel;
  std::list<User *>::const_iterator it;
  bool is_new_channel;
  bool is_channel;

  memcpy(&current_request, buffer, sizeof(struct request));
//  std::cout << "request type: " << current_request.req_type << std::endl;
  request_t request_type = current_request.req_type;

  switch(request_type) {
    case REQ_LOGIN:
      struct request_login login_request;
      memcpy(&login_request, buffer, sizeof(struct request_login));

      current_user = new User(login_request.req_username, request_address, request_port);
      kUsers.insert({std::string(login_request.req_username), current_user});

      std::cout << "server: " << login_request.req_username << " logs in" << std::endl;
      break;

    case REQ_LOGOUT:
      struct request_logout logout_request;
      memcpy(&logout_request, buffer, sizeof(struct request_logout));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " logs out" << std::endl;
          kUsers.erase(user.first);
          break;
        }
      }
      break;

    case REQ_LEAVE:
      struct request_leave leave_request;
      memcpy(&leave_request, buffer, sizeof(struct request_leave));
      current_channel = leave_request.req_channel;
      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          is_channel = false;
          for(auto ch : kChannels){
            if(ch.first == current_channel){
              std::cout << "channel found" << std::endl;
              is_channel = true;
              break;
            }
          }

          if(is_channel){
            channel = kChannels[current_channel];

            for (it = channel->users.begin(); it != channel->users.end(); ++it){
              if((*it)->name == user.first){
                break;
              }
            }

            if( it != channel->users.end()){
              channel->users.remove(*it);
              std::cout << user.first << " leaves channel " << channel->name << std::endl;
              if (channel->users.size() == 0) {
                kChannels.erase(channel->name);
                std::cout << "server: removing empty channel " << channel->name << std::endl;
              }
            }
            for (auto u : channel->users) {
              std::cout << "user: " << u->name << std::endl;
            }
            break;
          } else {
            std::cout << "server: " << user.first << " trying to leave non-existent channel " << channel->name << std::endl;
          }

        }
      }
      break;
    case REQ_JOIN:
      struct request_join join_request;
      memcpy(&join_request, buffer, sizeof(struct request_join));
      is_new_channel = true;

      // If channel does exists in global map, set local channel to channel from kChannels
      for (auto ch : kChannels) {
        if (join_request.req_channel == ch.second->name) {
          is_new_channel = false;
          channel = ch.second;
          break;
        }
      }

      // If channel is new create a new channel
      if (is_new_channel) {
        channel = new Channel(join_request.req_channel);
      }

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " joins channel "<< channel->name << std::endl;

          channel->users.push_back(user.second);

          // Otherwise
          if (is_new_channel) {
            kChannels.insert({channel->name, channel});
          }

          // Test print
          for (auto u : channel->users) {
            std::cout << "user: " << u->name << std::endl;
          }
          break;
        }

      }
      break;
    case REQ_SAY:
      struct request_say say_request;
      memcpy(&say_request, buffer, sizeof(struct request_say));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          current_user = user.second;
          break;
        }
      }

      for(auto user : kChannels[say_request.req_channel]->users){
//        std::cout << "server sending message to: " << user->name << std::endl;
        struct sockaddr_in client_addr;
        struct text_say say;
        memcpy(&say, buffer, sizeof(struct text_say));

        memset(&client_addr, 0, sizeof(struct sockaddr_in));
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = user->port;
        client_addr.sin_addr.s_addr = user->address;

        // copy message into struct being sent
        strncpy(say.txt_channel, say_request.req_channel, CHANNEL_MAX);
        strncpy(say.txt_text, say_request.req_text, SAY_MAX);
        say.txt_type = TXT_SAY;
        strncpy(say.txt_username, current_user->name.c_str(), USERNAME_MAX);

        size_t message_size = sizeof(struct text_say);

        if (sendto(server_socket, &say, message_size, 0, (struct sockaddr*) &client_addr, sizeof(client_addr)) < 0) {
          Error("server: failed to send say\n");
        }
        std::cout << current_user->name << " sends day message in " << say_request.req_channel << std::endl;
      }
      break;
    default:
      break;
  }
}

int main(int argc, char *argv[]) {
  struct sockaddr_in server_addr;

  int server_socket;
  int receive_len;
  void* buffer[kBufferSize];
  int port;
//  std::string domain;

  if (argc < 3) {
    std::cerr << "Usage: ./server domain_name port_num" << std::endl;
    exit(1);
  }

//  domain = argv[1];
  port = atoi(argv[2]);

  memset((char *) &server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(port);

  if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    Error("server: can't open socket\n");
  }

  if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
    Error("server: bind failed\n");
  }

  printf("server: waiting on port %d\n", port);
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
//    std::cout << "before recvfrom" << std::endl;
    receive_len = recvfrom(server_socket, buffer, kBufferSize, 0, (struct sockaddr *) &client_addr, &client_addr_len);
    memcpy(&most_recent_client_addr, &client_addr, sizeof(most_recent_client_addr));
    std::cout << "port " << most_recent_client_addr.sin_port << std::endl;

    if (receive_len > 0) {
      buffer[receive_len] = 0;

//      std::cout << "port " << client_addr.sin_port << std::endl;

      ProcessRequest(server_socket, buffer, client_addr.sin_addr.s_addr, client_addr.sin_port);
    }
  }
}
<commit_msg>removed printing<commit_after>#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <cstring>
#include <stdio.h>
#include <string>
#include <iostream>
#include <list>
#include <map>
#include <set>

#include "server.h"
#include "duckchat.h"

// TODO Handle domain
// Server can accept connections.
// Server handles Login and Logout from users, and keeps records of which users are logged in.
// Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

class Channel {
public:
  std::string name;
  std::list<User *> users;

  Channel(std::string name): name(name) {};
};

class User {
public:
  std::string name;
  in_addr_t address;
  unsigned short port;

  User(std::string name, in_addr_t address, unsigned short port): name(name), address(address), port(port) {};
};

std::map<std::string, User *> kUsers;
std::map<std::string, Channel *> kChannels;

void Error(const char *msg) {
  perror(msg);
  exit(1);
}

void ProcessRequest(int server_socket, void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request current_request;
  User *current_user;
  Channel *channel;
  std::string current_channel;
  std::list<User *>::const_iterator it;
  bool is_new_channel;
  bool is_channel;

  memcpy(&current_request, buffer, sizeof(struct request));
//  std::cout << "request type: " << current_request.req_type << std::endl;
  request_t request_type = current_request.req_type;

  switch(request_type) {
    case REQ_LOGIN:
      struct request_login login_request;
      memcpy(&login_request, buffer, sizeof(struct request_login));

      current_user = new User(login_request.req_username, request_address, request_port);
      kUsers.insert({std::string(login_request.req_username), current_user});

      std::cout << "server: " << login_request.req_username << " logs in" << std::endl;
      break;

    case REQ_LOGOUT:
      struct request_logout logout_request;
      memcpy(&logout_request, buffer, sizeof(struct request_logout));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " logs out" << std::endl;
          kUsers.erase(user.first);
          break;
        }
      }
      break;

    case REQ_LEAVE:
      struct request_leave leave_request;
      memcpy(&leave_request, buffer, sizeof(struct request_leave));
      current_channel = leave_request.req_channel;
      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          is_channel = false;
          for(auto ch : kChannels){
            if(ch.first == current_channel){
              std::cout << "channel found" << std::endl;
              is_channel = true;
              break;
            }
          }

          if(is_channel){
            channel = kChannels[current_channel];

            for (it = channel->users.begin(); it != channel->users.end(); ++it){
              if((*it)->name == user.first){
                break;
              }
            }

            if( it != channel->users.end()){
              channel->users.remove(*it);
              std::cout << user.first << " leaves channel " << channel->name << std::endl;
              if (channel->users.size() == 0) {
                kChannels.erase(channel->name);
                std::cout << "server: removing empty channel " << channel->name << std::endl;
              }
            }
            for (auto u : channel->users) {
              std::cout << "user: " << u->name << std::endl;
            }
            break;
          } else {
            std::cout << "server: " << user.first << " trying to leave non-existent channel " << channel->name << std::endl;
          }

        }
      }
      break;
    case REQ_JOIN:
      struct request_join join_request;
      memcpy(&join_request, buffer, sizeof(struct request_join));
      is_new_channel = true;

      // If channel does exists in global map, set local channel to channel from kChannels
      for (auto ch : kChannels) {
        if (join_request.req_channel == ch.second->name) {
          is_new_channel = false;
          channel = ch.second;
          break;
        }
      }

      // If channel is new create a new channel
      if (is_new_channel) {
        channel = new Channel(join_request.req_channel);
      }

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " joins channel "<< channel->name << std::endl;

          channel->users.push_back(user.second);

          // Otherwise
          if (is_new_channel) {
            kChannels.insert({channel->name, channel});
          }

          // Test print
          for (auto u : channel->users) {
            std::cout << "user: " << u->name << std::endl;
          }
          break;
        }

      }
      break;
    case REQ_SAY:
      struct request_say say_request;
      memcpy(&say_request, buffer, sizeof(struct request_say));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          current_user = user.second;
          break;
        }
      }

      for(auto user : kChannels[say_request.req_channel]->users){
        struct sockaddr_in client_addr;
        struct text_say say;
        memcpy(&say, buffer, sizeof(struct text_say));

        memset(&client_addr, 0, sizeof(struct sockaddr_in));
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = user->port;
        client_addr.sin_addr.s_addr = user->address;

        // copy message into struct being sent
        strncpy(say.txt_channel, say_request.req_channel, CHANNEL_MAX);
        strncpy(say.txt_text, say_request.req_text, SAY_MAX);
        say.txt_type = TXT_SAY;
        strncpy(say.txt_username, current_user->name.c_str(), USERNAME_MAX);

        size_t message_size = sizeof(struct text_say);

        if (sendto(server_socket, &say, message_size, 0, (struct sockaddr*) &client_addr, sizeof(client_addr)) < 0) {
          Error("server: failed to send say\n");
        }
      }
      std::cout << current_user->name << " sends day message in " << say_request.req_channel << std::endl;
      break;
    default:
      break;
  }
}

int main(int argc, char *argv[]) {
  struct sockaddr_in server_addr;

  int server_socket;
  int receive_len;
  void* buffer[kBufferSize];
  int port;
//  std::string domain;

  if (argc < 3) {
    std::cerr << "Usage: ./server domain_name port_num" << std::endl;
    exit(1);
  }

//  domain = argv[1];
  port = atoi(argv[2]);

  memset((char *) &server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(port);

  if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    Error("server: can't open socket\n");
  }

  if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
    Error("server: bind failed\n");
  }

  printf("server: waiting on port %d\n", port);
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    receive_len = recvfrom(server_socket, buffer, kBufferSize, 0, (struct sockaddr *) &client_addr, &client_addr_len);

    if (receive_len > 0) {
      buffer[receive_len] = 0;

      ProcessRequest(server_socket, buffer, client_addr.sin_addr.s_addr, client_addr.sin_port);
    }
  }
}
<|endoftext|>


<commit_before>#include "connection.h"
#include "channel.cpp"

#ifndef SERVER_ROBOBO
#define SERVER_ROBOBO
Server::Server(std::string serverAddress, std::tr1::unordered_map<std::string, std::string> confVars, ModuleInterface* modFace) {
	serverName = serverAddress;
	serverConf = confVars;
	moduleData = modFace;
	std::istringstream portNumber (serverConf["port"]);
	unsigned int port;
	portNumber >> port;
	serverConnection.connectServer(serverAddress, port);
	sendLine("NICK " + serverConf["nick"]);
	sendLine("USER " + serverConf["ident"] + " here " + serverAddress + " :" + serverConf["gecos"]);
	handleData();
}

void Server::sendLine(std::string line) {
	serverConnection.sendData(line);
	moduleData->callHookOut(serverName, parseLine(line));
}

std::tr1::unordered_map<std::string, std::string> Server::getInfo() {
	return serverConf;
}

std::tr1::unordered_map<char, char> Server::getPrefixes() {
	return prefix;
}

std::vector<std::vector<char> > Server::getChanModes() {
	return chanModes;
}

std::vector<char> getChanTypes() {
	return chanTypes;
}

void Server::resyncChannels() {
	for (std::tr1::unordered_map<std::string, Channel>::iterator iter = inChannels.begin(); iter != inChannels.end(); iter++)
		serverConnection.sendData("NAMES " + iter->first);
}

void Server::handleData() {
	std::string receivedLine = "";
	std::vector<std::string> parsedLine;
	while (true) {
		receivedLine = serverConnection.receive();
		std::cout << receivedLine << std::endl;
		parsedLine = parseLine(receivedLine);
		if (parsedLine[1] == "001") { // welcome to the network
			if (serverConf["channels"] != "")
				serverConnection.sendData("JOIN " + serverConf["channels"]);
		} else if (parsedLine[1] == "005") // server features
			parse005(parsedLine);
		else if (parsedLine[1] == "332") { // channel topic
			for (std::tr1::unordered_map<std::string, Channel>::iterator it = inChannels.begin(); it != inChannels.end(); it++) {
				if (it->first == parsedLine[3])
					it->second.setTopic(parsedLine[4]);
			}
		} else if (parsedLine[1] == "353") { // NAMES reply
			for (std::tr1::unordered_map<std::string, Channel>::iterator it = inChannels.begin(); it != inChannels.end(); it++) {
				if (it->first == parsedLine[4])
					it->second.parseNames(separateBySpace(parsedLine[5]));
			}
		} else if (parsedLine[1] == "366") { // end of NAMES reply
			for (std::tr1::unordered_map<std::string, Channel>::iterator it = inChannels.begin(); it != inChannels.end(); it++) {
				if (it->first == parsedLine[3])
					it->second.numeric366();
			}
		} else if (parsedLine[1] == "JOIN" && serverConf["nick"] == separateNickFromFullHostmask(parsedLine[0].substr(1))) // bot joined a channel
			inChannels.insert(std::pair<std::string, Channel> (parsedLine[2], Channel (this)));
		else if (parsedLine[0] == "PING") // server ping
			serverConnection.sendData("PONG " + parsedLine[1]);
	}
}

void Server::parse005(std::vector<std::string> parsedLine) {
	for (unsigned int i = 3; i < parsedLine.size(); i++) {
		if (parsedLine[i] == "NAMESX")
			serverConnection.sendData("PROTOCTL NAMESX");
		// other parts will come soon
	}
}

std::vector<std::string> Server::parseLine(std::string unformattedLine) {
	std::vector<std::string> parsedLine;
	std::string linePart = "";
	for (unsigned int i = 0; i < unformattedLine.size(); i++) {
		if (unformattedLine[i] == ' ') {
			parsedLine.push_back(linePart);
			linePart = "";
			continue;
		}
		if (i > 0) {
			if (unformattedLine[i] == ':' && unformattedLine[i-1] == ' ') {
				for (i++; i < unformattedLine.size(); i++)
					linePart += unformattedLine[i];
				parsedLine.push_back(linePart);
				linePart = "";
				break;
			}
		}
		linePart += unformattedLine[i];
	}
	if (linePart != "")
		parsedLine.push_back(linePart);
	return parsedLine;
}

std::vector<std::string> Server::separateBySpace(std::string joinedLine) {
	std::vector<std::string> words;
	std::string wordPart;
	for (unsigned int i = 0; i < joinedLine.size(); i++) {
		if (joinedLine[i] == ' ') {
			words.push_back(wordPart);
			wordPart = "";
			continue;
		}
		wordPart += joinedLine[i];
	}
	if (wordPart != "")
		words.push_back(wordPart);
	return words;
}

std::string Server::separateNickFromFullHostmask(std::string hostmask) {
	return hostmask.substr(0, hostmask.find_first_of('!'));
}
#endif<commit_msg>Call the regular message hooks now, too.<commit_after>#include "connection.h"
#include "channel.cpp"

#ifndef SERVER_ROBOBO
#define SERVER_ROBOBO
Server::Server(std::string serverAddress, std::tr1::unordered_map<std::string, std::string> confVars, ModuleInterface* modFace) {
	serverName = serverAddress;
	serverConf = confVars;
	moduleData = modFace;
	std::istringstream portNumber (serverConf["port"]);
	unsigned int port;
	portNumber >> port;
	serverConnection.connectServer(serverAddress, port);
	sendLine("NICK " + serverConf["nick"]);
	sendLine("USER " + serverConf["ident"] + " here " + serverAddress + " :" + serverConf["gecos"]);
	handleData();
}

void Server::sendLine(std::string line) {
	serverConnection.sendData(line);
	moduleData->callHookOut(serverName, parseLine(line));
}

std::tr1::unordered_map<std::string, std::string> Server::getInfo() {
	return serverConf;
}

std::tr1::unordered_map<char, char> Server::getPrefixes() {
	return prefix;
}

std::vector<std::vector<char> > Server::getChanModes() {
	return chanModes;
}

std::vector<char> getChanTypes() {
	return chanTypes;
}

void Server::resyncChannels() {
	for (std::tr1::unordered_map<std::string, Channel>::iterator iter = inChannels.begin(); iter != inChannels.end(); iter++)
		serverConnection.sendData("NAMES " + iter->first);
}

void Server::handleData() {
	std::string receivedLine = "";
	std::vector<std::string> parsedLine;
	while (true) {
		receivedLine = serverConnection.receive();
		std::cout << receivedLine << std::endl;
		parsedLine = parseLine(receivedLine);
		moduleData->callHook(serverName, parsedLine); // call module hooks for the received message
		if (parsedLine[1] == "001") { // welcome to the network
			if (serverConf["channels"] != "")
				serverConnection.sendData("JOIN " + serverConf["channels"]);
		} else if (parsedLine[1] == "005") // server features
			parse005(parsedLine);
		else if (parsedLine[1] == "332") { // channel topic
			for (std::tr1::unordered_map<std::string, Channel>::iterator it = inChannels.begin(); it != inChannels.end(); it++) {
				if (it->first == parsedLine[3])
					it->second.setTopic(parsedLine[4]);
			}
		} else if (parsedLine[1] == "353") { // NAMES reply
			for (std::tr1::unordered_map<std::string, Channel>::iterator it = inChannels.begin(); it != inChannels.end(); it++) {
				if (it->first == parsedLine[4])
					it->second.parseNames(separateBySpace(parsedLine[5]));
			}
		} else if (parsedLine[1] == "366") { // end of NAMES reply
			for (std::tr1::unordered_map<std::string, Channel>::iterator it = inChannels.begin(); it != inChannels.end(); it++) {
				if (it->first == parsedLine[3])
					it->second.numeric366();
			}
		} else if (parsedLine[1] == "JOIN" && serverConf["nick"] == separateNickFromFullHostmask(parsedLine[0].substr(1))) // bot joined a channel
			inChannels.insert(std::pair<std::string, Channel> (parsedLine[2], Channel (this)));
		else if (parsedLine[0] == "PING") // server ping
			serverConnection.sendData("PONG " + parsedLine[1]);
	}
}

void Server::parse005(std::vector<std::string> parsedLine) {
	for (unsigned int i = 3; i < parsedLine.size(); i++) {
		if (parsedLine[i] == "NAMESX")
			serverConnection.sendData("PROTOCTL NAMESX");
		// other parts will come soon
	}
}

std::vector<std::string> Server::parseLine(std::string unformattedLine) {
	std::vector<std::string> parsedLine;
	std::string linePart = "";
	for (unsigned int i = 0; i < unformattedLine.size(); i++) {
		if (unformattedLine[i] == ' ') {
			parsedLine.push_back(linePart);
			linePart = "";
			continue;
		}
		if (i > 0) {
			if (unformattedLine[i] == ':' && unformattedLine[i-1] == ' ') {
				for (i++; i < unformattedLine.size(); i++)
					linePart += unformattedLine[i];
				parsedLine.push_back(linePart);
				linePart = "";
				break;
			}
		}
		linePart += unformattedLine[i];
	}
	if (linePart != "")
		parsedLine.push_back(linePart);
	return parsedLine;
}

std::vector<std::string> Server::separateBySpace(std::string joinedLine) {
	std::vector<std::string> words;
	std::string wordPart;
	for (unsigned int i = 0; i < joinedLine.size(); i++) {
		if (joinedLine[i] == ' ') {
			words.push_back(wordPart);
			wordPart = "";
			continue;
		}
		wordPart += joinedLine[i];
	}
	if (wordPart != "")
		words.push_back(wordPart);
	return words;
}

std::string Server::separateNickFromFullHostmask(std::string hostmask) {
	return hostmask.substr(0, hostmask.find_first_of('!'));
}
#endif<|endoftext|>


<commit_before>#include "server.h"
#include "backend.h"

//#define QS_LOG_DISABLE
#include "QsLog.h"

Server::Server(QObject *parent) :
	QObject(parent)
{
	mServer = new QTcpServer(this);
	if (mServer->listen(QHostAddress::Any, 1502))
	{
		QLOG_INFO() << "[Server] Server listening at: " << mServer->serverAddress().toString()+":"+QString::number(mServer->serverPort());
		connect(mServer, SIGNAL(newConnection()), SLOT(newConnection()));
	}
	else QLOG_ERROR() << "[Server] QTcpServer error: " + mServer->errorString();
}

void Server::newConnection()
{
	QTcpSocket * newConnection = mServer->nextPendingConnection();

	newConnection->socketOption(QAbstractSocket::LowDelayOption);
	mClients.append(newConnection);
	QLOG_INFO() << "[Server] New connecion: " << newConnection->peerAddress().toString()+":"+QString::number(newConnection->peerPort());
	connect(newConnection, SIGNAL(readyRead()), SLOT(readyRead()));
	connect(newConnection, SIGNAL(disconnected()), SLOT(disconnected()));
}

void Server::readyRead()
{
	QTcpSocket * socket = (QTcpSocket *) sender();
	if (socket == 0) return;

	QByteArray tcpReq = socket->readAll();
	QLOG_TRACE() << "[Server] request from " << socket->peerAddress().toString()+":"+QString::number(socket->peerPort());
	QLOG_TRACE() << "[Server] request data " << tcpReq.toHex().toUpper();
	ADU * request = new ADU(tcpReq);
	mRequests.insert(request, socket);
	QLOG_DEBUG() << "[Server] Request:" << request->toString();
	emit modbusRequest(request);
}

void Server::disconnected()
{
	QTcpSocket *socket = (QTcpSocket *) sender();
	if (socket == 0) return;

	QLOG_INFO() << "[Server] Disconnected: " << socket->peerAddress().toString()+":"+QString::number(socket->peerPort());
	mClients.removeAll(socket);
	delete mRequests.key(socket);
	socket->deleteLater();
}

void Server::modbusReply(ADU * const modbusReply)
{
	QLOG_DEBUG() << "[Server] Reply:" << modbusReply->toString();
	mRequests.value(modbusReply)->write(modbusReply->toQByteArray());
	mRequests.remove(modbusReply);
	delete modbusReply;
}
<commit_msg>server: Changed default port number to 502<commit_after>#include "server.h"
#include "backend.h"

//#define QS_LOG_DISABLE
#include "QsLog.h"

Server::Server(QObject *parent) :
	QObject(parent)
{
	mServer = new QTcpServer(this);
	if (mServer->listen(QHostAddress::Any, 502))
	{
		QLOG_INFO() << "[Server] Server listening at: " << mServer->serverAddress().toString()+":"+QString::number(mServer->serverPort());
		connect(mServer, SIGNAL(newConnection()), SLOT(newConnection()));
	}
	else QLOG_ERROR() << "[Server] QTcpServer error: " + mServer->errorString();
}

void Server::newConnection()
{
	QTcpSocket * newConnection = mServer->nextPendingConnection();

	newConnection->socketOption(QAbstractSocket::LowDelayOption);
	mClients.append(newConnection);
	QLOG_INFO() << "[Server] New connecion: " << newConnection->peerAddress().toString()+":"+QString::number(newConnection->peerPort());
	connect(newConnection, SIGNAL(readyRead()), SLOT(readyRead()));
	connect(newConnection, SIGNAL(disconnected()), SLOT(disconnected()));
}

void Server::readyRead()
{
	QTcpSocket * socket = (QTcpSocket *) sender();
	if (socket == 0) return;

	QByteArray tcpReq = socket->readAll();
	QLOG_TRACE() << "[Server] request from " << socket->peerAddress().toString()+":"+QString::number(socket->peerPort());
	QLOG_TRACE() << "[Server] request data " << tcpReq.toHex().toUpper();
	ADU * request = new ADU(tcpReq);
	mRequests.insert(request, socket);
	QLOG_DEBUG() << "[Server] Request:" << request->toString();
	emit modbusRequest(request);
}

void Server::disconnected()
{
	QTcpSocket *socket = (QTcpSocket *) sender();
	if (socket == 0) return;

	QLOG_INFO() << "[Server] Disconnected: " << socket->peerAddress().toString()+":"+QString::number(socket->peerPort());
	mClients.removeAll(socket);
	delete mRequests.key(socket);
	socket->deleteLater();
}

void Server::modbusReply(ADU * const modbusReply)
{
	QLOG_DEBUG() << "[Server] Reply:" << modbusReply->toString();
	mRequests.value(modbusReply)->write(modbusReply->toQByteArray());
	mRequests.remove(modbusReply);
	delete modbusReply;
}
<|endoftext|>


<commit_before>#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <cstring>
#include <stdio.h>
#include <string>
#include <iostream>
#include <list>
#include <map>
#include <set>

#include "server.h"
#include "duckchat.h"

// TODO Handle domain
// Server can accept connections.
// Server handles Login and Logout from users, and keeps records of which users are logged in.
// Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

class Channel {
public:
  std::string name;
  std::list<User *> users;

  Channel(std::string name): name(name) {};
};

class User {
public:
  std::string name;
  in_addr_t address;
  unsigned short port;

  User(std::string name, in_addr_t address, unsigned short port): name(name), address(address), port(port) {};
};

std::map<std::string, User *> kUsers;
std::map<std::string, Channel *> kChannels;

void Error(const char *msg) {
  perror(msg);
  exit(1);
}

void ProcessRequest(int server_socket, void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request current_request;
  User *current_user;
  Channel *channel;
  std::string current_channel;
  std::list<User *>::const_iterator it;
  std::map<std::string, User *>::const_iterator map_it;
  bool is_new_channel;
  bool is_channel;
  bool is_channel_user;
  bool is_user;

  memcpy(&current_request, buffer, sizeof(struct request));
//  std::cout << "request type: " << current_request.req_type << std::endl;
  request_t request_type = current_request.req_type;

  switch(request_type) {
    case REQ_LOGIN:
      struct request_login login_request;
      memcpy(&login_request, buffer, sizeof(struct request_login));

      current_user = new User(login_request.req_username, request_address, request_port);
      is_user = false;
      for (map_it = kUsers.begin(); map_it != kUsers.end(); ++map_it){
        if((*map_it).first == current_user->name){
          (*map_it).second = current_user;
          is_user = true;
          break;
        }
      }

      if(!is_user){
        kUsers.insert({std::string(login_request.req_username), current_user});
      }

      std::cout << "server: " << login_request.req_username << " logs in" << std::endl;
      break;

    case REQ_LOGOUT:
      struct request_logout logout_request;
      memcpy(&logout_request, buffer, sizeof(struct request_logout));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " logs out" << std::endl;
          kUsers.erase(user.first);
          break;
        }
      }
      break;

    case REQ_LEAVE:
      struct request_leave leave_request;
      memcpy(&leave_request, buffer, sizeof(struct request_leave));
      current_channel = leave_request.req_channel;
      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          is_channel = false;
          for(auto ch : kChannels){
            if(ch.first == current_channel){
              std::cout << "channel found" << std::endl;
              is_channel = true;
              break;
            }
          }

          if(is_channel){
            channel = kChannels[current_channel];

            for (it = channel->users.begin(); it != channel->users.end(); ++it){
              if((*it)->name == user.first){
                break;
              }
            }

            if( it != channel->users.end()){
              channel->users.remove(*it);
              std::cout << user.first << " leaves channel " << channel->name << std::endl;
              if (channel->users.size() == 0) {
                kChannels.erase(channel->name);
                std::cout << "server: removing empty channel " << channel->name << std::endl;
              }
            }
            for (auto u : channel->users) {
              std::cout << "user: " << u->name << std::endl;
            }
            break;
          } else {
            std::cout << "server: " << user.first << " trying to leave non-existent channel " << channel->name << std::endl;
          }

        }
      }
      break;
    case REQ_JOIN:
      struct request_join join_request;
      memcpy(&join_request, buffer, sizeof(struct request_join));
      is_new_channel = true;

      // If channel does exists in global map, set local channel to channel from kChannels
      for (auto ch : kChannels) {
        if (join_request.req_channel == ch.second->name) {
          is_new_channel = false;
          channel = ch.second;
          break;
        }
      }

      // If channel is new create a new channel
      if (is_new_channel) {
        channel = new Channel(join_request.req_channel);
      }

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;
        std::cout << std::endl;
        std::cout << "port: " << current_port << " address "<< current_address << std::endl;
        std::cout << "port: " << request_port << " address "<< request_address << std::endl;
        std::cout << std::endl;
        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " joins channel "<< channel->name << std::endl;

          is_channel_user = false;
          for(auto u : channel->users){
            if(u->name == user.second->name){
              is_channel_user = true;
              break;
            }
          }

          if(!is_channel_user){
            channel->users.push_back(user.second);
          }

          // Otherwise
          if (is_new_channel) {
            kChannels.insert({channel->name, channel});
          }

          // Test print
          for (auto u : channel->users) {
            std::cout << "user: " << u->name << std::endl;
          }
          break;
        }

      }
      break;
    case REQ_SAY:
      struct request_say say_request;
      memcpy(&say_request, buffer, sizeof(struct request_say));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          current_user = user.second;
          std::cout << "user found : " << current_user->name << std::endl;
          break;
        }
      }

      for(auto user : kChannels[say_request.req_channel]->users){
        struct sockaddr_in client_addr;
        struct text_say say;
        memcpy(&say, buffer, sizeof(struct text_say));

        memset(&client_addr, 0, sizeof(struct sockaddr_in));
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = user->port;
        client_addr.sin_addr.s_addr = user->address;

        // copy message into struct being sent
        strncpy(say.txt_channel, say_request.req_channel, CHANNEL_MAX);
        strncpy(say.txt_text, say_request.req_text, SAY_MAX);
        say.txt_type = TXT_SAY;
        strncpy(say.txt_username, current_user->name.c_str(), USERNAME_MAX);

        size_t message_size = sizeof(struct text_say);

        if (sendto(server_socket, &say, message_size, 0, (struct sockaddr*) &client_addr, sizeof(client_addr)) < 0) {
          Error("server: failed to send say\n");
        }
      }
      std::cout << current_user->name << " sends day message in " << say_request.req_channel << std::endl;
      break;
    default:
      break;
  }
}

int main(int argc, char *argv[]) {
  struct sockaddr_in server_addr;

  int server_socket;
  int receive_len;
  void* buffer[kBufferSize];
  int port;
//  std::string domain;

  if (argc < 3) {
    std::cerr << "Usage: ./server domain_name port_num" << std::endl;
    exit(1);
  }

//  domain = argv[1];
  port = atoi(argv[2]);

  memset((char *) &server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(port);

  if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    Error("server: can't open socket\n");
  }

  if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
    Error("server: bind failed\n");
  }

  printf("server: waiting on port %d\n", port);
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    receive_len = recvfrom(server_socket, buffer, kBufferSize, 0, (struct sockaddr *) &client_addr, &client_addr_len);

    if (receive_len > 0) {
      buffer[receive_len] = 0;

      ProcessRequest(server_socket, buffer, client_addr.sin_addr.s_addr, client_addr.sin_port);
    }
  }
}
<commit_msg>erasing element from map<commit_after>#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <cstring>
#include <stdio.h>
#include <string>
#include <iostream>
#include <list>
#include <map>
#include <set>

#include "server.h"
#include "duckchat.h"

// TODO Handle domain
// Server can accept connections.
// Server handles Login and Logout from users, and keeps records of which users are logged in.
// Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

class Channel {
public:
  std::string name;
  std::list<User *> users;

  Channel(std::string name): name(name) {};
};

class User {
public:
  std::string name;
  in_addr_t address;
  unsigned short port;

  User(std::string name, in_addr_t address, unsigned short port): name(name), address(address), port(port) {};
};

std::map<std::string, User *> kUsers;
std::map<std::string, Channel *> kChannels;

void Error(const char *msg) {
  perror(msg);
  exit(1);
}

void ProcessRequest(int server_socket, void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request current_request;
  User *current_user;
  Channel *channel;
  std::string current_channel;
  std::list<User *>::const_iterator it;
  std::map<std::string, User *>::const_iterator map_it;
  bool is_new_channel;
  bool is_channel;
  bool is_channel_user;

  memcpy(&current_request, buffer, sizeof(struct request));
//  std::cout << "request type: " << current_request.req_type << std::endl;
  request_t request_type = current_request.req_type;

  switch(request_type) {
    case REQ_LOGIN:
      struct request_login login_request;
      memcpy(&login_request, buffer, sizeof(struct request_login));

      current_user = new User(login_request.req_username, request_address, request_port);
      for (map_it = kUsers.begin(); map_it != kUsers.end(); ++map_it){
        if((*map_it).first == current_user->name){
            kUsers.erase(current_user->name);
          break;
        }
      }

      kUsers.insert({std::string(login_request.req_username), current_user});

      std::cout << "server: " << login_request.req_username << " logs in" << std::endl;
      break;

    case REQ_LOGOUT:
      struct request_logout logout_request;
      memcpy(&logout_request, buffer, sizeof(struct request_logout));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " logs out" << std::endl;
          kUsers.erase(user.first);
          break;
        }
      }
      break;

    case REQ_LEAVE:
      struct request_leave leave_request;
      memcpy(&leave_request, buffer, sizeof(struct request_leave));
      current_channel = leave_request.req_channel;
      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          is_channel = false;
          for(auto ch : kChannels){
            if(ch.first == current_channel){
              std::cout << "channel found" << std::endl;
              is_channel = true;
              break;
            }
          }

          if(is_channel){
            channel = kChannels[current_channel];

            for (it = channel->users.begin(); it != channel->users.end(); ++it){
              if((*it)->name == user.first){
                break;
              }
            }

            if( it != channel->users.end()){
              channel->users.remove(*it);
              std::cout << user.first << " leaves channel " << channel->name << std::endl;
              if (channel->users.size() == 0) {
                kChannels.erase(channel->name);
                std::cout << "server: removing empty channel " << channel->name << std::endl;
              }
            }
            for (auto u : channel->users) {
              std::cout << "user: " << u->name << std::endl;
            }
            break;
          } else {
            std::cout << "server: " << user.first << " trying to leave non-existent channel " << channel->name << std::endl;
          }

        }
      }
      break;
    case REQ_JOIN:
      struct request_join join_request;
      memcpy(&join_request, buffer, sizeof(struct request_join));
      is_new_channel = true;

      // If channel does exists in global map, set local channel to channel from kChannels
      for (auto ch : kChannels) {
        if (join_request.req_channel == ch.second->name) {
          is_new_channel = false;
          channel = ch.second;
          break;
        }
      }

      // If channel is new create a new channel
      if (is_new_channel) {
        channel = new Channel(join_request.req_channel);
      }

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;
        std::cout << std::endl;
        std::cout << "port: " << current_port << " address "<< current_address << std::endl;
        std::cout << "port: " << request_port << " address "<< request_address << std::endl;
        std::cout << std::endl;
        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " joins channel "<< channel->name << std::endl;

          is_channel_user = false;
          for(auto u : channel->users){
            if(u->name == user.second->name){
              is_channel_user = true;
              break;
            }
          }

          if(!is_channel_user){
            channel->users.push_back(user.second);
          }

          // Otherwise
          if (is_new_channel) {
            kChannels.insert({channel->name, channel});
          }

          // Test print
          for (auto u : channel->users) {
            std::cout << "user: " << u->name << std::endl;
          }
          break;
        }

      }
      break;
    case REQ_SAY:
      struct request_say say_request;
      memcpy(&say_request, buffer, sizeof(struct request_say));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          current_user = user.second;
          std::cout << "user found : " << current_user->name << std::endl;
          break;
        }
      }

      for(auto user : kChannels[say_request.req_channel]->users){
        struct sockaddr_in client_addr;
        struct text_say say;
        memcpy(&say, buffer, sizeof(struct text_say));

        memset(&client_addr, 0, sizeof(struct sockaddr_in));
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = user->port;
        client_addr.sin_addr.s_addr = user->address;

        // copy message into struct being sent
        strncpy(say.txt_channel, say_request.req_channel, CHANNEL_MAX);
        strncpy(say.txt_text, say_request.req_text, SAY_MAX);
        say.txt_type = TXT_SAY;
        strncpy(say.txt_username, current_user->name.c_str(), USERNAME_MAX);

        size_t message_size = sizeof(struct text_say);

        if (sendto(server_socket, &say, message_size, 0, (struct sockaddr*) &client_addr, sizeof(client_addr)) < 0) {
          Error("server: failed to send say\n");
        }
      }
      std::cout << current_user->name << " sends day message in " << say_request.req_channel << std::endl;
      break;
    default:
      break;
  }
}

int main(int argc, char *argv[]) {
  struct sockaddr_in server_addr;

  int server_socket;
  int receive_len;
  void* buffer[kBufferSize];
  int port;
//  std::string domain;

  if (argc < 3) {
    std::cerr << "Usage: ./server domain_name port_num" << std::endl;
    exit(1);
  }

//  domain = argv[1];
  port = atoi(argv[2]);

  memset((char *) &server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(port);

  if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    Error("server: can't open socket\n");
  }

  if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
    Error("server: bind failed\n");
  }

  printf("server: waiting on port %d\n", port);
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    receive_len = recvfrom(server_socket, buffer, kBufferSize, 0, (struct sockaddr *) &client_addr, &client_addr_len);

    if (receive_len > 0) {
      buffer[receive_len] = 0;

      ProcessRequest(server_socket, buffer, client_addr.sin_addr.s_addr, client_addr.sin_port);
    }
  }
}
<|endoftext|>


<commit_before>/*
 * CacheOverlay.cpp
 *
 *  Created on: Feb 21, 2013
 *      Author: Nicolas Rondaud
 */

#include "StatisticsOverlay.h"
#include <duke/engine/Context.h>
#include <duke/engine/rendering/GlyphRenderer.h>
#include <duke/engine/rendering/GeometryRenderer.h>
#include <sstream>

namespace duke {

StatisticsOverlay::StatisticsOverlay(const GlyphRenderer & glyphRenderer, const Timeline& timeline) :
		vBlankMetronom(100), frameMetronom(10), m_GlyphRenderer(glyphRenderer), m_Timeline(timeline) {
}

void StatisticsOverlay::render(const Context& context) const {
	if (!context.pCurrentImage)
		return;

	const size_t frameCount = m_Timeline.getRange().last - m_Timeline.getRange().first + 1;
	const float frameLength = context.viewport.dimension.x / (float) frameCount;
	const glm::ivec2 halfViewportDim = context.viewport.dimension / 2;
	const size_t height = 10;
	const int xOffset = -halfViewportDim.x;
	const int yOffset = -halfViewportDim.y + height / 2;

	// draw background
	const auto& geometryRenderer = m_GlyphRenderer.getGeometryRenderer();
	geometryRenderer.drawRect(context.viewport.dimension, glm::ivec2(context.viewport.dimension.x, height), //size
	glm::ivec2(0, yOffset), //pan
	glm::vec4(1, 1, 1, 0.2)); //color

	// draw cache state
	for (const Track &track : m_Timeline) {
		for (const auto & clip : track) {
			if (clip.second.pStream) {
				const auto it = cacheState.find(clip.second.pStream.get());
				if (it != cacheState.end())
					for (const auto range : it->second) {
						size_t rangeLength = frameLength * (range.last - range.first + 1);
						geometryRenderer.drawRect(context.viewport.dimension, glm::ivec2(rangeLength, height), //size
						glm::ivec2(frameLength * (clip.first + range.first) + rangeLength / 2.f + xOffset, yOffset), //pan
						glm::vec4(1, 1, 1, 0.4)); //color
					}
			}
		}
	}

	// draw cursor
	geometryRenderer.drawRect(context.viewport.dimension, glm::ivec2((frameLength < 1.0) ? 1.0 : frameLength, height), //size
	glm::ivec2(frameLength * context.currentFrame.round() + frameLength / 2.f + xOffset, yOffset), //pan
	glm::vec4(1, 1, 1, 1)); //color

	//  draw current frame
	std::ostringstream oss;

	oss << context.currentFrame.round() << '\n';
	oss << std::fixed;
	oss.width(5);
	oss.precision(2);
	oss << frameMetronom.getFPS() << "  FPS" << '\n';
	oss << vBlankMetronom.getFPS() << " VBPS";
	drawText(m_GlyphRenderer, context.viewport, oss.str().c_str(), 5, height + 10, 1.f, 1.f);
}

} /* namespace duke */
<commit_msg>fixes #13 hiding vblank overlay info in release mode<commit_after>/*
 * CacheOverlay.cpp
 *
 *  Created on: Feb 21, 2013
 *      Author: Nicolas Rondaud
 */

#include "StatisticsOverlay.h"
#include <duke/engine/Context.h>
#include <duke/engine/rendering/GlyphRenderer.h>
#include <duke/engine/rendering/GeometryRenderer.h>
#include <sstream>

namespace duke {

StatisticsOverlay::StatisticsOverlay(const GlyphRenderer & glyphRenderer, const Timeline& timeline) :
		vBlankMetronom(100), frameMetronom(10), m_GlyphRenderer(glyphRenderer), m_Timeline(timeline) {
}

void StatisticsOverlay::render(const Context& context) const {
	if (!context.pCurrentImage)
		return;

	const size_t frameCount = m_Timeline.getRange().last - m_Timeline.getRange().first + 1;
	const float frameLength = context.viewport.dimension.x / (float) frameCount;
	const glm::ivec2 halfViewportDim = context.viewport.dimension / 2;
	const size_t height = 10;
	const int xOffset = -halfViewportDim.x;
	const int yOffset = -halfViewportDim.y + height / 2;

	// draw background
	const auto& geometryRenderer = m_GlyphRenderer.getGeometryRenderer();
	geometryRenderer.drawRect(context.viewport.dimension, glm::ivec2(context.viewport.dimension.x, height), //size
	glm::ivec2(0, yOffset), //pan
	glm::vec4(1, 1, 1, 0.2)); //color

	// draw cache state
	for (const Track &track : m_Timeline) {
		for (const auto & clip : track) {
			if (clip.second.pStream) {
				const auto it = cacheState.find(clip.second.pStream.get());
				if (it != cacheState.end())
					for (const auto range : it->second) {
						size_t rangeLength = frameLength * (range.last - range.first + 1);
						geometryRenderer.drawRect(context.viewport.dimension, glm::ivec2(rangeLength, height), //size
						glm::ivec2(frameLength * (clip.first + range.first) + rangeLength / 2.f + xOffset, yOffset), //pan
						glm::vec4(1, 1, 1, 0.4)); //color
					}
			}
		}
	}

	// draw cursor
	geometryRenderer.drawRect(context.viewport.dimension, glm::ivec2((frameLength < 1.0) ? 1.0 : frameLength, height), //size
	glm::ivec2(frameLength * context.currentFrame.round() + frameLength / 2.f + xOffset, yOffset), //pan
	glm::vec4(1, 1, 1, 1)); //color

	//  draw current frame
	std::ostringstream oss;

	oss << context.currentFrame.round() << '\n';
	oss << std::fixed;
	oss.width(5);
	oss.precision(2);
	oss << frameMetronom.getFPS() << "  FPS";
#ifndef NDEBUG // adding vblank in case in debug mode
	oss << '\n' << vBlankMetronom.getFPS() << " VBPS";
#endif
	drawText(m_GlyphRenderer, context.viewport, oss.str().c_str(), 5, height + 10, 1.f, 1.f);
}

} /* namespace duke */
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/hwpf/fapi2/include/hw_access_def.H $               */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2012,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file hw_access_def.H
/// @brief Hardware access definitions
///

#ifndef FAPI2_HWACCESSDEF_H_
#define FAPI2_HWACCESSDEF_H_

#include <stdint.h>

/// @cond
typedef uint64_t spyId_t;
typedef uint64_t scanRingId_t;
/// @endcond

namespace fapi2
{
///
/// @enum fapi2::ChipOpModifyMode
/// @brief Enumeration of modify modes used in HW access modify operations
///
enum ChipOpModifyMode
{
    CHIP_OP_MODIFY_MODE_OR = 1,  ///< Modify or mode
    CHIP_OP_MODIFY_MODE_AND = 2, ///< Modify and mode
    CHIP_OP_MODIFY_MODE_XOR = 3, ///< Modify xor mode
};

///
/// @enum fapi2::RingMode
/// @brief Enumeration of Ring access operation modes
///        This is a bitmap to allow the user to specify multiple modes.
///
enum RingMode
{
    RING_MODE_HEADER_CHECK              = 0x00000000, ///< Check header
    RING_MODE_SET_PULSE_NO_OPCG_COND    = 0x00000001, ///< Set pulse with
    ///  no OPCG conditioning
    RING_MODE_NO_HEADER_CHECK           = 0x00000002, ///< Don't check header
    RING_MODE_SET_PULSE_NSL             = 0x00000004, ///< Set pulse with NSL
    ///  pulse
    RING_MODE_SET_PULSE_SL              = 0x00000008, ///< Set pulse with SL
    ///  pulse
    RING_MODE_SET_PULSE_ALL             = 0x00000010, ///< Set pulse with pulse
    ///  to all hold types
    RING_MODE_FASTARRAY                 = 0x00000020, ///< Single clock pulse
    ///  for fastarray
};

/// @enum OpModes operational Mode Error Functions
enum OpModes
{
    // These are bit-masks in case they need to be or'd together
    NORMAL           = 0x00,
    IGNORE_HW_ERROR  = 0x01,
    DO_NOT_DO_WAKEUP = 0x02,
};

}

#endif
<commit_msg>Defining generic RingId_t type for transitional eCMD release<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/hwpf/fapi2/include/hw_access_def.H $               */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2012,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file hw_access_def.H
/// @brief Hardware access definitions
///

#ifndef FAPI2_HWACCESSDEF_H_
#define FAPI2_HWACCESSDEF_H_

#include <stdint.h>

/// @cond
typedef uint64_t spyId_t;
typedef uint64_t scanRingId_t;
typedef uint16_t RingId_t;
/// @endcond

namespace fapi2
{
///
/// @enum fapi2::ChipOpModifyMode
/// @brief Enumeration of modify modes used in HW access modify operations
///
enum ChipOpModifyMode
{
    CHIP_OP_MODIFY_MODE_OR = 1,  ///< Modify or mode
    CHIP_OP_MODIFY_MODE_AND = 2, ///< Modify and mode
    CHIP_OP_MODIFY_MODE_XOR = 3, ///< Modify xor mode
};

///
/// @enum fapi2::RingMode
/// @brief Enumeration of Ring access operation modes
///        This is a bitmap to allow the user to specify multiple modes.
///
enum RingMode
{
    RING_MODE_HEADER_CHECK              = 0x00000000, ///< Check header
    RING_MODE_SET_PULSE_NO_OPCG_COND    = 0x00000001, ///< Set pulse with
    ///  no OPCG conditioning
    RING_MODE_NO_HEADER_CHECK           = 0x00000002, ///< Don't check header
    RING_MODE_SET_PULSE_NSL             = 0x00000004, ///< Set pulse with NSL
    ///  pulse
    RING_MODE_SET_PULSE_SL              = 0x00000008, ///< Set pulse with SL
    ///  pulse
    RING_MODE_SET_PULSE_ALL             = 0x00000010, ///< Set pulse with pulse
    ///  to all hold types
    RING_MODE_FASTARRAY                 = 0x00000020, ///< Single clock pulse
    ///  for fastarray
};

/// @enum OpModes operational Mode Error Functions
enum OpModes
{
    // These are bit-masks in case they need to be or'd together
    NORMAL           = 0x00,
    IGNORE_HW_ERROR  = 0x01,
    DO_NOT_DO_WAKEUP = 0x02,
};

}

#endif
<|endoftext|>


<commit_before>//..............................................................................
//
//  This file is part of the Jancy toolkit.
//
//  Jancy is distributed under the MIT license.
//  For details see accompanying license.txt file,
//  the public copy of which is also available at:
//  http://tibbo.com/downloads/archive/jancy/license.txt
//
//..............................................................................

#include "pch.h"
#include "jnc_io_UsbEndpoint.h"
#include "jnc_io_UsbInterface.h"
#include "jnc_io_UsbDevice.h"
#include "jnc_io_UsbLib.h"
#include "jnc_Error.h"

namespace jnc {
namespace io {

//..............................................................................

JNC_DEFINE_OPAQUE_CLASS_TYPE (
	UsbEndpoint,
	"io.UsbEndpoint",
	g_usbLibGuid,
	UsbLibCacheSlot_UsbEndpoint,
	UsbEndpoint,
	&UsbEndpoint::markOpaqueGcRoots
	)

JNC_BEGIN_TYPE_FUNCTION_MAP (UsbEndpoint)
	JNC_MAP_CONSTRUCTOR (&jnc::construct <UsbEndpoint>)
	JNC_MAP_DESTRUCTOR (&jnc::destruct <UsbEndpoint>)

	JNC_MAP_AUTOGET_PROPERTY ("m_readTimeout",     &UsbEndpoint::setReadTimeout)
	JNC_MAP_AUTOGET_PROPERTY ("m_readParallelism", &UsbEndpoint::setReadParallelism)
	JNC_MAP_AUTOGET_PROPERTY ("m_readBlockSize",   &UsbEndpoint::setReadBlockSize)
	JNC_MAP_AUTOGET_PROPERTY ("m_readBufferSize",  &UsbEndpoint::setReadBufferSize)
	JNC_MAP_AUTOGET_PROPERTY ("m_writeBufferSize", &UsbEndpoint::setWriteBufferSize)
	JNC_MAP_AUTOGET_PROPERTY ("m_options",         &UsbEndpoint::setOptions)

	JNC_MAP_FUNCTION ("close",        &UsbEndpoint::close)
	JNC_MAP_FUNCTION ("write",        &UsbEndpoint::write)
	JNC_MAP_FUNCTION ("read",         &UsbEndpoint::read)
	JNC_MAP_FUNCTION ("wait",         &UsbEndpoint::wait)
	JNC_MAP_FUNCTION ("cancelWait",   &UsbEndpoint::cancelWait)
	JNC_MAP_FUNCTION ("blockingWait", &UsbEndpoint::blockingWait)
JNC_END_TYPE_FUNCTION_MAP ()

//..............................................................................

UsbEndpoint::UsbEndpoint ()
{
	m_readTimeout = Def_ReadTimeout;
	m_readParallelism = Def_ReadParallelism;
	m_readBlockSize = Def_ReadBlockSize;
	m_readBufferSize = Def_ReadBufferSize;
	m_writeBufferSize = Def_WriteBufferSize;
	m_options = Def_Options;

	m_readBuffer.setBufferSize (Def_ReadBufferSize);
	m_writeBuffer.setBufferSize (Def_WriteBufferSize);
}

bool
UsbEndpoint::open ()
{
	AsyncIoDevice::open ();
	wakeIoThread ();
	return m_ioThread.start ();
}

void
JNC_CDECL
UsbEndpoint::close ()
{
	if (!m_isOpen)
		return;

	m_lock.lock ();
	m_ioThreadFlags |= IoThreadFlag_Closing;
	wakeIoThread ();
	m_lock.unlock ();

	GcHeap* gcHeap = m_runtime->getGcHeap ();
	gcHeap->enterWaitRegion ();
	m_ioThread.waitAndClose ();
	gcHeap->leaveWaitRegion ();

	AsyncIoDevice::close ();
}

size_t
JNC_CDECL
UsbEndpoint::read (
	DataPtr ptr,
	size_t size
	)
{
	if (isOutEndpoint ())
	{
		err::setError ("Cannot read from a USB OUT-endpoint");
		jnc::propagateLastError ();
		return -1;
	}

	return bufferedRead (ptr, size);
}

size_t
JNC_CDECL
UsbEndpoint::write (
	DataPtr ptr,
	size_t size
	)
{
	if (isInEndpoint ())
	{
		err::setError ("Cannot write to a USB IN-endpoint");
		jnc::propagateLastError ();
		return -1;
	}

	return bufferedWrite (ptr, size);
}

void
UsbEndpoint::ioThreadFunc ()
{
	if (isInEndpoint ())
		readLoop ();
	else
		writeLoop ();

	cancelAllActiveTransfers ();
}

void
UsbEndpoint::cancelAllActiveTransfers ()
{
	char buffer [256];
	sl::Array <Transfer*> activeTransferArray (ref::BufKind_Stack, buffer, sizeof (buffer));

	m_lock.lock ();

	sl::Iterator <Transfer> it = m_activeTransferList.getHead ();
	for (; it; it++)
		activeTransferArray.append (*it);

	m_lock.unlock ();

	size_t count = activeTransferArray.getCount ();
	for (size_t i = 0; i < count; i++)
		activeTransferArray [i]->m_usbTransfer.cancel (); // may fail if already completed

	// wait for all transfers to complete

	m_lock.lock ();

	while (!m_activeTransferList.isEmpty ())
	{
		m_lock.unlock ();
		sleepIoThread ();
		m_lock.lock ();
	}

	// put all completed transfer back to the free pool

	while (!m_completedTransferList.isEmpty ())
	{
		Transfer* transfer = m_completedTransferList.removeHead ();
		m_transferPool.put (transfer);
	}

	m_lock.unlock ();
}

void
UsbEndpoint::readLoop ()
{
	ASSERT (isInEndpoint ());

	bool result;

	UsbEndpointDesc* desc = (UsbEndpointDesc*) m_endpointDescPtr.m_p;

	for (;;)
	{
		sleepIoThread ();

		m_lock.lock ();
		if (m_ioThreadFlags & IoThreadFlag_Closing)
		{
			m_lock.unlock ();
			break;
		}

		uint_t prevActiveEvents = m_activeEvents;
		m_activeEvents = 0;

		while (!m_completedTransferList.isEmpty ())
		{
			Transfer* transfer = m_completedTransferList.removeHead ();

			if (transfer->m_usbTransfer->status != LIBUSB_TRANSFER_COMPLETED &&
				transfer->m_usbTransfer->status != LIBUSB_TRANSFER_TIMED_OUT && // <- not really an error
				transfer->m_usbTransfer->status != LIBUSB_TRANSFER_CANCELLED    // <- not really an error
				)
			{
				m_transferPool.put (transfer);
				setIoErrorEvent_l (axl::io::UsbError (LIBUSB_ERROR_IO));
				return;
			}

			addToReadBuffer (transfer->m_buffer, transfer->m_usbTransfer->actual_length);
			m_transferPool.put (transfer);
		}

		size_t activeReadCount = m_activeTransferList.getCount ();
		if (!m_readBuffer.isFull () && activeReadCount < m_readParallelism)
		{
			uint_t readTimeout = m_readTimeout;
			size_t readBlockSize = m_readBlockSize;
			size_t newReadCount = m_readParallelism - activeReadCount;
			for (size_t i = 0; i < newReadCount; i++)
			{
				Transfer* transfer = m_transferPool.get ();
				result = transfer->m_buffer.setCount (readBlockSize);
				if (!result)
				{
					m_transferPool.put (transfer);
					setIoErrorEvent_l ();
					return;
				}

				transfer->m_isCompletedOutOfOrder = false;
				m_activeTransferList.insertTail (transfer); // put it on active list prior to submission
				m_lock.unlock ();

				result = submitTransfer (transfer, transfer->m_buffer, transfer->m_buffer.getCount (), readTimeout);

				m_lock.lock ();

				if (!result)
				{
					m_activeTransferList.remove (transfer);
					m_transferPool.put (transfer);
					setIoErrorEvent_l ();
					return;
				}
			}
		}

		updateReadWriteBufferEvents ();

		if (m_activeEvents != prevActiveEvents)
			processWaitLists_l ();
		else
			m_lock.unlock ();
	}
}

void
UsbEndpoint::writeLoop ()
{
	ASSERT (isOutEndpoint ());

	bool result;

	for (;;)
	{
		sleepIoThread ();

		m_lock.lock ();
		if (m_ioThreadFlags & IoThreadFlag_Closing)
		{
			m_lock.unlock ();
			break;
		}

		uint_t prevActiveEvents = m_activeEvents;
		m_activeEvents = 0;

		if (!m_completedTransferList.isEmpty ())
		{
			Transfer* transfer = m_completedTransferList.removeHead ();

			if (transfer->m_usbTransfer->status != LIBUSB_TRANSFER_COMPLETED)
			{
				m_transferPool.put (transfer);
				setIoErrorEvent_l (axl::io::UsbError (LIBUSB_ERROR_IO));
				break;
			}

			if ((size_t) transfer->m_usbTransfer->actual_length < m_writeBlock.getCount ()) // shouldn't happen, actually
				m_writeBlock.remove (0, transfer->m_usbTransfer->actual_length);
			else
				m_writeBlock.clear ();

			m_transferPool.put (transfer);
			ASSERT (m_completedTransferList.isEmpty ()); // we never submit more than one
		}

		getNextWriteBlock (&m_writeBlock);

		if (m_activeTransferList.isEmpty () && !m_writeBlock.isEmpty ())
		{
			Transfer* transfer = m_transferPool.get ();
			transfer->m_isCompletedOutOfOrder = false;
			m_activeTransferList.insertTail (transfer); // put it on active list prior to submission
			m_lock.unlock ();

			result = submitTransfer (transfer, m_writeBlock, m_writeBlock.getCount (), -1);

			m_lock.lock ();

			if (!result)
			{
				m_activeTransferList.remove (transfer);
				m_transferPool.put (transfer);
				setIoErrorEvent_l ();
				break;
			}
		}

		updateReadWriteBufferEvents ();

		if (m_activeEvents != prevActiveEvents)
			processWaitLists_l ();
		else
			m_lock.unlock ();
	}
}

bool
UsbEndpoint::submitTransfer (
	Transfer* transfer,
	void* p,
	size_t size,
	uint_t timeout
	)
{
	UsbEndpointDesc* desc = (UsbEndpointDesc*) m_endpointDescPtr.m_p;

	bool result = transfer->m_usbTransfer.create ();
	if (!result)
		return false;

	axl::io::UsbDevice* device = m_parentInterface->m_parentDevice->getDevice ();
	switch (desc->m_transferType)
	{
	case LIBUSB_TRANSFER_TYPE_BULK:
		transfer->m_usbTransfer.fillBulkTransfer (
			device->getOpenHandle (),
			desc->m_endpointId,
			p,
			size,
			onTransferCompleted,
			transfer,
			timeout
			);

		break;

	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
		transfer->m_usbTransfer.fillInterruptTransfer (
			device->getOpenHandle (),
			desc->m_endpointId,
			p,
			size,
			onTransferCompleted,
			transfer,
			timeout
			);

		break;

	case LIBUSB_TRANSFER_TYPE_CONTROL:
	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
	case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
		// not yet, fall through...

	default:
		err::setError (err::SystemErrorCode_NotImplemented);
		return false;
	}

	transfer->m_self = this;

	return transfer->m_usbTransfer.submit ();
}

void
LIBUSB_CALL
UsbEndpoint::onTransferCompleted (libusb_transfer* usbTransfer)
{
	Transfer* transfer = (Transfer*) usbTransfer->user_data;
	ASSERT (transfer->m_usbTransfer == usbTransfer);

	UsbEndpoint* self = transfer->m_self;

	self->m_lock.lock ();

	// surprisingly enough, libusb may deliver completions out-of-order

	if (transfer != *self->m_activeTransferList.getHead ())
	{
		transfer->m_isCompletedOutOfOrder = true;
	}
	else
	{
		do
		{
			self->m_activeTransferList.remove (transfer);
			self->m_completedTransferList.insertTail (transfer);
			transfer = *self->m_activeTransferList.getHead ();
		} while (transfer && transfer->m_isCompletedOutOfOrder);

		self->wakeIoThread ();
	}

	self->m_lock.unlock ();
}

//..............................................................................

} // namespace io
} // namespace jnc
<commit_msg>[jnc_io_usb] fix: invalid location for clearing active events (lock was released after that)<commit_after>//..............................................................................
//
//  This file is part of the Jancy toolkit.
//
//  Jancy is distributed under the MIT license.
//  For details see accompanying license.txt file,
//  the public copy of which is also available at:
//  http://tibbo.com/downloads/archive/jancy/license.txt
//
//..............................................................................

#include "pch.h"
#include "jnc_io_UsbEndpoint.h"
#include "jnc_io_UsbInterface.h"
#include "jnc_io_UsbDevice.h"
#include "jnc_io_UsbLib.h"
#include "jnc_Error.h"

namespace jnc {
namespace io {

//..............................................................................

JNC_DEFINE_OPAQUE_CLASS_TYPE (
	UsbEndpoint,
	"io.UsbEndpoint",
	g_usbLibGuid,
	UsbLibCacheSlot_UsbEndpoint,
	UsbEndpoint,
	&UsbEndpoint::markOpaqueGcRoots
	)

JNC_BEGIN_TYPE_FUNCTION_MAP (UsbEndpoint)
	JNC_MAP_CONSTRUCTOR (&jnc::construct <UsbEndpoint>)
	JNC_MAP_DESTRUCTOR (&jnc::destruct <UsbEndpoint>)

	JNC_MAP_AUTOGET_PROPERTY ("m_readTimeout",     &UsbEndpoint::setReadTimeout)
	JNC_MAP_AUTOGET_PROPERTY ("m_readParallelism", &UsbEndpoint::setReadParallelism)
	JNC_MAP_AUTOGET_PROPERTY ("m_readBlockSize",   &UsbEndpoint::setReadBlockSize)
	JNC_MAP_AUTOGET_PROPERTY ("m_readBufferSize",  &UsbEndpoint::setReadBufferSize)
	JNC_MAP_AUTOGET_PROPERTY ("m_writeBufferSize", &UsbEndpoint::setWriteBufferSize)
	JNC_MAP_AUTOGET_PROPERTY ("m_options",         &UsbEndpoint::setOptions)

	JNC_MAP_FUNCTION ("close",        &UsbEndpoint::close)
	JNC_MAP_FUNCTION ("write",        &UsbEndpoint::write)
	JNC_MAP_FUNCTION ("read",         &UsbEndpoint::read)
	JNC_MAP_FUNCTION ("wait",         &UsbEndpoint::wait)
	JNC_MAP_FUNCTION ("cancelWait",   &UsbEndpoint::cancelWait)
	JNC_MAP_FUNCTION ("blockingWait", &UsbEndpoint::blockingWait)
JNC_END_TYPE_FUNCTION_MAP ()

//..............................................................................

UsbEndpoint::UsbEndpoint ()
{
	m_readTimeout = Def_ReadTimeout;
	m_readParallelism = Def_ReadParallelism;
	m_readBlockSize = Def_ReadBlockSize;
	m_readBufferSize = Def_ReadBufferSize;
	m_writeBufferSize = Def_WriteBufferSize;
	m_options = Def_Options;

	m_readBuffer.setBufferSize (Def_ReadBufferSize);
	m_writeBuffer.setBufferSize (Def_WriteBufferSize);
}

bool
UsbEndpoint::open ()
{
	AsyncIoDevice::open ();
	wakeIoThread ();
	return m_ioThread.start ();
}

void
JNC_CDECL
UsbEndpoint::close ()
{
	if (!m_isOpen)
		return;

	m_lock.lock ();
	m_ioThreadFlags |= IoThreadFlag_Closing;
	wakeIoThread ();
	m_lock.unlock ();

	GcHeap* gcHeap = m_runtime->getGcHeap ();
	gcHeap->enterWaitRegion ();
	m_ioThread.waitAndClose ();
	gcHeap->leaveWaitRegion ();

	AsyncIoDevice::close ();
}

size_t
JNC_CDECL
UsbEndpoint::read (
	DataPtr ptr,
	size_t size
	)
{
	if (isOutEndpoint ())
	{
		err::setError ("Cannot read from a USB OUT-endpoint");
		jnc::propagateLastError ();
		return -1;
	}

	return bufferedRead (ptr, size);
}

size_t
JNC_CDECL
UsbEndpoint::write (
	DataPtr ptr,
	size_t size
	)
{
	if (isInEndpoint ())
	{
		err::setError ("Cannot write to a USB IN-endpoint");
		jnc::propagateLastError ();
		return -1;
	}

	return bufferedWrite (ptr, size);
}

void
UsbEndpoint::ioThreadFunc ()
{
	if (isInEndpoint ())
		readLoop ();
	else
		writeLoop ();

	cancelAllActiveTransfers ();
}

void
UsbEndpoint::cancelAllActiveTransfers ()
{
	char buffer [256];
	sl::Array <Transfer*> activeTransferArray (ref::BufKind_Stack, buffer, sizeof (buffer));

	m_lock.lock ();

	sl::Iterator <Transfer> it = m_activeTransferList.getHead ();
	for (; it; it++)
		activeTransferArray.append (*it);

	m_lock.unlock ();

	size_t count = activeTransferArray.getCount ();
	for (size_t i = 0; i < count; i++)
		activeTransferArray [i]->m_usbTransfer.cancel (); // may fail if already completed

	// wait for all transfers to complete

	m_lock.lock ();

	while (!m_activeTransferList.isEmpty ())
	{
		m_lock.unlock ();
		sleepIoThread ();
		m_lock.lock ();
	}

	// put all completed transfer back to the free pool

	while (!m_completedTransferList.isEmpty ())
	{
		Transfer* transfer = m_completedTransferList.removeHead ();
		m_transferPool.put (transfer);
	}

	m_lock.unlock ();
}

void
UsbEndpoint::readLoop ()
{
	ASSERT (isInEndpoint ());

	bool result;

	UsbEndpointDesc* desc = (UsbEndpointDesc*) m_endpointDescPtr.m_p;

	for (;;)
	{
		sleepIoThread ();

		m_lock.lock ();
		if (m_ioThreadFlags & IoThreadFlag_Closing)
		{
			m_lock.unlock ();
			break;
		}

		while (!m_completedTransferList.isEmpty ())
		{
			Transfer* transfer = m_completedTransferList.removeHead ();

			if (transfer->m_usbTransfer->status != LIBUSB_TRANSFER_COMPLETED &&
				transfer->m_usbTransfer->status != LIBUSB_TRANSFER_TIMED_OUT && // <- not really an error
				transfer->m_usbTransfer->status != LIBUSB_TRANSFER_CANCELLED    // <- not really an error
				)
			{
				m_transferPool.put (transfer);
				setIoErrorEvent_l (axl::io::UsbError (LIBUSB_ERROR_IO));
				return;
			}

			addToReadBuffer (transfer->m_buffer, transfer->m_usbTransfer->actual_length);
			m_transferPool.put (transfer);
		}

		size_t activeReadCount = m_activeTransferList.getCount ();
		if (!m_readBuffer.isFull () && activeReadCount < m_readParallelism)
		{
			uint_t readTimeout = m_readTimeout;
			size_t readBlockSize = m_readBlockSize;
			size_t newReadCount = m_readParallelism - activeReadCount;
			for (size_t i = 0; i < newReadCount; i++)
			{
				Transfer* transfer = m_transferPool.get ();
				result = transfer->m_buffer.setCount (readBlockSize);
				if (!result)
				{
					m_transferPool.put (transfer);
					setIoErrorEvent_l ();
					return;
				}

				transfer->m_isCompletedOutOfOrder = false;
				m_activeTransferList.insertTail (transfer); // put it on active list prior to submission
				m_lock.unlock ();

				result = submitTransfer (transfer, transfer->m_buffer, transfer->m_buffer.getCount (), readTimeout);

				m_lock.lock ();

				if (!result)
				{
					m_activeTransferList.remove (transfer);
					m_transferPool.put (transfer);
					setIoErrorEvent_l ();
					return;
				}
			}
		}

		uint_t prevActiveEvents = m_activeEvents;
		m_activeEvents = 0;

		updateReadWriteBufferEvents ();

		if (m_activeEvents != prevActiveEvents)
			processWaitLists_l ();
		else
			m_lock.unlock ();
	}
}

void
UsbEndpoint::writeLoop ()
{
	ASSERT (isOutEndpoint ());

	bool result;

	for (;;)
	{
		sleepIoThread ();

		m_lock.lock ();
		if (m_ioThreadFlags & IoThreadFlag_Closing)
		{
			m_lock.unlock ();
			break;
		}

		if (!m_completedTransferList.isEmpty ())
		{
			Transfer* transfer = m_completedTransferList.removeHead ();

			if (transfer->m_usbTransfer->status != LIBUSB_TRANSFER_COMPLETED)
			{
				m_transferPool.put (transfer);
				setIoErrorEvent_l (axl::io::UsbError (LIBUSB_ERROR_IO));
				break;
			}

			if ((size_t) transfer->m_usbTransfer->actual_length < m_writeBlock.getCount ()) // shouldn't happen, actually
				m_writeBlock.remove (0, transfer->m_usbTransfer->actual_length);
			else
				m_writeBlock.clear ();

			m_transferPool.put (transfer);
			ASSERT (m_completedTransferList.isEmpty ()); // we never submit more than one
		}

		getNextWriteBlock (&m_writeBlock);

		if (m_activeTransferList.isEmpty () && !m_writeBlock.isEmpty ())
		{
			Transfer* transfer = m_transferPool.get ();
			transfer->m_isCompletedOutOfOrder = false;
			m_activeTransferList.insertTail (transfer); // put it on active list prior to submission
			m_lock.unlock ();

			result = submitTransfer (transfer, m_writeBlock, m_writeBlock.getCount (), -1);

			m_lock.lock ();

			if (!result)
			{
				m_activeTransferList.remove (transfer);
				m_transferPool.put (transfer);
				setIoErrorEvent_l ();
				break;
			}
		}

		uint_t prevActiveEvents = m_activeEvents;
		m_activeEvents = 0;

		updateReadWriteBufferEvents ();

		if (m_activeEvents != prevActiveEvents)
			processWaitLists_l ();
		else
			m_lock.unlock ();
	}
}

bool
UsbEndpoint::submitTransfer (
	Transfer* transfer,
	void* p,
	size_t size,
	uint_t timeout
	)
{
	UsbEndpointDesc* desc = (UsbEndpointDesc*) m_endpointDescPtr.m_p;

	bool result = transfer->m_usbTransfer.create ();
	if (!result)
		return false;

	axl::io::UsbDevice* device = m_parentInterface->m_parentDevice->getDevice ();
	switch (desc->m_transferType)
	{
	case LIBUSB_TRANSFER_TYPE_BULK:
		transfer->m_usbTransfer.fillBulkTransfer (
			device->getOpenHandle (),
			desc->m_endpointId,
			p,
			size,
			onTransferCompleted,
			transfer,
			timeout
			);

		break;

	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
		transfer->m_usbTransfer.fillInterruptTransfer (
			device->getOpenHandle (),
			desc->m_endpointId,
			p,
			size,
			onTransferCompleted,
			transfer,
			timeout
			);

		break;

	case LIBUSB_TRANSFER_TYPE_CONTROL:
	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
	case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
		// not yet, fall through...

	default:
		err::setError (err::SystemErrorCode_NotImplemented);
		return false;
	}

	transfer->m_self = this;

	return transfer->m_usbTransfer.submit ();
}

void
LIBUSB_CALL
UsbEndpoint::onTransferCompleted (libusb_transfer* usbTransfer)
{
	Transfer* transfer = (Transfer*) usbTransfer->user_data;
	ASSERT (transfer->m_usbTransfer == usbTransfer);

	UsbEndpoint* self = transfer->m_self;

	self->m_lock.lock ();

	// surprisingly enough, libusb may deliver completions out-of-order

	if (transfer != *self->m_activeTransferList.getHead ())
	{
		transfer->m_isCompletedOutOfOrder = true;
	}
	else
	{
		do
		{
			self->m_activeTransferList.remove (transfer);
			self->m_completedTransferList.insertTail (transfer);
			transfer = *self->m_activeTransferList.getHead ();
		} while (transfer && transfer->m_isCompletedOutOfOrder);

		self->wakeIoThread ();
	}

	self->m_lock.unlock ();
}

//..............................................................................

} // namespace io
} // namespace jnc
<|endoftext|>