<commit_before>#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <resolv.h>

#include "../include/secnet.h"

// no sane human will use packets with more than 128MB payload
// at least not in the current century.
#define MAX_PACKET_PAYLOAD_SIZE	134217728

bool SecNet::_listening;
int SecNet::_serverSocket;
sigslot::signal3<SecNet::Packet, uint8_t*, SecNet*> SecNet::receivedPacket;

SecNet::SecNet(int socket, SSL_CTX* serverContext)
{
	close(SecNet::_serverSocket);

	_socket = socket;
	_ssl = SSL_new(serverContext);

	if(_ssl == 0)
	{
		std::cout << "Could not create new SSL connection!" << std::endl;
		exit(-1); // if the fork can't handle the client, there is no reason to allow it staying alive >:]
	}

	SSL_set_fd(_ssl, _socket);

	if(SSL_accept(_ssl) != 1)
	{
		std::cout << "SSL Handshake failed!" << std::endl;
		ERR_print_errors_cb(SecNet::sslErrorCallback, NULL);
		exit(-1); // still no reason to allow it staying alive >:]
	}

	// handle the connection
	Packet packet;
	bzero(&packet, sizeof(packet));
	SSL_read(_ssl, &packet, sizeof(packet));

	if(*(int*)&packet == 0x20544547) // don't hurt me .. it's just "GET " as integer
	{
		// meh .. It's HTTP, probably Websocket as we use this one ...
		handleWebSocket(&packet);
		return;
	}

	if(packet.payloadLength > MAX_PACKET_PAYLOAD_SIZE)
	{
		std::cout << "Client wanted to send data with payload size " << packet.payloadLength << " but we only handle a maximum of " << MAX_PACKET_PAYLOAD_SIZE << "." << std::endl;
		exit(-1); // nope. Just nope.
	}

	uint8_t* payload = new uint8_t[packet.payloadLength];
	SSL_read(_ssl, payload, packet.payloadLength);

	receivedPacket.emit(packet, payload, this);
}

SecNet::~SecNet()
{
	SSL_shutdown(_ssl);
	close(_socket);
	SSL_free(_ssl);

	exit(0);
}

void SecNet::sendPacket(Packet packet, uint8_t* payload)
{
	SSL_write(_ssl, &packet, sizeof(packet));
	SSL_write(_ssl, payload, packet.payloadLength);
}

void SecNet::handleWebSocket(Packet* packet)
{
	const int MaxHeaders = 15;

	char initialData[sizeof(Packet) + 1];
	bzero(initialData, sizeof(Packet) + 1);
	memcpy(initialData, packet, sizeof(Packet));

	std::string headerLines[MaxHeaders]; // we only support the first 15 headers
	headerLines[0] = "";

	int headers = 0;
	bool headerEnd = false;

	for(unsigned int i = 0; i < sizeof(Packet); i++)
	{
		char c = initialData[i];

		if(c != '\r' && c != '\n')
		{
			headerLines[headers] += c;
		}
		else if(c == '\r')
		{
			headers++;
			headerLines[headers] = "";
		}
	}

	while(!headerEnd && headers < MaxHeaders)
	{
		char c = 0;
		SSL_read(_ssl, &c, 1);

		if(c != '\r' && c != '\n')
		{
			headerLines[headers] += c;
		}
		else if(c == '\r')
		{
			if(headerLines[headers] == "")
			{
				headerEnd = true;
				headers--;
				break;
			}

			headers++;
			headerLines[headers] = "";
		}
	}

	std::string protocol, key, version;

	for(int i = 1; i < headers; i++)
	{
		std::string headerName = "";
		std::string headerContent = "";

		unsigned int j;
		for(j = 0; j < headerLines[i].size(); j++)
		{
			char c = headerLines[i].at(j);

			if(c != ':')
				headerName += c;
			else
				break;
		}

		j += 2; // skip : and the following space

		for(; j < headerLines[i].size(); j++)
		{
			char c = headerLines[i].at(j);
			headerContent += c;
		}

		if(headerName == "Sec-WebSocket-Protocol")
			protocol = headerContent;
		else if(headerName == "Sec-WebSocket-Version")
			version = headerContent;
		else if(headerName == "Sec-WebSocket-Key")
			key = headerContent;
	}

	if(version != "13")
	{
		std::cout << "Unsupported WebSocket version. Got: " << version << ", but we only support 13" << std::endl;
		exit(-1);
	}

	if(protocol != "shepoo")
	{
		std::cout << "Unsupported protocol. Got: " << protocol << ", but we only support shepoo" << std::endl;
		exit(-1);
	}

	SHA_CTX shaContext;
	unsigned char hash[SHA_DIGEST_LENGTH];

	SHA1_Init(&shaContext);
	SHA1_Update(&shaContext, key.c_str(), key.size());
	SHA1_Update(&shaContext, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", 36);
	SHA1_Final(hash, &shaContext);

	char acceptKey[30];
	bzero(acceptKey, 30);
	// hidden gem in resolv.h
	b64_ntop(hash, sizeof(hash), acceptKey, 29);

	std::string answer = std::string("HTTP/1.1 101 Switching Protocols\r\n\
Upgrade: websocket\r\n\
Connection: Upgrade\r\n\
Sec-WebSocket-Accept: ") + acceptKey + std::string("\r\n\
Sec-WebSocket-Protocol: shepoo\r\n\
\r\n");

	SSL_write(_ssl, answer.c_str(), answer.size());

	uint8_t tmp[3];
	SSL_read(_ssl, tmp, 3);

	handleWebSocketFrame();
}

void SecNet::handleWebSocketFrame()
{
	WebSocketFrameHeader header;
	bzero(&header, sizeof(header));
	SSL_read(_ssl, &header, sizeof(header));

	uint64_t payloadLength = header.payloadLen;

	if(payloadLength == 126)
	{
		uint16_t payloadLen16;
		SSL_read(_ssl, &payloadLen16, sizeof(payloadLen16));
		payloadLength = payloadLen16;
	}
	else if(payloadLength == 127)
	{
		uint64_t payloadLen64;
		SSL_read(_ssl, &payloadLen64, sizeof(payloadLen64));
		payloadLength = payloadLen64;
	}

	uint32_t maskingKey = 0;

	if(header.maskFlag)
	{
		SSL_read(_ssl, &maskingKey, sizeof(maskingKey));
	}

	uint8_t* payload = new uint8_t[payloadLength+1];
	payload[payloadLength] = 0;
	SSL_read(_ssl, payload, payloadLength);

	if(header.maskFlag)
	{
		// urghs ... it's masked ... WHY THE FUCK DO THEY DO THIS?! WE HAVE TLS!

		for(uint64_t i = 0; i < payloadLength; i++)
		{
			uint8_t masked = payload[i];
			int j = i % 4;
			uint8_t key = ((uint8_t*)&maskingKey)[j];
			uint8_t demasked = masked ^ key;

			payload[i] = demasked;
		}
	}
}

void SecNet::Initialize(std::string listenAddress, std::string certificateFile, std::string privateKeyFile, std::string dhParamFile, std::string tlsCipherList)
{
	SSL_library_init();
	SSL_load_error_strings();
	SSL_METHOD* serverMethod = (SSL_METHOD*)TLSv1_2_server_method();
	SSL_CTX* serverContext = SSL_CTX_new(serverMethod);

	if(!serverContext)
	{
		std::cout << "SSL_CTX_new() failed!" << std::endl;
		exit(-1);
	}

	int status = SSL_CTX_use_certificate_file(serverContext, certificateFile.c_str(), SSL_FILETYPE_PEM);

	if(status <= 0)
	{
		std::cout << "Could not load certificate-file!" << std::endl;
		exit(-1);
	}

	status = SSL_CTX_use_PrivateKey_file(serverContext, privateKeyFile.c_str(), SSL_FILETYPE_PEM);

	if(status <= 0)
	{
		std::cout << "Could not load private-key-file!" << std::endl;
		exit(-1);
	}

	if(!SSL_CTX_check_private_key(serverContext))
	{
		std::cout << "SSL-certificate and private-key don't belong together!" << std::endl;
		exit(-1);
	}

	long sslopt = SSL_OP_SINGLE_DH_USE;
	status = SSL_CTX_set_options(serverContext, sslopt);

	if(status <= 0)
	{
		std::cout << "Could not set SSL options!" << std::endl;
		exit(-1);
	}

	FILE* dhFile = fopen(dhParamFile.c_str(), "rb");
	DH* dh = PEM_read_DHparams(dhFile, NULL, NULL, NULL);
	fclose(dhFile);
	status = SSL_CTX_set_tmp_dh(serverContext, dh);

	if(status <= 0)
	{
		std::cout << "Could not load dhparam-file!" << std::endl;
		exit(-1);
	}

	if(tlsCipherList != "")
	{
		status = SSL_CTX_set_cipher_list(serverContext, tlsCipherList.c_str());

		if(status <= 0)
		{
			std::cout << "Could not set TLS cipher list!" << std::endl;
			exit(-1);
		}
	}

	_serverSocket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);

	if(_serverSocket == -1)
	{
		std::cout << "socket() failed!" << std::endl;
		exit(-1);
	}

	struct sockaddr_in6 address;
	bzero(&address, sizeof(address));
	address.sin6_family = AF_INET6;
	address.sin6_port = htons(SecNet::DefaultPort);
	address.sin6_addr = in6addr_any;

	status = bind(_serverSocket, (struct sockaddr*)&address, sizeof(address));

	if(status != 0)
	{
		std::cout << "bind() failed!" << std::endl;
		exit(-1);
	}

	status = listen(_serverSocket, SOMAXCONN);

	if(status != 0)
	{
		std::cout << "listen() failed!" << std::endl;
		exit(-1);
	}

	SecNet::_listening = true;

	ListenLoop(serverContext);
}

void SecNet::ListenLoop(SSL_CTX* serverContext)
{
	sockaddr_in6 clientAddress;
	socklen_t caLength = sizeof(clientAddress);

	while(SecNet::_listening)
	{
		int newSocket = accept(SecNet::_serverSocket, (struct sockaddr*)&clientAddress, &caLength);

		if(newSocket < 0)
		{
			std::cout << "Error accepting new connection: " << errno << std::endl;
		}
		else
		{
			__pid_t clientProcess = fork();

			if(clientProcess == 0)
			{
				// we are the new fork
				SecNet clientInstance(newSocket, serverContext);
				exit(-1);
			}
			else
			{
				close(newSocket); // we are the server process

				char* ip = new char[INET6_ADDRSTRLEN];
				getnameinfo((struct sockaddr*)&clientAddress, caLength, ip, INET6_ADDRSTRLEN, 0, 0, NI_NUMERICHOST);

				std::cout << "Forked for client-handling. Client-IP: " << ip << std::endl;
			}
		}
	}
}

int SecNet::sslErrorCallback(const char* message, size_t length, void* userData)
{
	std::cout << "SSL: " << message << std::endl;
	return 0;
}
<commit_msg>More cleaning up<commit_after>#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <resolv.h>

#include "../include/secnet.h"

// no sane human will use packets with more than 128MB payload
// at least not in the current century.
#define MAX_PACKET_PAYLOAD_SIZE	134217728

bool SecNet::_listening;
int SecNet::_serverSocket;
sigslot::signal3<SecNet::Packet, uint8_t*, SecNet*> SecNet::receivedPacket;

SecNet::SecNet(int socket, SSL_CTX* serverContext)
{
	close(SecNet::_serverSocket);

	_socket = socket;
	_ssl = SSL_new(serverContext);

	if(_ssl == 0)
	{
		std::cout << "Could not create new SSL connection!" << std::endl;
		exit(-1); // if the fork can't handle the client, there is no reason to allow it staying alive >:]
	}

	SSL_set_fd(_ssl, _socket);

	if(SSL_accept(_ssl) != 1)
	{
		std::cout << "SSL Handshake failed!" << std::endl;
		ERR_print_errors_cb(SecNet::sslErrorCallback, NULL);
		exit(-1); // still no reason to allow it staying alive >:]
	}

	// handle the connection
	Packet packet;
	bzero(&packet, sizeof(packet));
	SSL_read(_ssl, &packet, sizeof(packet));

	if(*(int*)&packet == 0x20544547) // don't hurt me .. it's just "GET " as integer
	{
		// meh .. It's HTTP, probably Websocket as we use this one ...
		handleWebSocket(&packet);
		return;
	}

	if(packet.payloadLength > MAX_PACKET_PAYLOAD_SIZE)
	{
		std::cout << "Client wanted to send data with payload size " << packet.payloadLength << " but we only handle a maximum of " << MAX_PACKET_PAYLOAD_SIZE << "." << std::endl;
		exit(-1); // nope. Just nope.
	}

	uint8_t* payload = new uint8_t[packet.payloadLength];
	SSL_read(_ssl, payload, packet.payloadLength);

	receivedPacket.emit(packet, payload, this);
}

SecNet::~SecNet()
{
	SSL_shutdown(_ssl);
	close(_socket);
	SSL_free(_ssl);

	exit(0);
}

void SecNet::sendPacket(Packet packet, uint8_t* payload)
{
	SSL_write(_ssl, &packet, sizeof(packet));
	SSL_write(_ssl, payload, packet.payloadLength);
}

void SecNet::handleWebSocket(Packet* packet)
{
	const int MaxHeaders = 15;

	char initialData[sizeof(Packet) + 1];
	bzero(initialData, sizeof(Packet) + 1);
	memcpy(initialData, packet, sizeof(Packet));

	std::string headerLines[MaxHeaders]; // we only support the first 15 headers
	headerLines[0] = "";

	int headers = 0;
	bool headerEnd = false;

	for(unsigned int i = 0; i < sizeof(Packet); i++)
	{
		char c = initialData[i];

		if(c != '\r' && c != '\n')
		{
			headerLines[headers] += c;
		}
		else if(c == '\r')
		{
			headers++;
			headerLines[headers] = "";
		}
	}

	while(!headerEnd && headers < MaxHeaders)
	{
		char c = 0;
		SSL_read(_ssl, &c, 1);

		if(c != '\r' && c != '\n')
		{
			headerLines[headers] += c;
		}
		else if(c == '\r')
		{
			if(headerLines[headers] == "")
			{
				headerEnd = true;
				headers--;
				break;
			}

			headers++;
			headerLines[headers] = "";
		}
	}

	std::string protocol, key, version;

	for(int i = 1; i < headers; i++)
	{
		std::string headerName = "";
		std::string headerContent = "";

		unsigned int j;
		for(j = 0; j < headerLines[i].size(); j++)
		{
			char c = headerLines[i].at(j);

			if(c != ':')
				headerName += c;
			else
				break;
		}

		j += 2; // skip : and the following space

		for(; j < headerLines[i].size(); j++)
		{
			char c = headerLines[i].at(j);
			headerContent += c;
		}

		if(headerName == "Sec-WebSocket-Protocol")
			protocol = headerContent;
		else if(headerName == "Sec-WebSocket-Version")
			version = headerContent;
		else if(headerName == "Sec-WebSocket-Key")
			key = headerContent;
	}

	if(version != "13")
	{
		std::cout << "Unsupported WebSocket version. Got: " << version << ", but we only support 13" << std::endl;
		exit(-1);
	}

	if(protocol != "shepoo")
	{
		std::cout << "Unsupported protocol. Got: " << protocol << ", but we only support shepoo" << std::endl;
		exit(-1);
	}

	SHA_CTX shaContext;
	unsigned char hash[SHA_DIGEST_LENGTH];

	SHA1_Init(&shaContext);
	SHA1_Update(&shaContext, key.c_str(), key.size());
	SHA1_Update(&shaContext, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", 36);
	SHA1_Final(hash, &shaContext);

	char acceptKey[30];
	bzero(acceptKey, 30);
	// hidden gem in resolv.h
	b64_ntop(hash, sizeof(hash), acceptKey, 29);

	std::string answer = std::string("HTTP/1.1 101 Switching Protocols\r\n\
Upgrade: websocket\r\n\
Connection: Upgrade\r\n\
Sec-WebSocket-Accept: ") + acceptKey + std::string("\r\n\
Sec-WebSocket-Protocol: shepoo\r\n\
\r\n");

	SSL_write(_ssl, answer.c_str(), answer.size());

	uint8_t tmp[3];
	SSL_read(_ssl, tmp, 3);

	handleWebSocketFrame();
}

void SecNet::handleWebSocketFrame()
{
	WebSocketFrameHeader header;
	bzero(&header, sizeof(header));
	SSL_read(_ssl, &header, sizeof(header));

	uint64_t payloadLength = header.payloadLen;

	if(payloadLength == 126)
	{
		uint16_t payloadLen16;
		SSL_read(_ssl, &payloadLen16, sizeof(payloadLen16));
		payloadLength = payloadLen16;
	}
	else if(payloadLength == 127)
	{
		uint64_t payloadLen64;
		SSL_read(_ssl, &payloadLen64, sizeof(payloadLen64));
		payloadLength = payloadLen64;
	}

	uint32_t maskingKey = 0;

	if(header.maskFlag)
	{
		SSL_read(_ssl, &maskingKey, sizeof(maskingKey));
	}

	uint8_t* payload = new uint8_t[payloadLength+1];
	payload[payloadLength] = 0;
	SSL_read(_ssl, payload, payloadLength);

	if(header.maskFlag)
	{
		// urghs ... it's masked ... WHY THE FUCK DO THEY DO THIS?! WE HAVE TLS!

		for(uint64_t i = 0; i < payloadLength; i++)
		{
			payload[i] = payload[i] ^ ((uint8_t*)&maskingKey)[i % 4];
		}
	}
}

void SecNet::Initialize(std::string listenAddress, std::string certificateFile, std::string privateKeyFile, std::string dhParamFile, std::string tlsCipherList)
{
	SSL_library_init();
	SSL_load_error_strings();
	SSL_METHOD* serverMethod = (SSL_METHOD*)TLSv1_2_server_method();
	SSL_CTX* serverContext = SSL_CTX_new(serverMethod);

	if(!serverContext)
	{
		std::cout << "SSL_CTX_new() failed!" << std::endl;
		exit(-1);
	}

	int status = SSL_CTX_use_certificate_file(serverContext, certificateFile.c_str(), SSL_FILETYPE_PEM);

	if(status <= 0)
	{
		std::cout << "Could not load certificate-file!" << std::endl;
		exit(-1);
	}

	status = SSL_CTX_use_PrivateKey_file(serverContext, privateKeyFile.c_str(), SSL_FILETYPE_PEM);

	if(status <= 0)
	{
		std::cout << "Could not load private-key-file!" << std::endl;
		exit(-1);
	}

	if(!SSL_CTX_check_private_key(serverContext))
	{
		std::cout << "SSL-certificate and private-key don't belong together!" << std::endl;
		exit(-1);
	}

	long sslopt = SSL_OP_SINGLE_DH_USE;
	status = SSL_CTX_set_options(serverContext, sslopt);

	if(status <= 0)
	{
		std::cout << "Could not set SSL options!" << std::endl;
		exit(-1);
	}

	FILE* dhFile = fopen(dhParamFile.c_str(), "rb");
	DH* dh = PEM_read_DHparams(dhFile, NULL, NULL, NULL);
	fclose(dhFile);
	status = SSL_CTX_set_tmp_dh(serverContext, dh);

	if(status <= 0)
	{
		std::cout << "Could not load dhparam-file!" << std::endl;
		exit(-1);
	}

	if(tlsCipherList != "")
	{
		status = SSL_CTX_set_cipher_list(serverContext, tlsCipherList.c_str());

		if(status <= 0)
		{
			std::cout << "Could not set TLS cipher list!" << std::endl;
			exit(-1);
		}
	}

	_serverSocket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);

	if(_serverSocket == -1)
	{
		std::cout << "socket() failed!" << std::endl;
		exit(-1);
	}

	struct sockaddr_in6 address;
	bzero(&address, sizeof(address));
	address.sin6_family = AF_INET6;
	address.sin6_port = htons(SecNet::DefaultPort);
	address.sin6_addr = in6addr_any;

	status = bind(_serverSocket, (struct sockaddr*)&address, sizeof(address));

	if(status != 0)
	{
		std::cout << "bind() failed!" << std::endl;
		exit(-1);
	}

	status = listen(_serverSocket, SOMAXCONN);

	if(status != 0)
	{
		std::cout << "listen() failed!" << std::endl;
		exit(-1);
	}

	SecNet::_listening = true;

	ListenLoop(serverContext);
}

void SecNet::ListenLoop(SSL_CTX* serverContext)
{
	sockaddr_in6 clientAddress;
	socklen_t caLength = sizeof(clientAddress);

	while(SecNet::_listening)
	{
		int newSocket = accept(SecNet::_serverSocket, (struct sockaddr*)&clientAddress, &caLength);

		if(newSocket < 0)
		{
			std::cout << "Error accepting new connection: " << errno << std::endl;
		}
		else
		{
			__pid_t clientProcess = fork();

			if(clientProcess == 0)
			{
				// we are the new fork
				SecNet clientInstance(newSocket, serverContext);
				exit(-1);
			}
			else
			{
				close(newSocket); // we are the server process

				char* ip = new char[INET6_ADDRSTRLEN];
				getnameinfo((struct sockaddr*)&clientAddress, caLength, ip, INET6_ADDRSTRLEN, 0, 0, NI_NUMERICHOST);

				std::cout << "Forked for client-handling. Client-IP: " << ip << std::endl;
			}
		}
	}
}

int SecNet::sslErrorCallback(const char* message, size_t length, void* userData)
{
	std::cout << "SSL: " << message << std::endl;
	return 0;
}
<|endoftext|>


<commit_before>#include "node_usb.h"
#include <string.h>

#define STRUCT_TO_V8(TARGET, STR, NAME) \
		TARGET->Set(V8STR(#NAME), NanNew<Uint32>((uint32_t) (STR).NAME), CONST_PROP);

#define CHECK_OPEN() \
		if (!self->device_handle){THROW_ERROR("Device is not opened");}

Handle<Object> makeBuffer(const unsigned char* ptr, unsigned length) {
	return NanNewBufferHandle((char*) ptr, (uint32_t) length);
}

Device::Device(libusb_device* d): device(d), device_handle(0) {
	libusb_ref_device(device);
	DEBUG_LOG("Created device %p", this);
}

Device::~Device(){
	DEBUG_LOG("Freed device %p", this);
	libusb_close(device_handle);
	libusb_unref_device(device);
}

// Map pinning each libusb_device to a particular V8 instance
std::map<libusb_device*, _NanWeakCallbackInfo<Value, libusb_device>*> Device::byPtr;

NAN_WEAK_CALLBACK(DeviceWeakCallback) {
	Device::unpin(data.GetParameter());
}

// Get a V8 instance for a libusb_device: either the existing one from the map,
// or create a new one and add it to the map.
Handle<Value> Device::get(libusb_device* dev){
	auto it = byPtr.find(dev);
	if (it != byPtr.end()){
		return NanNew(it->second->persistent);
	}else{
		Handle<Value> v = pDevice.create(new Device(dev));
		auto p = NanMakeWeakPersistent(v, dev, DeviceWeakCallback);
		byPtr.insert(std::make_pair(dev, p));
		return v;
	}
}

void Device::unpin(libusb_device* device) {
	byPtr.erase(device);
	DEBUG_LOG("Removed cached device %p", device);
}

static NAN_METHOD(deviceConstructor) {
	ENTER_CONSTRUCTOR_POINTER(pDevice, 1);

	args.This()->Set(V8SYM("busNumber"),
		NanNew<Uint32>((uint32_t) libusb_get_bus_number(self->device)), CONST_PROP);
	args.This()->Set(V8SYM("deviceAddress"),
		NanNew<Uint32>((uint32_t) libusb_get_device_address(self->device)), CONST_PROP);

	Local<Object> v8dd = NanNew<Object>();
	args.This()->Set(V8SYM("deviceDescriptor"), v8dd, CONST_PROP);

	struct libusb_device_descriptor dd;
	CHECK_USB(libusb_get_device_descriptor(self->device, &dd));

	STRUCT_TO_V8(v8dd, dd, bLength)
	STRUCT_TO_V8(v8dd, dd, bDescriptorType)
	STRUCT_TO_V8(v8dd, dd, bcdUSB)
	STRUCT_TO_V8(v8dd, dd, bDeviceClass)
	STRUCT_TO_V8(v8dd, dd, bDeviceSubClass)
	STRUCT_TO_V8(v8dd, dd, bDeviceProtocol)
	STRUCT_TO_V8(v8dd, dd, bMaxPacketSize0)
	STRUCT_TO_V8(v8dd, dd, idVendor)
	STRUCT_TO_V8(v8dd, dd, idProduct)
	STRUCT_TO_V8(v8dd, dd, bcdDevice)
	STRUCT_TO_V8(v8dd, dd, iManufacturer)
	STRUCT_TO_V8(v8dd, dd, iProduct)
	STRUCT_TO_V8(v8dd, dd, iSerialNumber)
	STRUCT_TO_V8(v8dd, dd, bNumConfigurations)

	NanReturnValue(args.This());
}

NAN_METHOD(Device_GetConfigDescriptor) {
	ENTER_METHOD(pDevice, 0);

	libusb_config_descriptor* cdesc;
	CHECK_USB(libusb_get_active_config_descriptor(self->device, &cdesc));

	Local<Object> v8cdesc = NanNew<Object>();

	STRUCT_TO_V8(v8cdesc, *cdesc, bLength)
	STRUCT_TO_V8(v8cdesc, *cdesc, bDescriptorType)
	STRUCT_TO_V8(v8cdesc, *cdesc, wTotalLength)
	STRUCT_TO_V8(v8cdesc, *cdesc, bNumInterfaces)
	STRUCT_TO_V8(v8cdesc, *cdesc, bConfigurationValue)
	STRUCT_TO_V8(v8cdesc, *cdesc, iConfiguration)
	STRUCT_TO_V8(v8cdesc, *cdesc, bmAttributes)
	// Libusb 1.0 typo'd bMaxPower as MaxPower
	v8cdesc->Set(V8STR("bMaxPower"), NanNew<Uint32>((uint32_t) cdesc->MaxPower), CONST_PROP);

	v8cdesc->Set(V8SYM("extra"), makeBuffer(cdesc->extra, cdesc->extra_length), CONST_PROP);

	Local<Array> v8interfaces = NanNew<Array>(cdesc->bNumInterfaces);
	v8cdesc->Set(V8SYM("interfaces"), v8interfaces);

	for (int idxInterface = 0; idxInterface < cdesc->bNumInterfaces; idxInterface++) {
		int numAltSettings = cdesc->interface[idxInterface].num_altsetting;

		Local<Array> v8altsettings = NanNew<Array>(numAltSettings);
		v8interfaces->Set(idxInterface, v8altsettings);

		for (int idxAltSetting = 0; idxAltSetting < numAltSettings; idxAltSetting++) {
			const libusb_interface_descriptor& idesc =
				cdesc->interface[idxInterface].altsetting[idxAltSetting];

			Local<Object> v8idesc = NanNew<Object>();
			v8altsettings->Set(idxAltSetting, v8idesc);

			STRUCT_TO_V8(v8idesc, idesc, bLength)
			STRUCT_TO_V8(v8idesc, idesc, bDescriptorType)
			STRUCT_TO_V8(v8idesc, idesc, bInterfaceNumber)
			STRUCT_TO_V8(v8idesc, idesc, bAlternateSetting)
			STRUCT_TO_V8(v8idesc, idesc, bNumEndpoints)
			STRUCT_TO_V8(v8idesc, idesc, bInterfaceClass)
			STRUCT_TO_V8(v8idesc, idesc, bInterfaceSubClass)
			STRUCT_TO_V8(v8idesc, idesc, bInterfaceProtocol)
			STRUCT_TO_V8(v8idesc, idesc, iInterface)

			v8idesc->Set(V8SYM("extra"), makeBuffer(idesc.extra, idesc.extra_length), CONST_PROP);

			Local<Array> v8endpoints = NanNew<Array>(idesc.bNumEndpoints);
			v8idesc->Set(V8SYM("endpoints"), v8endpoints, CONST_PROP);
			for (int idxEndpoint = 0; idxEndpoint < idesc.bNumEndpoints; idxEndpoint++){
				const libusb_endpoint_descriptor& edesc = idesc.endpoint[idxEndpoint];

				Local<Object> v8edesc = NanNew<Object>();
				v8endpoints->Set(idxEndpoint, v8edesc);

				STRUCT_TO_V8(v8edesc, edesc, bLength)
				STRUCT_TO_V8(v8edesc, edesc, bDescriptorType)
				STRUCT_TO_V8(v8edesc, edesc, bEndpointAddress)
				STRUCT_TO_V8(v8edesc, edesc, bmAttributes)
				STRUCT_TO_V8(v8edesc, edesc, wMaxPacketSize)
				STRUCT_TO_V8(v8edesc, edesc, bInterval)
				STRUCT_TO_V8(v8edesc, edesc, bRefresh)
				STRUCT_TO_V8(v8edesc, edesc, bSynchAddress)

				v8edesc->Set(V8SYM("extra"), makeBuffer(edesc.extra, edesc.extra_length), CONST_PROP);
			}
		}
	}

	libusb_free_config_descriptor(cdesc);
	NanReturnValue(v8cdesc);
}

NAN_METHOD(Device_Open) {
	ENTER_METHOD(pDevice, 0);
	if (!self->device_handle){
		CHECK_USB(libusb_open(self->device, &self->device_handle));
	}
	NanReturnValue(NanUndefined());
}

NAN_METHOD(Device_Close) {
	ENTER_METHOD(pDevice, 0);
	if (self->canClose()){
		libusb_close(self->device_handle);
		self->device_handle = NULL;
	}else{
		THROW_ERROR("Can't close device with a pending request");
	}
	NanReturnValue(NanUndefined());
}

struct Req{
	uv_work_t req;
	Device* device;
	Persistent<Function> callback;
	int errcode;

	void submit(Device* d, Handle<Function> cb, uv_work_cb backend, uv_work_cb after){
		NanAssignPersistent(callback, cb);
		device = d;
		device->ref();
		req.data = this;
		uv_queue_work(uv_default_loop(), &req, backend, (uv_after_work_cb) after);
	}

	static void default_after(uv_work_t *req){
		NanScope();
		auto baton = (Req*) req->data;

		auto device = NanObjectWrapHandle(baton->device);
		baton->device->unref();

		if (!NanNew(baton->callback).IsEmpty()) {
			Handle<Value> error = NanUndefined();
			if (baton->errcode < 0){
				error = libusbException(baton->errcode);
			}
			Handle<Value> argv[1] = {error};
			TryCatch try_catch;
			NanNew(baton->callback)->Call(device, 1, argv);
			if (try_catch.HasCaught()) {
				FatalException(try_catch);
			}
			NanDisposePersistent(baton->callback);
		}
		delete baton;
	}
};

struct Device_Reset: Req{
	static NAN_METHOD(begin) {
		ENTER_METHOD(pDevice, 0);
		CHECK_OPEN();
		CALLBACK_ARG(0);
		auto baton = new Device_Reset;
		baton->submit(self, callback, &backend, &default_after);
		NanReturnValue(NanUndefined());
	}

	static void backend(uv_work_t *req){
		auto baton = (Device_Reset*) req->data;
		baton->errcode = libusb_reset_device(baton->device->device_handle);
	}
};

NAN_METHOD(IsKernelDriverActive) {
	ENTER_METHOD(pDevice, 1);
	CHECK_OPEN();
	int interface;
	INT_ARG(interface, 0);
	int r = libusb_kernel_driver_active(self->device_handle, interface);
	CHECK_USB(r);
	NanReturnValue(NanNew<Boolean>(r));
}

NAN_METHOD(DetachKernelDriver) {
	ENTER_METHOD(pDevice, 1);
	CHECK_OPEN();
	int interface;
	INT_ARG(interface, 0);
	CHECK_USB(libusb_detach_kernel_driver(self->device_handle, interface));
	NanReturnValue(NanUndefined());
}

NAN_METHOD(AttachKernelDriver) {
	ENTER_METHOD(pDevice, 1);
	CHECK_OPEN();
	int interface;
	INT_ARG(interface, 0);
	CHECK_USB(libusb_attach_kernel_driver(self->device_handle, interface));
	NanReturnValue(NanUndefined());
}

NAN_METHOD(Device_ClaimInterface) {
	ENTER_METHOD(pDevice, 1);
	CHECK_OPEN();
	int interface;
	INT_ARG(interface, 0);
	CHECK_USB(libusb_claim_interface(self->device_handle, interface));
	NanReturnValue(NanUndefined());
}

struct Device_ReleaseInterface: Req{
	int interface;

	static NAN_METHOD(begin){
		ENTER_METHOD(pDevice, 1);
		CHECK_OPEN();
		int interface;
		INT_ARG(interface, 0);
		CALLBACK_ARG(1);
		auto baton = new Device_ReleaseInterface;
		baton->interface = interface;
		baton->submit(self, callback, &backend, &default_after);

		NanReturnValue(NanUndefined());
	}

	static void backend(uv_work_t *req){
		auto baton = (Device_ReleaseInterface*) req->data;
		baton->errcode = libusb_release_interface(baton->device->device_handle, baton->interface);
	}
};

struct Device_SetInterface: Req{
	int interface;
	int altsetting;

	static NAN_METHOD(begin){
		ENTER_METHOD(pDevice, 2);
		CHECK_OPEN();
		int interface, altsetting;
		INT_ARG(interface, 0);
		INT_ARG(altsetting, 1);
		CALLBACK_ARG(2);
		auto baton = new Device_SetInterface;
		baton->interface = interface;
		baton->altsetting = altsetting;
		baton->submit(self, callback, &backend, &default_after);
		NanReturnValue(NanUndefined());
	}

	static void backend(uv_work_t *req){
		auto baton = (Device_SetInterface*) req->data;
		baton->errcode = libusb_set_interface_alt_setting(
			baton->device->device_handle, baton->interface, baton->altsetting);
	}
};

NAN_METHOD(Device_Destroy) {
	ENTER_METHOD(pDevice, 0);

	// Remove from pin table
	Device::unpin(self->device);

	// Kill internal field (any future method calls will think this is not a Device object)
	NanSetInternalFieldPointer(args.This(), 0, NULL);

	// Free the device (which unrefs the libusb_device)
	delete self;

	NanReturnValue(NanUndefined());
}

static void init(Handle<Object> target){
	pDevice.init(&deviceConstructor);
	pDevice.addMethod("__getConfigDescriptor", Device_GetConfigDescriptor);
	pDevice.addMethod("__open", Device_Open);
	pDevice.addMethod("__close", Device_Close);
	pDevice.addMethod("reset", Device_Reset::begin);

	pDevice.addMethod("__claimInterface", Device_ClaimInterface);
	pDevice.addMethod("__releaseInterface", Device_ReleaseInterface::begin);
	pDevice.addMethod("__setInterface", Device_SetInterface::begin);

	pDevice.addMethod("__isKernelDriverActive", IsKernelDriverActive);
	pDevice.addMethod("__detachKernelDriver", DetachKernelDriver);
	pDevice.addMethod("__attachKernelDriver", AttachKernelDriver);

	pDevice.addMethod("__destroy", Device_Destroy);
}

Proto<Device> pDevice("Device", &init);
<commit_msg>Destroy __destroy<commit_after>#include "node_usb.h"
#include <string.h>

#define STRUCT_TO_V8(TARGET, STR, NAME) \
		TARGET->Set(V8STR(#NAME), NanNew<Uint32>((uint32_t) (STR).NAME), CONST_PROP);

#define CHECK_OPEN() \
		if (!self->device_handle){THROW_ERROR("Device is not opened");}

Handle<Object> makeBuffer(const unsigned char* ptr, unsigned length) {
	return NanNewBufferHandle((char*) ptr, (uint32_t) length);
}

Device::Device(libusb_device* d): device(d), device_handle(0) {
	libusb_ref_device(device);
	DEBUG_LOG("Created device %p", this);
}

Device::~Device(){
	DEBUG_LOG("Freed device %p", this);
	libusb_close(device_handle);
	libusb_unref_device(device);
}

// Map pinning each libusb_device to a particular V8 instance
std::map<libusb_device*, _NanWeakCallbackInfo<Value, libusb_device>*> Device::byPtr;

NAN_WEAK_CALLBACK(DeviceWeakCallback) {
	Device::unpin(data.GetParameter());
}

// Get a V8 instance for a libusb_device: either the existing one from the map,
// or create a new one and add it to the map.
Handle<Value> Device::get(libusb_device* dev){
	auto it = byPtr.find(dev);
	if (it != byPtr.end()){
		return NanNew(it->second->persistent);
	}else{
		Handle<Value> v = pDevice.create(new Device(dev));
		auto p = NanMakeWeakPersistent(v, dev, DeviceWeakCallback);
		byPtr.insert(std::make_pair(dev, p));
		return v;
	}
}

void Device::unpin(libusb_device* device) {
	byPtr.erase(device);
	DEBUG_LOG("Removed cached device %p", device);
}

static NAN_METHOD(deviceConstructor) {
	ENTER_CONSTRUCTOR_POINTER(pDevice, 1);

	args.This()->Set(V8SYM("busNumber"),
		NanNew<Uint32>((uint32_t) libusb_get_bus_number(self->device)), CONST_PROP);
	args.This()->Set(V8SYM("deviceAddress"),
		NanNew<Uint32>((uint32_t) libusb_get_device_address(self->device)), CONST_PROP);

	Local<Object> v8dd = NanNew<Object>();
	args.This()->Set(V8SYM("deviceDescriptor"), v8dd, CONST_PROP);

	struct libusb_device_descriptor dd;
	CHECK_USB(libusb_get_device_descriptor(self->device, &dd));

	STRUCT_TO_V8(v8dd, dd, bLength)
	STRUCT_TO_V8(v8dd, dd, bDescriptorType)
	STRUCT_TO_V8(v8dd, dd, bcdUSB)
	STRUCT_TO_V8(v8dd, dd, bDeviceClass)
	STRUCT_TO_V8(v8dd, dd, bDeviceSubClass)
	STRUCT_TO_V8(v8dd, dd, bDeviceProtocol)
	STRUCT_TO_V8(v8dd, dd, bMaxPacketSize0)
	STRUCT_TO_V8(v8dd, dd, idVendor)
	STRUCT_TO_V8(v8dd, dd, idProduct)
	STRUCT_TO_V8(v8dd, dd, bcdDevice)
	STRUCT_TO_V8(v8dd, dd, iManufacturer)
	STRUCT_TO_V8(v8dd, dd, iProduct)
	STRUCT_TO_V8(v8dd, dd, iSerialNumber)
	STRUCT_TO_V8(v8dd, dd, bNumConfigurations)

	NanReturnValue(args.This());
}

NAN_METHOD(Device_GetConfigDescriptor) {
	ENTER_METHOD(pDevice, 0);

	libusb_config_descriptor* cdesc;
	CHECK_USB(libusb_get_active_config_descriptor(self->device, &cdesc));

	Local<Object> v8cdesc = NanNew<Object>();

	STRUCT_TO_V8(v8cdesc, *cdesc, bLength)
	STRUCT_TO_V8(v8cdesc, *cdesc, bDescriptorType)
	STRUCT_TO_V8(v8cdesc, *cdesc, wTotalLength)
	STRUCT_TO_V8(v8cdesc, *cdesc, bNumInterfaces)
	STRUCT_TO_V8(v8cdesc, *cdesc, bConfigurationValue)
	STRUCT_TO_V8(v8cdesc, *cdesc, iConfiguration)
	STRUCT_TO_V8(v8cdesc, *cdesc, bmAttributes)
	// Libusb 1.0 typo'd bMaxPower as MaxPower
	v8cdesc->Set(V8STR("bMaxPower"), NanNew<Uint32>((uint32_t) cdesc->MaxPower), CONST_PROP);

	v8cdesc->Set(V8SYM("extra"), makeBuffer(cdesc->extra, cdesc->extra_length), CONST_PROP);

	Local<Array> v8interfaces = NanNew<Array>(cdesc->bNumInterfaces);
	v8cdesc->Set(V8SYM("interfaces"), v8interfaces);

	for (int idxInterface = 0; idxInterface < cdesc->bNumInterfaces; idxInterface++) {
		int numAltSettings = cdesc->interface[idxInterface].num_altsetting;

		Local<Array> v8altsettings = NanNew<Array>(numAltSettings);
		v8interfaces->Set(idxInterface, v8altsettings);

		for (int idxAltSetting = 0; idxAltSetting < numAltSettings; idxAltSetting++) {
			const libusb_interface_descriptor& idesc =
				cdesc->interface[idxInterface].altsetting[idxAltSetting];

			Local<Object> v8idesc = NanNew<Object>();
			v8altsettings->Set(idxAltSetting, v8idesc);

			STRUCT_TO_V8(v8idesc, idesc, bLength)
			STRUCT_TO_V8(v8idesc, idesc, bDescriptorType)
			STRUCT_TO_V8(v8idesc, idesc, bInterfaceNumber)
			STRUCT_TO_V8(v8idesc, idesc, bAlternateSetting)
			STRUCT_TO_V8(v8idesc, idesc, bNumEndpoints)
			STRUCT_TO_V8(v8idesc, idesc, bInterfaceClass)
			STRUCT_TO_V8(v8idesc, idesc, bInterfaceSubClass)
			STRUCT_TO_V8(v8idesc, idesc, bInterfaceProtocol)
			STRUCT_TO_V8(v8idesc, idesc, iInterface)

			v8idesc->Set(V8SYM("extra"), makeBuffer(idesc.extra, idesc.extra_length), CONST_PROP);

			Local<Array> v8endpoints = NanNew<Array>(idesc.bNumEndpoints);
			v8idesc->Set(V8SYM("endpoints"), v8endpoints, CONST_PROP);
			for (int idxEndpoint = 0; idxEndpoint < idesc.bNumEndpoints; idxEndpoint++){
				const libusb_endpoint_descriptor& edesc = idesc.endpoint[idxEndpoint];

				Local<Object> v8edesc = NanNew<Object>();
				v8endpoints->Set(idxEndpoint, v8edesc);

				STRUCT_TO_V8(v8edesc, edesc, bLength)
				STRUCT_TO_V8(v8edesc, edesc, bDescriptorType)
				STRUCT_TO_V8(v8edesc, edesc, bEndpointAddress)
				STRUCT_TO_V8(v8edesc, edesc, bmAttributes)
				STRUCT_TO_V8(v8edesc, edesc, wMaxPacketSize)
				STRUCT_TO_V8(v8edesc, edesc, bInterval)
				STRUCT_TO_V8(v8edesc, edesc, bRefresh)
				STRUCT_TO_V8(v8edesc, edesc, bSynchAddress)

				v8edesc->Set(V8SYM("extra"), makeBuffer(edesc.extra, edesc.extra_length), CONST_PROP);
			}
		}
	}

	libusb_free_config_descriptor(cdesc);
	NanReturnValue(v8cdesc);
}

NAN_METHOD(Device_Open) {
	ENTER_METHOD(pDevice, 0);
	if (!self->device_handle){
		CHECK_USB(libusb_open(self->device, &self->device_handle));
	}
	NanReturnValue(NanUndefined());
}

NAN_METHOD(Device_Close) {
	ENTER_METHOD(pDevice, 0);
	if (self->canClose()){
		libusb_close(self->device_handle);
		self->device_handle = NULL;
	}else{
		THROW_ERROR("Can't close device with a pending request");
	}
	NanReturnValue(NanUndefined());
}

struct Req{
	uv_work_t req;
	Device* device;
	Persistent<Function> callback;
	int errcode;

	void submit(Device* d, Handle<Function> cb, uv_work_cb backend, uv_work_cb after){
		NanAssignPersistent(callback, cb);
		device = d;
		device->ref();
		req.data = this;
		uv_queue_work(uv_default_loop(), &req, backend, (uv_after_work_cb) after);
	}

	static void default_after(uv_work_t *req){
		NanScope();
		auto baton = (Req*) req->data;

		auto device = NanObjectWrapHandle(baton->device);
		baton->device->unref();

		if (!NanNew(baton->callback).IsEmpty()) {
			Handle<Value> error = NanUndefined();
			if (baton->errcode < 0){
				error = libusbException(baton->errcode);
			}
			Handle<Value> argv[1] = {error};
			TryCatch try_catch;
			NanNew(baton->callback)->Call(device, 1, argv);
			if (try_catch.HasCaught()) {
				FatalException(try_catch);
			}
			NanDisposePersistent(baton->callback);
		}
		delete baton;
	}
};

struct Device_Reset: Req{
	static NAN_METHOD(begin) {
		ENTER_METHOD(pDevice, 0);
		CHECK_OPEN();
		CALLBACK_ARG(0);
		auto baton = new Device_Reset;
		baton->submit(self, callback, &backend, &default_after);
		NanReturnValue(NanUndefined());
	}

	static void backend(uv_work_t *req){
		auto baton = (Device_Reset*) req->data;
		baton->errcode = libusb_reset_device(baton->device->device_handle);
	}
};

NAN_METHOD(IsKernelDriverActive) {
	ENTER_METHOD(pDevice, 1);
	CHECK_OPEN();
	int interface;
	INT_ARG(interface, 0);
	int r = libusb_kernel_driver_active(self->device_handle, interface);
	CHECK_USB(r);
	NanReturnValue(NanNew<Boolean>(r));
}

NAN_METHOD(DetachKernelDriver) {
	ENTER_METHOD(pDevice, 1);
	CHECK_OPEN();
	int interface;
	INT_ARG(interface, 0);
	CHECK_USB(libusb_detach_kernel_driver(self->device_handle, interface));
	NanReturnValue(NanUndefined());
}

NAN_METHOD(AttachKernelDriver) {
	ENTER_METHOD(pDevice, 1);
	CHECK_OPEN();
	int interface;
	INT_ARG(interface, 0);
	CHECK_USB(libusb_attach_kernel_driver(self->device_handle, interface));
	NanReturnValue(NanUndefined());
}

NAN_METHOD(Device_ClaimInterface) {
	ENTER_METHOD(pDevice, 1);
	CHECK_OPEN();
	int interface;
	INT_ARG(interface, 0);
	CHECK_USB(libusb_claim_interface(self->device_handle, interface));
	NanReturnValue(NanUndefined());
}

struct Device_ReleaseInterface: Req{
	int interface;

	static NAN_METHOD(begin){
		ENTER_METHOD(pDevice, 1);
		CHECK_OPEN();
		int interface;
		INT_ARG(interface, 0);
		CALLBACK_ARG(1);
		auto baton = new Device_ReleaseInterface;
		baton->interface = interface;
		baton->submit(self, callback, &backend, &default_after);

		NanReturnValue(NanUndefined());
	}

	static void backend(uv_work_t *req){
		auto baton = (Device_ReleaseInterface*) req->data;
		baton->errcode = libusb_release_interface(baton->device->device_handle, baton->interface);
	}
};

struct Device_SetInterface: Req{
	int interface;
	int altsetting;

	static NAN_METHOD(begin){
		ENTER_METHOD(pDevice, 2);
		CHECK_OPEN();
		int interface, altsetting;
		INT_ARG(interface, 0);
		INT_ARG(altsetting, 1);
		CALLBACK_ARG(2);
		auto baton = new Device_SetInterface;
		baton->interface = interface;
		baton->altsetting = altsetting;
		baton->submit(self, callback, &backend, &default_after);
		NanReturnValue(NanUndefined());
	}

	static void backend(uv_work_t *req){
		auto baton = (Device_SetInterface*) req->data;
		baton->errcode = libusb_set_interface_alt_setting(
			baton->device->device_handle, baton->interface, baton->altsetting);
	}
};

static void init(Handle<Object> target){
	pDevice.init(&deviceConstructor);
	pDevice.addMethod("__getConfigDescriptor", Device_GetConfigDescriptor);
	pDevice.addMethod("__open", Device_Open);
	pDevice.addMethod("__close", Device_Close);
	pDevice.addMethod("reset", Device_Reset::begin);

	pDevice.addMethod("__claimInterface", Device_ClaimInterface);
	pDevice.addMethod("__releaseInterface", Device_ReleaseInterface::begin);
	pDevice.addMethod("__setInterface", Device_SetInterface::begin);

	pDevice.addMethod("__isKernelDriverActive", IsKernelDriverActive);
	pDevice.addMethod("__detachKernelDriver", DetachKernelDriver);
	pDevice.addMethod("__attachKernelDriver", AttachKernelDriver);
}

Proto<Device> pDevice("Device", &init);
<|endoftext|>


<commit_before>#include <loom/differ.hpp>

namespace loom
{

Differ::Differ (
        const protobuf::Config::Sparsify & config,
        const ValueSchema & schema) :
    config_(config),
    schema_(schema),
    unobserved_(get_unobserved(schema)),
    row_count_(0),
    booleans_(schema.booleans_size),
    counts_(schema.counts_size),
    tare_()
{
    LOOM_ASSERT(config.run(), "sparsify is not configured to run");

    tare_.mutable_observed()->set_sparsity(ProductValue::Observed::NONE);
    schema.normalize_dense(tare_);
}

void Differ::add_rows (const char * rows_in)
{
    protobuf::InFile rows(rows_in);
    protobuf::Row row;
    while (rows.try_read_stream(row)) {
        LOOM_ASSERT(not row.has_diff(), "row is already sparsified");
        const auto & value = row.data();
        LOOM_ASSERT_EQ(
            value.observed().sparsity(),
            ProductValue::Observed::DENSE);

        auto observed = value.observed().dense().begin();
        for (size_t i = 0; i < schema_.booleans_size; ++i) {
            if (*observed++) {
                booleans_[i].add(value.booleans(i));
            }
        }
        for (size_t i = 0; i < schema_.counts_size; ++i) {
            if (*observed++) {
                counts_[i].add(value.counts(i));
            }
        }
        ++row_count_;
    }

    make_tare();
}

void Differ::make_tare ()
{
    tare_.Clear();
    tare_.mutable_observed()->set_sparsity(ProductValue::Observed::DENSE);

    make_tare_type(booleans_, * tare_.mutable_booleans());
    make_tare_type(counts_, * tare_.mutable_counts());
}

void Differ::sparsify_rows (
    const char * absolute_rows_in,
    const char * relative_rows_out) const
{
    protobuf::InFile absolute_rows(absolute_rows_in);
    if (absolute_rows.is_file()) {
        LOOM_ASSERT(
            std::string(absolute_rows_in) != std::string(relative_rows_out),
            "in-place sparsify is not supported");
    }
    protobuf::OutFile relative_rows(relative_rows_out);
    protobuf::Row abs;
    protobuf::Row rel;
    while (absolute_rows.try_read_stream(abs)) {
        rel.set_id(abs.id());
        auto & pos = * rel.mutable_data();
        auto & neg = * rel.mutable_diff();
        absolute_to_relative(abs.data(), pos, neg);
        relative_rows.write_stream(rel);
    }
}

protobuf::ProductValue::Observed Differ::get_unobserved (
        const ValueSchema & schema)
{
    protobuf::ProductValue::Observed unobserved;
    unobserved.set_sparsity(ProductValue::Observed::DENSE);
    for (size_t i = 0; i < schema.total_size(); ++i) {
        unobserved.add_dense(false);
    }
    return unobserved;
}

template<class Summaries, class Values>
inline void Differ::make_tare_type (
        const Summaries & summaries,
        Values & values)
{
    const float count_threshold = config_.tare_threshold() * row_count_;
    for (const auto & summary : summaries) {
        const auto mode = summary.get_mode();
        bool is_dense = (summary.get_count(mode) > count_threshold);
        tare_.mutable_observed()->add_dense(is_dense);
        if (is_dense) {
            values.Add(mode);
        }
    }
}

template<class T>
inline void Differ::_abs_to_rel (
        const ProductValue & abs,
        ProductValue & pos,
        ProductValue & neg,
        size_t begin,
        size_t end) const
{
    const auto & tare_dense = tare_.observed().dense();
    const auto & abs_dense = abs.observed().dense();
    auto & pos_dense = * pos.mutable_observed()->mutable_dense();
    auto & neg_dense = * neg.mutable_observed()->mutable_dense();

    const auto & tare_values = protobuf::Fields<T>::get(tare_);
    const auto & abs_values = protobuf::Fields<T>::get(abs);
    auto & pos_values = protobuf::Fields<T>::get(pos);
    auto & neg_values = protobuf::Fields<T>::get(neg);

    size_t tare_pos = 0;
    size_t abs_pos = 0;
    for (size_t i = begin; i < end; ++i) {
        const bool tare_observed = tare_dense.Get(i);
        const bool abs_observed = abs_dense.Get(i);
        if (tare_observed) {
            const auto tare_value = tare_values.Get(tare_pos++);
            if (LOOM_LIKELY(abs_observed)) {
                const auto abs_value = abs_values.Get(abs_pos++);
                if (LOOM_UNLIKELY(abs_value != tare_value)) {
                    pos_dense.Set(i, true);
                    pos_values.Add(abs_value);
                    neg_dense.Set(i, true);
                    neg_values.Add(tare_value);
                }
            } else {
                neg_dense.Set(i, true);
                neg_values.Add(tare_value);
            }
        } else {
            if (LOOM_UNLIKELY(abs_observed)) {
                const auto abs_value = abs_values.Get(abs_pos++);
                pos_dense.Set(i, true);
                pos_values.Add(abs_value);
            }
        }
    }
}

void Differ::absolute_to_relative (
        const ProductValue & abs,
        ProductValue & pos,
        ProductValue & neg) const
{
    LOOM_ASSERT_EQ(abs.observed().sparsity(), ProductValue::Observed::DENSE);

    pos.Clear();
    neg.Clear();

    * pos.mutable_observed() = unobserved_;
    * neg.mutable_observed() = unobserved_;

    size_t begin = 0;
    size_t end = schema_.booleans_size;
    _abs_to_rel<bool>(abs, pos, neg, begin, end);

    begin = end;
    end += schema_.counts_size;
    _abs_to_rel<uint32_t>(abs, pos, neg, begin, end);

    if (LOOM_DEBUG_LEVEL >= 3) {
        ProductValue expected;
        absolute_to_relative(expected, pos, neg);
        LOOM_ASSERT_EQ(abs, expected);
    }

    const float sparse_threshold = config_.sparse_threshold();
    schema_.normalize_small(pos, sparse_threshold);
    schema_.normalize_small(neg, sparse_threshold);
}

template<class T>
inline void Differ::_rel_to_abs (
        ProductValue & abs,
        const ProductValue & pos,
        const ProductValue & neg,
        size_t begin,
        size_t end) const
{
    const auto & tare_dense = tare_.observed().dense();
    auto & abs_dense = * abs.mutable_observed()->mutable_dense();
    const auto & pos_dense = pos.observed().dense();
    const auto & neg_dense = neg.observed().dense();

    const auto & tare_values = protobuf::Fields<T>::get(tare_);
    auto & abs_values = protobuf::Fields<T>::get(abs);
    const auto & pos_values = protobuf::Fields<T>::get(pos);

    size_t tare_pos = 0;
    size_t pos_pos = 0;
    for (size_t i = begin; i < end; ++i) {
        const bool tare_observed = tare_dense.Get(i);
        const bool pos_observed = pos_dense.Get(i);
        if (pos_observed) {
            const auto pos_value = pos_values.Get(pos_pos++);
            abs_dense.Set(i, true);
            abs_values.Add(pos_value);
        }
        if (tare_observed) {
            const auto tare_value = tare_values.Get(tare_pos++);
            if (not neg_dense.Get(i)) {
                LOOM_ASSERT1(not pos_observed, "tare, pos, and neg disagree");
                abs_dense.Set(i, true);
                abs_values.Add(tare_value);
            }
        }
    }
}

void Differ::relative_to_absolute (
        ProductValue & abs,
        const ProductValue & pos,
        const ProductValue & neg) const
{
    // FIXME allow pos & neg to have any sparsity type
    LOOM_ASSERT_EQ(pos.observed().sparsity(), ProductValue::Observed::DENSE);
    LOOM_ASSERT_EQ(neg.observed().sparsity(), ProductValue::Observed::DENSE);

    abs.Clear();
    * abs.mutable_observed() = unobserved_;

    size_t begin = 0;
    size_t end = schema_.booleans_size;
    _rel_to_abs<bool>(abs, pos, neg, begin, end);

    begin = end;
    end += schema_.counts_size;
    _rel_to_abs<uint32_t>(abs, pos, neg, begin, end);
}

} // namespace loom
<commit_msg>Fix assertion in absolute_to_relative<commit_after>#include <loom/differ.hpp>

namespace loom
{

Differ::Differ (
        const protobuf::Config::Sparsify & config,
        const ValueSchema & schema) :
    config_(config),
    schema_(schema),
    unobserved_(get_unobserved(schema)),
    row_count_(0),
    booleans_(schema.booleans_size),
    counts_(schema.counts_size),
    tare_()
{
    LOOM_ASSERT(config.run(), "sparsify is not configured to run");

    tare_.mutable_observed()->set_sparsity(ProductValue::Observed::NONE);
    schema.normalize_dense(tare_);
}

void Differ::add_rows (const char * rows_in)
{
    protobuf::InFile rows(rows_in);
    protobuf::Row row;
    while (rows.try_read_stream(row)) {
        LOOM_ASSERT(not row.has_diff(), "row is already sparsified");
        const auto & value = row.data();
        LOOM_ASSERT_EQ(
            value.observed().sparsity(),
            ProductValue::Observed::DENSE);

        auto observed = value.observed().dense().begin();
        for (size_t i = 0; i < schema_.booleans_size; ++i) {
            if (*observed++) {
                booleans_[i].add(value.booleans(i));
            }
        }
        for (size_t i = 0; i < schema_.counts_size; ++i) {
            if (*observed++) {
                counts_[i].add(value.counts(i));
            }
        }
        ++row_count_;
    }

    make_tare();
}

void Differ::make_tare ()
{
    tare_.Clear();
    tare_.mutable_observed()->set_sparsity(ProductValue::Observed::DENSE);

    make_tare_type(booleans_, * tare_.mutable_booleans());
    make_tare_type(counts_, * tare_.mutable_counts());
}

void Differ::sparsify_rows (
    const char * absolute_rows_in,
    const char * relative_rows_out) const
{
    protobuf::InFile absolute_rows(absolute_rows_in);
    if (absolute_rows.is_file()) {
        LOOM_ASSERT(
            std::string(absolute_rows_in) != std::string(relative_rows_out),
            "in-place sparsify is not supported");
    }
    protobuf::OutFile relative_rows(relative_rows_out);
    protobuf::Row abs;
    protobuf::Row rel;
    while (absolute_rows.try_read_stream(abs)) {
        rel.set_id(abs.id());
        auto & pos = * rel.mutable_data();
        auto & neg = * rel.mutable_diff();
        absolute_to_relative(abs.data(), pos, neg);
        relative_rows.write_stream(rel);
    }
}

protobuf::ProductValue::Observed Differ::get_unobserved (
        const ValueSchema & schema)
{
    protobuf::ProductValue::Observed unobserved;
    unobserved.set_sparsity(ProductValue::Observed::DENSE);
    for (size_t i = 0; i < schema.total_size(); ++i) {
        unobserved.add_dense(false);
    }
    return unobserved;
}

template<class Summaries, class Values>
inline void Differ::make_tare_type (
        const Summaries & summaries,
        Values & values)
{
    const float count_threshold = config_.tare_threshold() * row_count_;
    for (const auto & summary : summaries) {
        const auto mode = summary.get_mode();
        bool is_dense = (summary.get_count(mode) > count_threshold);
        tare_.mutable_observed()->add_dense(is_dense);
        if (is_dense) {
            values.Add(mode);
        }
    }
}

template<class T>
inline void Differ::_abs_to_rel (
        const ProductValue & abs,
        ProductValue & pos,
        ProductValue & neg,
        size_t begin,
        size_t end) const
{
    const auto & tare_dense = tare_.observed().dense();
    const auto & abs_dense = abs.observed().dense();
    auto & pos_dense = * pos.mutable_observed()->mutable_dense();
    auto & neg_dense = * neg.mutable_observed()->mutable_dense();

    const auto & tare_values = protobuf::Fields<T>::get(tare_);
    const auto & abs_values = protobuf::Fields<T>::get(abs);
    auto & pos_values = protobuf::Fields<T>::get(pos);
    auto & neg_values = protobuf::Fields<T>::get(neg);

    size_t tare_pos = 0;
    size_t abs_pos = 0;
    for (size_t i = begin; i < end; ++i) {
        const bool tare_observed = tare_dense.Get(i);
        const bool abs_observed = abs_dense.Get(i);
        if (tare_observed) {
            const auto tare_value = tare_values.Get(tare_pos++);
            if (LOOM_LIKELY(abs_observed)) {
                const auto abs_value = abs_values.Get(abs_pos++);
                if (LOOM_UNLIKELY(abs_value != tare_value)) {
                    pos_dense.Set(i, true);
                    pos_values.Add(abs_value);
                    neg_dense.Set(i, true);
                    neg_values.Add(tare_value);
                }
            } else {
                neg_dense.Set(i, true);
                neg_values.Add(tare_value);
            }
        } else {
            if (LOOM_UNLIKELY(abs_observed)) {
                const auto abs_value = abs_values.Get(abs_pos++);
                pos_dense.Set(i, true);
                pos_values.Add(abs_value);
            }
        }
    }
}

void Differ::absolute_to_relative (
        const ProductValue & abs,
        ProductValue & pos,
        ProductValue & neg) const
{
    LOOM_ASSERT_EQ(abs.observed().sparsity(), ProductValue::Observed::DENSE);

    pos.Clear();
    neg.Clear();

    * pos.mutable_observed() = unobserved_;
    * neg.mutable_observed() = unobserved_;

    size_t begin = 0;
    size_t end = schema_.booleans_size;
    _abs_to_rel<bool>(abs, pos, neg, begin, end);

    begin = end;
    end += schema_.counts_size;
    _abs_to_rel<uint32_t>(abs, pos, neg, begin, end);

    if (LOOM_DEBUG_LEVEL >= 3) {
        ProductValue abs_to_rel_to_abs;
        relative_to_absolute(abs_to_rel_to_abs, pos, neg);
        LOOM_ASSERT_EQ(abs_to_rel_to_abs, abs);
    }

    const float sparse_threshold = config_.sparse_threshold();
    schema_.normalize_small(pos, sparse_threshold);
    schema_.normalize_small(neg, sparse_threshold);
}

template<class T>
inline void Differ::_rel_to_abs (
        ProductValue & abs,
        const ProductValue & pos,
        const ProductValue & neg,
        size_t begin,
        size_t end) const
{
    const auto & tare_dense = tare_.observed().dense();
    auto & abs_dense = * abs.mutable_observed()->mutable_dense();
    const auto & pos_dense = pos.observed().dense();
    const auto & neg_dense = neg.observed().dense();

    const auto & tare_values = protobuf::Fields<T>::get(tare_);
    auto & abs_values = protobuf::Fields<T>::get(abs);
    const auto & pos_values = protobuf::Fields<T>::get(pos);

    size_t tare_pos = 0;
    size_t pos_pos = 0;
    for (size_t i = begin; i < end; ++i) {
        const bool tare_observed = tare_dense.Get(i);
        const bool pos_observed = pos_dense.Get(i);
        if (pos_observed) {
            const auto pos_value = pos_values.Get(pos_pos++);
            abs_dense.Set(i, true);
            abs_values.Add(pos_value);
        }
        if (tare_observed) {
            const auto tare_value = tare_values.Get(tare_pos++);
            if (not neg_dense.Get(i)) {
                LOOM_ASSERT1(not pos_observed, "tare, pos, and neg disagree");
                abs_dense.Set(i, true);
                abs_values.Add(tare_value);
            }
        }
    }
}

void Differ::relative_to_absolute (
        ProductValue & abs,
        const ProductValue & pos,
        const ProductValue & neg) const
{
    // FIXME allow pos & neg to have any sparsity type
    LOOM_ASSERT_EQ(pos.observed().sparsity(), ProductValue::Observed::DENSE);
    LOOM_ASSERT_EQ(neg.observed().sparsity(), ProductValue::Observed::DENSE);

    abs.Clear();
    * abs.mutable_observed() = unobserved_;

    size_t begin = 0;
    size_t end = schema_.booleans_size;
    _rel_to_abs<bool>(abs, pos, neg, begin, end);

    begin = end;
    end += schema_.counts_size;
    _rel_to_abs<uint32_t>(abs, pos, neg, begin, end);
}

} // namespace loom
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2003 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* no header other than VotingArbiter.h should be included here */

#ifdef _WIN32
#pragma warning( 4:4786)
#endif

#include "VotingArbiter.h"

/* private */

/* protected */

void VotingArbiter::updatePollers(void)
{
  while (!_pollers.empty()) {
    poller_t& p = _pollers.front();
    if ((TimeKeeper::getCurrent() - p.lastRequest) > _voteRepeatTime) {
      // remove pollers that expired their repoll timeout
      _pollers.pop_front();
    } else {
      break;
    }
  }
  return;
}

bool VotingArbiter::isPollerWaiting(std::string name) const
{
  for (unsigned int i = 0; i < _pollers.size(); i++) {
    if (_pollers[i].name == name) {
      return true;
    }
  }
  return false;
}

/* public */

bool VotingArbiter::forgetPoll(void)
{
  if (_votingBooth != NULL) {
    delete _votingBooth;
    _votingBooth = NULL;
  }
  _startTime = TimeKeeper::getNullTime();
  _pollee = "nobody";
  _polleeIP = "";
  _action = UNDEFINED;
  _pollRequestor = "nobody";
  return true;
}

bool VotingArbiter::poll(std::string player, std::string playerRequesting, pollAction_t action, std::string playerIP)
{
  poller_t p;
  char message[256];
  bool tooSoon;

  // you have to forget the current poll before another can be spawned
  if (this->isPollOpen()) {
    std::cout << "/poll was called and a poll is already open?" << std::endl;
    return false;
  }

  // update the poller list (people on the pollers list cannot initiate a poll
  updatePollers();

  // see if the poller is in the list
  tooSoon = isPollerWaiting(playerRequesting);
  if (tooSoon) {
    std::cout << "/poll was called and the requesting poller already asked?? [" << playerRequesting << "]" << std::endl;
    return false;
  }

  // add this poller to the end list
  p.name = playerRequesting;
  p.lastRequest = TimeKeeper::getCurrent();
  _pollers.push_back(p);

  // create the booth to record votes
#ifdef _WIN32
  _snprintf(message, 256,  "%s %s", action == POLL_KICK_PLAYER ? "kick" : "ban", player.c_str());
#else
  snprintf(message, 256,  "%s %s", action == POLL_KICK_PLAYER ? "kick" : "ban", player.c_str());
#endif
  if (_votingBooth != NULL) {
    delete _votingBooth;
  }
  _votingBooth = YesNoVotingBooth(message);
  _pollee = player;
  _polleeIP = playerIP;
  _action = action;
  _pollRequestor = playerRequesting;

  // set timers
  _startTime = TimeKeeper::getCurrent();

  return true;
}

bool VotingArbiter::pollToKick(std::string player, std::string playerRequesting)
{
  return (this->poll(player, playerRequesting, POLL_KICK_PLAYER));
}

bool VotingArbiter::pollToBan(std::string player, std::string playerRequesting, std::string playerIP)
{
  return (this->poll(player, playerRequesting, POLL_BAN_PLAYER, playerIP));
}

bool VotingArbiter::closePoll(void)
{
  if (this->isPollClosed()) {
    return true;
  }
  _startTime = TimeKeeper::getSunExplodeTime();
  return true;
}

bool VotingArbiter::setAvailableVoters(unsigned short int count)
{
  _maxVotes = count;
  return true;
}

bool VotingArbiter::allowSuffrage(std::string player) const
{
  // is there a poll to vote on?
  if (!this->isPollOpen()) {
    return false;
  }

  // has this player already voted?
  if (_votingBooth->hasVoted(player)) {
    return false;
  }

  if (_votingBooth->getTotalVotes() >= _maxVotes) {
    return false;
  }

  return true;
}

bool VotingArbiter::voteYes(std::string player)
{
  if (!this->knowsPoll() || this->isPollClosed()) {
    return false;
  }

  if (_votingBooth->getTotalVotes() >= _maxVotes) {
    return false;
  }

  return (_votingBooth->vote(player, 1));
}

bool VotingArbiter::voteNo(std::string player)
{
  if (!this->knowsPoll() || this->isPollClosed()) {
    return false;
  }

  if (_votingBooth->getTotalVotes() >= _maxVotes) {
    return false;
  }

  return (_votingBooth->vote(player, 0));
}

bool VotingArbiter::isPollSuccessful(void) const
{
  if (!this-knowsPoll()) {
    return false;
  }
  unsigned long int votes = _votingBooth->getVoteCount(1);

  //ensure minimum votage
  if (votes < _votesRequired) {
    return false;
  }

  // were there enough votes?
  if (((double)votes * (double)100.0 / (double)_maxVotes) > (double)_votePercentage) {
    return true;
  }

  return false;
}

unsigned long int VotingArbiter::timeRemaining(void) const
{
  if (_votingBooth == NULL) {
    return 0;
  }

  // if the poll is successful early, terminate the clock
  if (this->isPollSuccessful()) {
    return 0;
  }

  float remaining = _voteTime - (TimeKeeper::getCurrent() - _startTime);
  if (remaining < 0.0f) {
    return 0;
  }
  return (unsigned int)remaining;
}

// ex: shiftwidth=2 tabstop=8
<commit_msg>dang minus is not arrow<commit_after>/* bzflag
 * Copyright (c) 1993 - 2003 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* no header other than VotingArbiter.h should be included here */

#ifdef _WIN32
#pragma warning( 4:4786)
#endif

#include "VotingArbiter.h"

/* private */

/* protected */

void VotingArbiter::updatePollers(void)
{
  while (!_pollers.empty()) {
    poller_t& p = _pollers.front();
    if ((TimeKeeper::getCurrent() - p.lastRequest) > _voteRepeatTime) {
      // remove pollers that expired their repoll timeout
      _pollers.pop_front();
    } else {
      break;
    }
  }
  return;
}

bool VotingArbiter::isPollerWaiting(std::string name) const
{
  for (unsigned int i = 0; i < _pollers.size(); i++) {
    if (_pollers[i].name == name) {
      return true;
    }
  }
  return false;
}

/* public */

bool VotingArbiter::forgetPoll(void)
{
  if (_votingBooth != NULL) {
    delete _votingBooth;
    _votingBooth = NULL;
  }
  _startTime = TimeKeeper::getNullTime();
  _pollee = "nobody";
  _polleeIP = "";
  _action = UNDEFINED;
  _pollRequestor = "nobody";
  return true;
}

bool VotingArbiter::poll(std::string player, std::string playerRequesting, pollAction_t action, std::string playerIP)
{
  poller_t p;
  char message[256];
  bool tooSoon;

  // you have to forget the current poll before another can be spawned
  if (this->isPollOpen()) {
    std::cout << "/poll was called and a poll is already open?" << std::endl;
    return false;
  }

  // update the poller list (people on the pollers list cannot initiate a poll
  updatePollers();

  // see if the poller is in the list
  tooSoon = isPollerWaiting(playerRequesting);
  if (tooSoon) {
    std::cout << "/poll was called and the requesting poller already asked?? [" << playerRequesting << "]" << std::endl;
    return false;
  }

  // add this poller to the end list
  p.name = playerRequesting;
  p.lastRequest = TimeKeeper::getCurrent();
  _pollers.push_back(p);

  // create the booth to record votes
#ifdef _WIN32
  _snprintf(message, 256,  "%s %s", action == POLL_KICK_PLAYER ? "kick" : "ban", player.c_str());
#else
  snprintf(message, 256,  "%s %s", action == POLL_KICK_PLAYER ? "kick" : "ban", player.c_str());
#endif
  if (_votingBooth != NULL) {
    delete _votingBooth;
  }
  _votingBooth = YesNoVotingBooth(message);
  _pollee = player;
  _polleeIP = playerIP;
  _action = action;
  _pollRequestor = playerRequesting;

  // set timers
  _startTime = TimeKeeper::getCurrent();

  return true;
}

bool VotingArbiter::pollToKick(std::string player, std::string playerRequesting)
{
  return (this->poll(player, playerRequesting, POLL_KICK_PLAYER));
}

bool VotingArbiter::pollToBan(std::string player, std::string playerRequesting, std::string playerIP)
{
  return (this->poll(player, playerRequesting, POLL_BAN_PLAYER, playerIP));
}

bool VotingArbiter::closePoll(void)
{
  if (this->isPollClosed()) {
    return true;
  }
  _startTime = TimeKeeper::getSunExplodeTime();
  return true;
}

bool VotingArbiter::setAvailableVoters(unsigned short int count)
{
  _maxVotes = count;
  return true;
}

bool VotingArbiter::allowSuffrage(std::string player) const
{
  // is there a poll to vote on?
  if (!this->isPollOpen()) {
    return false;
  }

  // has this player already voted?
  if (_votingBooth->hasVoted(player)) {
    return false;
  }

  if (_votingBooth->getTotalVotes() >= _maxVotes) {
    return false;
  }

  return true;
}

bool VotingArbiter::voteYes(std::string player)
{
  if (!this->knowsPoll() || this->isPollClosed()) {
    return false;
  }

  if (_votingBooth->getTotalVotes() >= _maxVotes) {
    return false;
  }

  return (_votingBooth->vote(player, 1));
}

bool VotingArbiter::voteNo(std::string player)
{
  if (!this->knowsPoll() || this->isPollClosed()) {
    return false;
  }

  if (_votingBooth->getTotalVotes() >= _maxVotes) {
    return false;
  }

  return (_votingBooth->vote(player, 0));
}

bool VotingArbiter::isPollSuccessful(void) const
{
  if (!this->knowsPoll()) {
    return false;
  }
  unsigned long int votes = _votingBooth->getVoteCount(1);

  //ensure minimum votage
  if (votes < _votesRequired) {
    return false;
  }

  // were there enough votes?
  if (((double)votes * (double)100.0 / (double)_maxVotes) > (double)_votePercentage) {
    return true;
  }

  return false;
}

unsigned long int VotingArbiter::timeRemaining(void) const
{
  if (_votingBooth == NULL) {
    return 0;
  }

  // if the poll is successful early, terminate the clock
  if (this->isPollSuccessful()) {
    return 0;
  }

  float remaining = _voteTime - (TimeKeeper::getCurrent() - _startTime);
  if (remaining < 0.0f) {
    return 0;
  }
  return (unsigned int)remaining;
}

// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: salgeom.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: kz $ $Date: 2008-04-03 15:47:24 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SV_SALGEOM_HXX
#define _SV_SALGEOM_HXX

typedef struct _SalFrameGeometry {
    // screen position of upper left corner of drawable area in pixel
    int                 nX, nY;
    // dimensions of the drawable area in pixel
    unsigned int        nWidth, nHeight;
    // thickness of the decoration in pixel
    unsigned int        nLeftDecoration,
                        nTopDecoration,
                        nRightDecoration,
                        nBottomDecoration;
    unsigned int        nScreenNumber;

    _SalFrameGeometry() :
    nX( 0 ), nY( 0 ), nWidth( 1 ), nHeight( 1 ),
    nLeftDecoration( 0 ), nTopDecoration( 0 ),
    nRightDecoration( 0 ), nBottomDecoration( 0 ),
    nScreenNumber( 0 )
    {}
} SalFrameGeometry;

#endif // _SV_SALGEOM_HXX
<commit_msg>INTEGRATION: CWS vcl87 (1.2.302); FILE MERGED 2008/03/11 10:43:58 pl 1.2.302.1: #i86882# use long for gemoetry like the rest of the Sal interface does<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: salgeom.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: kz $ $Date: 2008-04-03 17:02:58 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SV_SALGEOM_HXX
#define _SV_SALGEOM_HXX

typedef struct _SalFrameGeometry {
    // screen position of upper left corner of drawable area in pixel
    long                nX, nY;
    // dimensions of the drawable area in pixel
    unsigned long       nWidth, nHeight;
    // thickness of the decoration in pixel
    unsigned long       nLeftDecoration,
                        nTopDecoration,
                        nRightDecoration,
                        nBottomDecoration;
    unsigned int        nScreenNumber;

    _SalFrameGeometry() :
    nX( 0 ), nY( 0 ), nWidth( 1 ), nHeight( 1 ),
    nLeftDecoration( 0 ), nTopDecoration( 0 ),
    nRightDecoration( 0 ), nBottomDecoration( 0 ),
    nScreenNumber( 0 )
    {}
} SalFrameGeometry;

#endif // _SV_SALGEOM_HXX
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <stdio.h>
#include <memory>
#include <string>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/types/optional.h"
#include "api/bitrate_constraints.h"
#include "api/test/simulated_network.h"
#include "api/test/video_quality_test_fixture.h"
#include "api/video_codecs/video_codec.h"
#include "rtc_base/checks.h"
#include "rtc_base/flags.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
#include "test/field_trial.h"
#include "test/gtest.h"
#include "test/run_test.h"
#include "video/video_quality_test.h"

namespace webrtc {
namespace flags {

// Flags common with screenshare loopback, with different default values.
WEBRTC_DEFINE_int(width, 640, "Video width.");
size_t Width() {
  return static_cast<size_t>(FLAG_width);
}

WEBRTC_DEFINE_int(height, 480, "Video height.");
size_t Height() {
  return static_cast<size_t>(FLAG_height);
}

WEBRTC_DEFINE_int(fps, 30, "Frames per second.");
int Fps() {
  return static_cast<int>(FLAG_fps);
}

WEBRTC_DEFINE_int(capture_device_index, 0, "Capture device to select");
size_t GetCaptureDevice() {
  return static_cast<size_t>(FLAG_capture_device_index);
}

WEBRTC_DEFINE_int(min_bitrate, 50, "Call and stream min bitrate in kbps.");
int MinBitrateKbps() {
  return static_cast<int>(FLAG_min_bitrate);
}

WEBRTC_DEFINE_int(start_bitrate, 300, "Call start bitrate in kbps.");
int StartBitrateKbps() {
  return static_cast<int>(FLAG_start_bitrate);
}

WEBRTC_DEFINE_int(target_bitrate, 800, "Stream target bitrate in kbps.");
int TargetBitrateKbps() {
  return static_cast<int>(FLAG_target_bitrate);
}

WEBRTC_DEFINE_int(max_bitrate, 800, "Call and stream max bitrate in kbps.");
int MaxBitrateKbps() {
  return static_cast<int>(FLAG_max_bitrate);
}

WEBRTC_DEFINE_bool(suspend_below_min_bitrate,
                   false,
                   "Suspends video below the configured min bitrate.");

WEBRTC_DEFINE_int(num_temporal_layers,
                  1,
                  "Number of temporal layers. Set to 1-4 to override.");
int NumTemporalLayers() {
  return static_cast<int>(FLAG_num_temporal_layers);
}

WEBRTC_DEFINE_int(
    inter_layer_pred,
    2,
    "Inter-layer prediction mode. "
    "0 - enabled, 1 - disabled, 2 - enabled only for key pictures.");
InterLayerPredMode InterLayerPred() {
  if (FLAG_inter_layer_pred == 0) {
    return InterLayerPredMode::kOn;
  } else if (FLAG_inter_layer_pred == 1) {
    return InterLayerPredMode::kOff;
  } else {
    RTC_DCHECK_EQ(FLAG_inter_layer_pred, 2);
    return InterLayerPredMode::kOnKeyPic;
  }
}

// Flags common with screenshare loopback, with equal default values.
WEBRTC_DEFINE_string(codec, "VP8", "Video codec to use.");
std::string Codec() {
  return static_cast<std::string>(FLAG_codec);
}

WEBRTC_DEFINE_int(
    selected_tl,
    -1,
    "Temporal layer to show or analyze. -1 to disable filtering.");
int SelectedTL() {
  return static_cast<int>(FLAG_selected_tl);
}

WEBRTC_DEFINE_int(
    duration,
    0,
    "Duration of the test in seconds. If 0, rendered will be shown instead.");
int DurationSecs() {
  return static_cast<int>(FLAG_duration);
}

WEBRTC_DEFINE_string(output_filename, "", "Target graph data filename.");
std::string OutputFilename() {
  return static_cast<std::string>(FLAG_output_filename);
}

WEBRTC_DEFINE_string(graph_title,
                     "",
                     "If empty, title will be generated automatically.");
std::string GraphTitle() {
  return static_cast<std::string>(FLAG_graph_title);
}

WEBRTC_DEFINE_int(loss_percent, 0, "Percentage of packets randomly lost.");
int LossPercent() {
  return static_cast<int>(FLAG_loss_percent);
}

WEBRTC_DEFINE_int(avg_burst_loss_length,
                  -1,
                  "Average burst length of lost packets.");
int AvgBurstLossLength() {
  return static_cast<int>(FLAG_avg_burst_loss_length);
}

WEBRTC_DEFINE_int(link_capacity,
                  0,
                  "Capacity (kbps) of the fake link. 0 means infinite.");
int LinkCapacityKbps() {
  return static_cast<int>(FLAG_link_capacity);
}

WEBRTC_DEFINE_int(queue_size,
                  0,
                  "Size of the bottleneck link queue in packets.");
int QueueSize() {
  return static_cast<int>(FLAG_queue_size);
}

WEBRTC_DEFINE_int(avg_propagation_delay_ms,
                  0,
                  "Average link propagation delay in ms.");
int AvgPropagationDelayMs() {
  return static_cast<int>(FLAG_avg_propagation_delay_ms);
}

WEBRTC_DEFINE_string(rtc_event_log_name,
                     "",
                     "Filename for rtc event log. Two files "
                     "with \"_send\" and \"_recv\" suffixes will be created.");
std::string RtcEventLogName() {
  return static_cast<std::string>(FLAG_rtc_event_log_name);
}

WEBRTC_DEFINE_string(rtp_dump_name,
                     "",
                     "Filename for dumped received RTP stream.");
std::string RtpDumpName() {
  return static_cast<std::string>(FLAG_rtp_dump_name);
}

WEBRTC_DEFINE_int(std_propagation_delay_ms,
                  0,
                  "Link propagation delay standard deviation in ms.");
int StdPropagationDelayMs() {
  return static_cast<int>(FLAG_std_propagation_delay_ms);
}

WEBRTC_DEFINE_int(num_streams, 0, "Number of streams to show or analyze.");
int NumStreams() {
  return static_cast<int>(FLAG_num_streams);
}

WEBRTC_DEFINE_int(selected_stream,
                  0,
                  "ID of the stream to show or analyze. "
                  "Set to the number of streams to show them all.");
int SelectedStream() {
  return static_cast<int>(FLAG_selected_stream);
}

WEBRTC_DEFINE_int(num_spatial_layers, 1, "Number of spatial layers to use.");
int NumSpatialLayers() {
  return static_cast<int>(FLAG_num_spatial_layers);
}

WEBRTC_DEFINE_int(selected_sl,
                  -1,
                  "Spatial layer to show or analyze. -1 to disable filtering.");
int SelectedSL() {
  return static_cast<int>(FLAG_selected_sl);
}

WEBRTC_DEFINE_string(
    stream0,
    "",
    "Comma separated values describing VideoStream for stream #0.");
std::string Stream0() {
  return static_cast<std::string>(FLAG_stream0);
}

WEBRTC_DEFINE_string(
    stream1,
    "",
    "Comma separated values describing VideoStream for stream #1.");
std::string Stream1() {
  return static_cast<std::string>(FLAG_stream1);
}

WEBRTC_DEFINE_string(
    sl0,
    "",
    "Comma separated values describing SpatialLayer for layer #0.");
std::string SL0() {
  return static_cast<std::string>(FLAG_sl0);
}

WEBRTC_DEFINE_string(
    sl1,
    "",
    "Comma separated values describing SpatialLayer for layer #1.");
std::string SL1() {
  return static_cast<std::string>(FLAG_sl1);
}

WEBRTC_DEFINE_string(
    encoded_frame_path,
    "",
    "The base path for encoded frame logs. Created files will have "
    "the form <encoded_frame_path>.<n>.(recv|send.<m>).ivf");
std::string EncodedFramePath() {
  return static_cast<std::string>(FLAG_encoded_frame_path);
}

WEBRTC_DEFINE_bool(logs, false, "print logs to stderr");

WEBRTC_DEFINE_bool(send_side_bwe, true, "Use send-side bandwidth estimation");

WEBRTC_DEFINE_bool(generic_descriptor,
                   false,
                   "Use the generic frame descriptor.");

WEBRTC_DEFINE_bool(allow_reordering, false, "Allow packet reordering to occur");

WEBRTC_DEFINE_bool(use_ulpfec,
                   false,
                   "Use RED+ULPFEC forward error correction.");

WEBRTC_DEFINE_bool(use_flexfec, false, "Use FlexFEC forward error correction.");

WEBRTC_DEFINE_bool(audio, false, "Add audio stream");

WEBRTC_DEFINE_bool(
    use_real_adm,
    false,
    "Use real ADM instead of fake (no effect if audio is false)");

WEBRTC_DEFINE_bool(audio_video_sync,
                   false,
                   "Sync audio and video stream (no effect if"
                   " audio is false)");

WEBRTC_DEFINE_bool(audio_dtx,
                   false,
                   "Enable audio DTX (no effect if audio is false)");

WEBRTC_DEFINE_bool(video, true, "Add video stream");

WEBRTC_DEFINE_string(
    force_fieldtrials,
    "",
    "Field trials control experimental feature code which can be forced. "
    "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enabled/"
    " will assign the group Enable to field trial WebRTC-FooFeature. Multiple "
    "trials are separated by \"/\"");

// Video-specific flags.
WEBRTC_DEFINE_string(
    clip,
    "",
    "Name of the clip to show. If empty, using chroma generator.");
std::string Clip() {
  return static_cast<std::string>(FLAG_clip);
}

WEBRTC_DEFINE_bool(help, false, "prints this message");

}  // namespace flags

void Loopback() {
  BuiltInNetworkBehaviorConfig pipe_config;
  pipe_config.loss_percent = flags::LossPercent();
  pipe_config.avg_burst_loss_length = flags::AvgBurstLossLength();
  pipe_config.link_capacity_kbps = flags::LinkCapacityKbps();
  pipe_config.queue_length_packets = flags::QueueSize();
  pipe_config.queue_delay_ms = flags::AvgPropagationDelayMs();
  pipe_config.delay_standard_deviation_ms = flags::StdPropagationDelayMs();
  pipe_config.allow_reordering = flags::FLAG_allow_reordering;

  BitrateConstraints call_bitrate_config;
  call_bitrate_config.min_bitrate_bps = flags::MinBitrateKbps() * 1000;
  call_bitrate_config.start_bitrate_bps = flags::StartBitrateKbps() * 1000;
  call_bitrate_config.max_bitrate_bps = -1;  // Don't cap bandwidth estimate.

  VideoQualityTest::Params params;
  params.call = {flags::FLAG_send_side_bwe, flags::FLAG_generic_descriptor,
                 call_bitrate_config, 0};
  params.video[0] = {flags::FLAG_video,
                     flags::Width(),
                     flags::Height(),
                     flags::Fps(),
                     flags::MinBitrateKbps() * 1000,
                     flags::TargetBitrateKbps() * 1000,
                     flags::MaxBitrateKbps() * 1000,
                     flags::FLAG_suspend_below_min_bitrate,
                     flags::Codec(),
                     flags::NumTemporalLayers(),
                     flags::SelectedTL(),
                     0,  // No min transmit bitrate.
                     flags::FLAG_use_ulpfec,
                     flags::FLAG_use_flexfec,
                     false,
                     flags::Clip(),
                     flags::GetCaptureDevice()};
  params.audio = {flags::FLAG_audio, flags::FLAG_audio_video_sync,
                  flags::FLAG_audio_dtx, flags::FLAG_use_real_adm};
  params.logging = {flags::FLAG_rtc_event_log_name, flags::FLAG_rtp_dump_name,
                    flags::FLAG_encoded_frame_path};
  params.screenshare[0].enabled = false;
  params.analyzer = {"video",
                     0.0,
                     0.0,
                     flags::DurationSecs(),
                     flags::OutputFilename(),
                     flags::GraphTitle()};
  params.config = pipe_config;

  if (flags::NumStreams() > 1 && flags::Stream0().empty() &&
      flags::Stream1().empty()) {
    params.ss[0].infer_streams = true;
  }

  std::vector<std::string> stream_descriptors;
  stream_descriptors.push_back(flags::Stream0());
  stream_descriptors.push_back(flags::Stream1());
  std::vector<std::string> SL_descriptors;
  SL_descriptors.push_back(flags::SL0());
  SL_descriptors.push_back(flags::SL1());
  VideoQualityTest::FillScalabilitySettings(
      &params, 0, stream_descriptors, flags::NumStreams(),
      flags::SelectedStream(), flags::NumSpatialLayers(), flags::SelectedSL(),
      flags::InterLayerPred(), SL_descriptors);

  auto fixture = absl::make_unique<VideoQualityTest>(nullptr);
  if (flags::DurationSecs()) {
    fixture->RunWithAnalyzer(params);
  } else {
    fixture->RunWithRenderers(params);
  }
}
}  // namespace webrtc

int main(int argc, char* argv[]) {
  ::testing::InitGoogleTest(&argc, argv);
  rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
  if (webrtc::flags::FLAG_help) {
    rtc::FlagList::Print(nullptr, false);
    return 0;
  }

  rtc::LogMessage::SetLogToStderr(webrtc::flags::FLAG_logs);

  webrtc::test::ValidateFieldTrialsStringOrDie(
      webrtc::flags::FLAG_force_fieldtrials);
  // InitFieldTrialsFromString stores the char*, so the char array must outlive
  // the application.
  webrtc::field_trial::InitFieldTrialsFromString(
      webrtc::flags::FLAG_force_fieldtrials);

  webrtc::test::RunTest(webrtc::Loopback);
  return 0;
}
<commit_msg>Enable quality scaling in video_loopback.<commit_after>/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <stdio.h>
#include <memory>
#include <string>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/types/optional.h"
#include "api/bitrate_constraints.h"
#include "api/test/simulated_network.h"
#include "api/test/video_quality_test_fixture.h"
#include "api/video_codecs/video_codec.h"
#include "rtc_base/checks.h"
#include "rtc_base/flags.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
#include "test/field_trial.h"
#include "test/gtest.h"
#include "test/run_test.h"
#include "video/video_quality_test.h"

namespace webrtc {
namespace flags {

// Flags common with screenshare loopback, with different default values.
WEBRTC_DEFINE_int(width, 640, "Video width.");
size_t Width() {
  return static_cast<size_t>(FLAG_width);
}

WEBRTC_DEFINE_int(height, 480, "Video height.");
size_t Height() {
  return static_cast<size_t>(FLAG_height);
}

WEBRTC_DEFINE_int(fps, 30, "Frames per second.");
int Fps() {
  return static_cast<int>(FLAG_fps);
}

WEBRTC_DEFINE_int(capture_device_index, 0, "Capture device to select");
size_t GetCaptureDevice() {
  return static_cast<size_t>(FLAG_capture_device_index);
}

WEBRTC_DEFINE_int(min_bitrate, 50, "Call and stream min bitrate in kbps.");
int MinBitrateKbps() {
  return static_cast<int>(FLAG_min_bitrate);
}

WEBRTC_DEFINE_int(start_bitrate, 300, "Call start bitrate in kbps.");
int StartBitrateKbps() {
  return static_cast<int>(FLAG_start_bitrate);
}

WEBRTC_DEFINE_int(target_bitrate, 800, "Stream target bitrate in kbps.");
int TargetBitrateKbps() {
  return static_cast<int>(FLAG_target_bitrate);
}

WEBRTC_DEFINE_int(max_bitrate, 800, "Call and stream max bitrate in kbps.");
int MaxBitrateKbps() {
  return static_cast<int>(FLAG_max_bitrate);
}

WEBRTC_DEFINE_bool(suspend_below_min_bitrate,
                   false,
                   "Suspends video below the configured min bitrate.");

WEBRTC_DEFINE_int(num_temporal_layers,
                  1,
                  "Number of temporal layers. Set to 1-4 to override.");
int NumTemporalLayers() {
  return static_cast<int>(FLAG_num_temporal_layers);
}

WEBRTC_DEFINE_int(
    inter_layer_pred,
    2,
    "Inter-layer prediction mode. "
    "0 - enabled, 1 - disabled, 2 - enabled only for key pictures.");
InterLayerPredMode InterLayerPred() {
  if (FLAG_inter_layer_pred == 0) {
    return InterLayerPredMode::kOn;
  } else if (FLAG_inter_layer_pred == 1) {
    return InterLayerPredMode::kOff;
  } else {
    RTC_DCHECK_EQ(FLAG_inter_layer_pred, 2);
    return InterLayerPredMode::kOnKeyPic;
  }
}

// Flags common with screenshare loopback, with equal default values.
WEBRTC_DEFINE_string(codec, "VP8", "Video codec to use.");
std::string Codec() {
  return static_cast<std::string>(FLAG_codec);
}

WEBRTC_DEFINE_int(
    selected_tl,
    -1,
    "Temporal layer to show or analyze. -1 to disable filtering.");
int SelectedTL() {
  return static_cast<int>(FLAG_selected_tl);
}

WEBRTC_DEFINE_int(
    duration,
    0,
    "Duration of the test in seconds. If 0, rendered will be shown instead.");
int DurationSecs() {
  return static_cast<int>(FLAG_duration);
}

WEBRTC_DEFINE_string(output_filename, "", "Target graph data filename.");
std::string OutputFilename() {
  return static_cast<std::string>(FLAG_output_filename);
}

WEBRTC_DEFINE_string(graph_title,
                     "",
                     "If empty, title will be generated automatically.");
std::string GraphTitle() {
  return static_cast<std::string>(FLAG_graph_title);
}

WEBRTC_DEFINE_int(loss_percent, 0, "Percentage of packets randomly lost.");
int LossPercent() {
  return static_cast<int>(FLAG_loss_percent);
}

WEBRTC_DEFINE_int(avg_burst_loss_length,
                  -1,
                  "Average burst length of lost packets.");
int AvgBurstLossLength() {
  return static_cast<int>(FLAG_avg_burst_loss_length);
}

WEBRTC_DEFINE_int(link_capacity,
                  0,
                  "Capacity (kbps) of the fake link. 0 means infinite.");
int LinkCapacityKbps() {
  return static_cast<int>(FLAG_link_capacity);
}

WEBRTC_DEFINE_int(queue_size,
                  0,
                  "Size of the bottleneck link queue in packets.");
int QueueSize() {
  return static_cast<int>(FLAG_queue_size);
}

WEBRTC_DEFINE_int(avg_propagation_delay_ms,
                  0,
                  "Average link propagation delay in ms.");
int AvgPropagationDelayMs() {
  return static_cast<int>(FLAG_avg_propagation_delay_ms);
}

WEBRTC_DEFINE_string(rtc_event_log_name,
                     "",
                     "Filename for rtc event log. Two files "
                     "with \"_send\" and \"_recv\" suffixes will be created.");
std::string RtcEventLogName() {
  return static_cast<std::string>(FLAG_rtc_event_log_name);
}

WEBRTC_DEFINE_string(rtp_dump_name,
                     "",
                     "Filename for dumped received RTP stream.");
std::string RtpDumpName() {
  return static_cast<std::string>(FLAG_rtp_dump_name);
}

WEBRTC_DEFINE_int(std_propagation_delay_ms,
                  0,
                  "Link propagation delay standard deviation in ms.");
int StdPropagationDelayMs() {
  return static_cast<int>(FLAG_std_propagation_delay_ms);
}

WEBRTC_DEFINE_int(num_streams, 0, "Number of streams to show or analyze.");
int NumStreams() {
  return static_cast<int>(FLAG_num_streams);
}

WEBRTC_DEFINE_int(selected_stream,
                  0,
                  "ID of the stream to show or analyze. "
                  "Set to the number of streams to show them all.");
int SelectedStream() {
  return static_cast<int>(FLAG_selected_stream);
}

WEBRTC_DEFINE_int(num_spatial_layers, 1, "Number of spatial layers to use.");
int NumSpatialLayers() {
  return static_cast<int>(FLAG_num_spatial_layers);
}

WEBRTC_DEFINE_int(selected_sl,
                  -1,
                  "Spatial layer to show or analyze. -1 to disable filtering.");
int SelectedSL() {
  return static_cast<int>(FLAG_selected_sl);
}

WEBRTC_DEFINE_string(
    stream0,
    "",
    "Comma separated values describing VideoStream for stream #0.");
std::string Stream0() {
  return static_cast<std::string>(FLAG_stream0);
}

WEBRTC_DEFINE_string(
    stream1,
    "",
    "Comma separated values describing VideoStream for stream #1.");
std::string Stream1() {
  return static_cast<std::string>(FLAG_stream1);
}

WEBRTC_DEFINE_string(
    sl0,
    "",
    "Comma separated values describing SpatialLayer for layer #0.");
std::string SL0() {
  return static_cast<std::string>(FLAG_sl0);
}

WEBRTC_DEFINE_string(
    sl1,
    "",
    "Comma separated values describing SpatialLayer for layer #1.");
std::string SL1() {
  return static_cast<std::string>(FLAG_sl1);
}

WEBRTC_DEFINE_string(
    encoded_frame_path,
    "",
    "The base path for encoded frame logs. Created files will have "
    "the form <encoded_frame_path>.<n>.(recv|send.<m>).ivf");
std::string EncodedFramePath() {
  return static_cast<std::string>(FLAG_encoded_frame_path);
}

WEBRTC_DEFINE_bool(logs, false, "print logs to stderr");

WEBRTC_DEFINE_bool(send_side_bwe, true, "Use send-side bandwidth estimation");

WEBRTC_DEFINE_bool(generic_descriptor,
                   false,
                   "Use the generic frame descriptor.");

WEBRTC_DEFINE_bool(allow_reordering, false, "Allow packet reordering to occur");

WEBRTC_DEFINE_bool(use_ulpfec,
                   false,
                   "Use RED+ULPFEC forward error correction.");

WEBRTC_DEFINE_bool(use_flexfec, false, "Use FlexFEC forward error correction.");

WEBRTC_DEFINE_bool(audio, false, "Add audio stream");

WEBRTC_DEFINE_bool(
    use_real_adm,
    false,
    "Use real ADM instead of fake (no effect if audio is false)");

WEBRTC_DEFINE_bool(audio_video_sync,
                   false,
                   "Sync audio and video stream (no effect if"
                   " audio is false)");

WEBRTC_DEFINE_bool(audio_dtx,
                   false,
                   "Enable audio DTX (no effect if audio is false)");

WEBRTC_DEFINE_bool(video, true, "Add video stream");

WEBRTC_DEFINE_string(
    force_fieldtrials,
    "",
    "Field trials control experimental feature code which can be forced. "
    "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enabled/"
    " will assign the group Enable to field trial WebRTC-FooFeature. Multiple "
    "trials are separated by \"/\"");

// Video-specific flags.
WEBRTC_DEFINE_string(
    clip,
    "",
    "Name of the clip to show. If empty, using chroma generator.");
std::string Clip() {
  return static_cast<std::string>(FLAG_clip);
}

WEBRTC_DEFINE_bool(help, false, "prints this message");

}  // namespace flags

void Loopback() {
  BuiltInNetworkBehaviorConfig pipe_config;
  pipe_config.loss_percent = flags::LossPercent();
  pipe_config.avg_burst_loss_length = flags::AvgBurstLossLength();
  pipe_config.link_capacity_kbps = flags::LinkCapacityKbps();
  pipe_config.queue_length_packets = flags::QueueSize();
  pipe_config.queue_delay_ms = flags::AvgPropagationDelayMs();
  pipe_config.delay_standard_deviation_ms = flags::StdPropagationDelayMs();
  pipe_config.allow_reordering = flags::FLAG_allow_reordering;

  BitrateConstraints call_bitrate_config;
  call_bitrate_config.min_bitrate_bps = flags::MinBitrateKbps() * 1000;
  call_bitrate_config.start_bitrate_bps = flags::StartBitrateKbps() * 1000;
  call_bitrate_config.max_bitrate_bps = -1;  // Don't cap bandwidth estimate.

  VideoQualityTest::Params params;
  params.call = {flags::FLAG_send_side_bwe, flags::FLAG_generic_descriptor,
                 call_bitrate_config, 0};
  params.video[0] = {flags::FLAG_video,
                     flags::Width(),
                     flags::Height(),
                     flags::Fps(),
                     flags::MinBitrateKbps() * 1000,
                     flags::TargetBitrateKbps() * 1000,
                     flags::MaxBitrateKbps() * 1000,
                     flags::FLAG_suspend_below_min_bitrate,
                     flags::Codec(),
                     flags::NumTemporalLayers(),
                     flags::SelectedTL(),
                     0,  // No min transmit bitrate.
                     flags::FLAG_use_ulpfec,
                     flags::FLAG_use_flexfec,
                     true,  // Automatic quality scaling.
                     flags::Clip(),
                     flags::GetCaptureDevice()};
  params.audio = {flags::FLAG_audio, flags::FLAG_audio_video_sync,
                  flags::FLAG_audio_dtx, flags::FLAG_use_real_adm};
  params.logging = {flags::FLAG_rtc_event_log_name, flags::FLAG_rtp_dump_name,
                    flags::FLAG_encoded_frame_path};
  params.screenshare[0].enabled = false;
  params.analyzer = {"video",
                     0.0,
                     0.0,
                     flags::DurationSecs(),
                     flags::OutputFilename(),
                     flags::GraphTitle()};
  params.config = pipe_config;

  if (flags::NumStreams() > 1 && flags::Stream0().empty() &&
      flags::Stream1().empty()) {
    params.ss[0].infer_streams = true;
  }

  std::vector<std::string> stream_descriptors;
  stream_descriptors.push_back(flags::Stream0());
  stream_descriptors.push_back(flags::Stream1());
  std::vector<std::string> SL_descriptors;
  SL_descriptors.push_back(flags::SL0());
  SL_descriptors.push_back(flags::SL1());
  VideoQualityTest::FillScalabilitySettings(
      &params, 0, stream_descriptors, flags::NumStreams(),
      flags::SelectedStream(), flags::NumSpatialLayers(), flags::SelectedSL(),
      flags::InterLayerPred(), SL_descriptors);

  auto fixture = absl::make_unique<VideoQualityTest>(nullptr);
  if (flags::DurationSecs()) {
    fixture->RunWithAnalyzer(params);
  } else {
    fixture->RunWithRenderers(params);
  }
}
}  // namespace webrtc

int main(int argc, char* argv[]) {
  ::testing::InitGoogleTest(&argc, argv);
  rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
  if (webrtc::flags::FLAG_help) {
    rtc::FlagList::Print(nullptr, false);
    return 0;
  }

  rtc::LogMessage::SetLogToStderr(webrtc::flags::FLAG_logs);

  webrtc::test::ValidateFieldTrialsStringOrDie(
      webrtc::flags::FLAG_force_fieldtrials);
  // InitFieldTrialsFromString stores the char*, so the char array must outlive
  // the application.
  webrtc::field_trial::InitFieldTrialsFromString(
      webrtc::flags::FLAG_force_fieldtrials);

  webrtc::test::RunTest(webrtc::Loopback);
  return 0;
}
<|endoftext|>