<commit_before>#include "detection.h"
#include "deviceList.h"

#include <CoreFoundation/CoreFoundation.h>

#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>

#include <sys/param.h>
#include <poll.h>

#include <uv.h>

/**********************************
 * Local typedefs
 **********************************/
typedef struct DeviceListItem {
	io_object_t notification;
	IOUSBDeviceInterface** deviceInterface;
	DeviceItem_t* deviceItem;
} stDeviceListItem;

/**********************************
 * Local Variables
 **********************************/
static ListResultItem_t* currentItem;
static bool isAdded = false;
static bool initialDeviceImport = true;

static IONotificationPortRef gNotifyPort;
static io_iterator_t gAddedIter;
static CFRunLoopRef gRunLoop;

static CFMutableDictionaryRef matchingDict;
static CFRunLoopSourceRef runLoopSource;

static uv_work_t work_req;
static uv_signal_t term_signal;
static uv_signal_t int_signal;

static uv_async_t async_handler;
static uv_mutex_t notify_mutex;
static uv_cond_t notifyDeviceHandled;

static bool deviceHandled = true;

static bool isRunning = false;

/**********************************
 * Local Helper Functions protoypes
 **********************************/

static void WaitForDeviceHandled();
static void SignalDeviceHandled();
static void cbTerminate(uv_signal_t *handle, int signum);
static void cbWork(uv_work_t *req);
static void cbAfter(uv_work_t *req, int status);
static void cbAsync(uv_async_t *handle);

/**********************************
 * Public Functions
 **********************************/
//================================================================================================
//
//  DeviceRemoved
//
//  This routine will get called whenever any kIOGeneralInterest notification happens.  We are
//  interested in the kIOMessageServiceIsTerminated message so that's what we look for.  Other
//  messages are defined in IOMessage.h.
//
//================================================================================================
static void DeviceRemoved(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) {
	kern_return_t kr;
	stDeviceListItem* deviceListItem = (stDeviceListItem *) refCon;
	DeviceItem_t* deviceItem = deviceListItem->deviceItem;

	if(messageType == kIOMessageServiceIsTerminated) {
		if(deviceListItem->deviceInterface) {
			kr = (*deviceListItem->deviceInterface)->Release(deviceListItem->deviceInterface);
		}

		kr = IOObjectRelease(deviceListItem->notification);

		ListResultItem_t* item = NULL;
		if(deviceItem) {
			item = CopyElement(&deviceItem->deviceParams);
			RemoveItemFromList(deviceItem);
			delete deviceItem;
		}
		else {
			item = new ListResultItem_t();
		}

		WaitForDeviceHandled();
		currentItem = item;
		isAdded = false;
		uv_async_send(&async_handler);
	}
}

//================================================================================================
//
//  DeviceAdded
//
//  This routine is the callback for our IOServiceAddMatchingNotification.  When we get called
//  we will look at all the devices that were added and we will:
//
//  1.  Create some private data to relate to each device (in this case we use the service's name
//      and the location ID of the device
//  2.  Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device,
//      using the refCon field to store a pointer to our private data.  When we get called with
//      this interest notification, we can grab the refCon and access our private data.
//
//================================================================================================
static void DeviceAdded(void *refCon, io_iterator_t iterator) {
	kern_return_t kr;
	io_service_t usbDevice;
	IOCFPlugInInterface **plugInInterface = NULL;
	SInt32 score;
	HRESULT res;

	while((usbDevice = IOIteratorNext(iterator))) {
		io_name_t deviceName;
		CFStringRef deviceNameAsCFString;
		UInt32 locationID;
		UInt16 vendorId;
		UInt16 productId;
		UInt16 addr;

		DeviceItem_t* deviceItem = new DeviceItem_t();

		// Get the USB device's name.
		kr = IORegistryEntryGetName(usbDevice, deviceName);
		if(KERN_SUCCESS != kr) {
			deviceName[0] = '\0';
		}

		deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII);

		if(deviceNameAsCFString) {
			Boolean result;
			char    deviceName[MAXPATHLEN];

			// Convert from a CFString to a C (NUL-terminated)
			result = CFStringGetCString(deviceNameAsCFString,
										deviceName,
										sizeof(deviceName),
										kCFStringEncodingUTF8);

			if(result) {
				deviceItem->deviceParams.deviceName = deviceName;
			}

			CFRelease(deviceNameAsCFString);
		}

		CFStringRef manufacturerAsCFString = (CFStringRef)IORegistryEntrySearchCFProperty(
				usbDevice,
				kIOServicePlane,
				CFSTR(kUSBVendorString),
				kCFAllocatorDefault,
				kIORegistryIterateRecursively
			);

		if(manufacturerAsCFString) {
			Boolean result;
			char    manufacturer[MAXPATHLEN];

			// Convert from a CFString to a C (NUL-terminated)
			result = CFStringGetCString(
					manufacturerAsCFString,
					manufacturer,
					sizeof(manufacturer),
					kCFStringEncodingUTF8
				);

			if(result) {
				deviceItem->deviceParams.manufacturer = manufacturer;
			}

			CFRelease(manufacturerAsCFString);
		}

		CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(
				usbDevice,
				kIOServicePlane,
				CFSTR(kUSBSerialNumberString),
				kCFAllocatorDefault,
				kIORegistryIterateRecursively
			);

		if(serialNumberAsCFString) {
			Boolean result;
			char    serialNumber[MAXPATHLEN];

			// Convert from a CFString to a C (NUL-terminated)
			result = CFStringGetCString(
					serialNumberAsCFString,
					serialNumber,
					sizeof(serialNumber),
					kCFStringEncodingUTF8
				);

			if(result) {
				deviceItem->deviceParams.serialNumber = serialNumber;
			}

			CFRelease(serialNumberAsCFString);
		}

		// Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface
		// for our device. This will create the necessary connections between our userland application and the
		// kernel object for the USB Device.
		kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);

		if((kIOReturnSuccess != kr) || !plugInInterface) {
			DEBUG_LOG("IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr);
			continue;
		}

		stDeviceListItem *deviceListItem = new stDeviceListItem();

		// Use the plugin interface to retrieve the device interface.
		res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceListItem->deviceInterface);

		// Now done with the plugin interface.
		(*plugInInterface)->Release(plugInInterface);

		if(res || deviceListItem->deviceInterface == NULL) {
			DEBUG_LOG("QueryInterface returned %d.\n", (int) res);
			continue;
		}

		// Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h.
		// In this case, fetch the locationID. The locationID uniquely identifies the device
		// and will remain the same, even across reboots, so long as the bus topology doesn't change.

		kr = (*deviceListItem->deviceInterface)->GetLocationID(deviceListItem->deviceInterface, &locationID);
		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("GetLocationID returned 0x%08x.\n", kr);
			continue;
		}
		deviceItem->deviceParams.locationId = locationID;

		kr = (*deviceListItem->deviceInterface)->GetDeviceAddress(deviceListItem->deviceInterface, &addr);
		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("GetDeviceAddress returned 0x%08x.\n", kr);
			continue;
		}
		deviceItem->deviceParams.deviceAddress = addr;

		kr = (*deviceListItem->deviceInterface)->GetDeviceVendor(deviceListItem->deviceInterface, &vendorId);
		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("GetDeviceVendor returned 0x%08x.\n", kr);
			continue;
		}
		deviceItem->deviceParams.vendorId = vendorId;

		kr = (*deviceListItem->deviceInterface)->GetDeviceProduct(deviceListItem->deviceInterface, &productId);
		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("GetDeviceProduct returned 0x%08x.\n", kr);
			continue;
		}
		deviceItem->deviceParams.productId = productId;

		// Extract path name as unique key
		io_string_t pathName;
		IORegistryEntryGetPath(usbDevice, kIOServicePlane, pathName);
		deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, pathName, kCFStringEncodingASCII);
		char cPathName[MAXPATHLEN];

		if(deviceNameAsCFString) {
			Boolean result;

			// Convert from a CFString to a C (NUL-terminated)
			result = CFStringGetCString(
					deviceNameAsCFString,
					cPathName,
					sizeof(cPathName),
					kCFStringEncodingUTF8
				);

			CFRelease(deviceNameAsCFString);
		}

		AddItemToList(cPathName, deviceItem);
		deviceListItem->deviceItem = deviceItem;

		if(initialDeviceImport == false) {
			WaitForDeviceHandled();
			currentItem = &deviceItem->deviceParams;
			isAdded = true;
			uv_async_send(&async_handler);
		}

		// Register for an interest notification of this device being removed. Use a reference to our
		// private data as the refCon which will be passed to the notification callback.
		kr = IOServiceAddInterestNotification(
				gNotifyPort, // notifyPort
				usbDevice, // service
				kIOGeneralInterest, // interestType
				DeviceRemoved, // callback
				deviceListItem, // refCon
				&(deviceListItem->notification) // notification
			);

		if(KERN_SUCCESS != kr) {
			printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr);
		}

		// Done with this USB device; release the reference added by IOIteratorNext
		kr = IOObjectRelease(usbDevice);
	}
}

void Start() {
	if(isRunning) {
		return;
	}

	isRunning = true;

	uv_mutex_init(&notify_mutex);
	uv_async_init(uv_default_loop(), &async_handler, cbAsync);
	uv_signal_init(uv_default_loop(), &term_signal);
	uv_signal_init(uv_default_loop(), &int_signal);
	uv_cond_init(&notifyDeviceHandled);

	uv_queue_work(uv_default_loop(), &work_req, cbWork, cbAfter);
}

void Stop() {
	if(!isRunning) {
		return;
	}

	isRunning = false;

	uv_mutex_destroy(&notify_mutex);
	uv_signal_stop(&int_signal);
	uv_signal_stop(&term_signal);
	uv_close((uv_handle_t *) &async_handler, NULL);
	uv_cond_destroy(&notifyDeviceHandled);

	if (gRunLoop) {
		CFRunLoopStop(gRunLoop);
	}
}

void InitDetection() {
	kern_return_t kr;

	// Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow
	// the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7.
	// See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X"
	// <http://developer.apple.com/qa/qa2001/qa1076.html>.
	// One exception is that you can use the matching dictionary "as is", i.e. without adding any matching
	// criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will
	// consume this dictionary reference, so there is no need to release it later on.

	// Interested in instances of class
	// IOUSBDevice and its subclasses
	matchingDict = IOServiceMatching(kIOUSBDeviceClassName);

	if (matchingDict == NULL) {
		DEBUG_LOG("IOServiceMatching returned NULL.\n");
	}

	// Create a notification port and add its run loop event source to our run loop
	// This is how async notifications get set up.

	gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);

	// Now set up a notification to be called when a device is first matched by I/O Kit.
	kr = IOServiceAddMatchingNotification(
			gNotifyPort, // notifyPort
			kIOFirstMatchNotification, // notificationType
			matchingDict, // matching
			DeviceAdded, // callback
			NULL, // refCon
			&gAddedIter // notification
		);

	if (KERN_SUCCESS != kr) {
		printf("IOServiceAddMatchingNotification returned 0x%08x.\n", kr);
	}

	// Iterate once to get already-present devices and arm the notification
	DeviceAdded(NULL, gAddedIter);
	initialDeviceImport = false;
}

void EIO_Find(uv_work_t* req) {
	ListBaton* data = static_cast<ListBaton*>(req->data);

	CreateFilteredList(&data->results, data->vid, data->pid);
}

static void WaitForDeviceHandled() {
	uv_mutex_lock(&notify_mutex);
	if(deviceHandled == false) {
		uv_cond_wait(&notifyDeviceHandled, &notify_mutex);
	}
	deviceHandled = false;
	uv_mutex_unlock(&notify_mutex);
}

static void SignalDeviceHandled() {
	uv_mutex_lock(&notify_mutex);
	deviceHandled = true;
	uv_cond_signal(&notifyDeviceHandled);
	uv_mutex_unlock(&notify_mutex);
}

static void cbWork(uv_work_t *req) {
	// We have this check in case we `Stop` before this thread starts,
	// otherwise the process will hang
	if(!isRunning) {
		return;
	}

	uv_signal_start(&int_signal, cbTerminate, SIGINT);
	uv_signal_start(&term_signal, cbTerminate, SIGTERM);

	runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);

	gRunLoop = CFRunLoopGetCurrent();
	CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);

	// Creating `gRunLoop` can take some cycles so we also need this second
	// `isRunning` check here because it happens at a future time
	if(isRunning) {
		// Start the run loop. Now we'll receive notifications.
		CFRunLoopRun();
	}

	// The `CFRunLoopRun` is a blocking call so we also need this second
	// `isRunning` check here because it happens at a future time
	if(isRunning) {
		// We should never get here while running
		DEBUG_LOG("Unexpectedly back from CFRunLoopRun()!\n");
	}
}

static void cbAfter(uv_work_t *req, int status) {
	Stop();
}

static void cbAsync(uv_async_t *handle) {
	if(!isRunning) {
		return;
	}

	if(isAdded) {
		NotifyAdded(currentItem);
	}
	else {
		NotifyRemoved(currentItem);
	}

	// Delete Item in case of removal
	if(isAdded == false) {
		delete currentItem;
	}

	SignalDeviceHandled();
}

static void cbTerminate(uv_signal_t *handle, int signum) {
	Stop();
}
<commit_msg>feat(debug): print nothing to output in native code<commit_after>#include "detection.h"
#include "deviceList.h"

#include <CoreFoundation/CoreFoundation.h>

#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>

#include <sys/param.h>
#include <poll.h>

#include <uv.h>

/**********************************
 * Local typedefs
 **********************************/
typedef struct DeviceListItem {
	io_object_t notification;
	IOUSBDeviceInterface** deviceInterface;
	DeviceItem_t* deviceItem;
} stDeviceListItem;

/**********************************
 * Local Variables
 **********************************/
static ListResultItem_t* currentItem;
static bool isAdded = false;
static bool initialDeviceImport = true;

static IONotificationPortRef gNotifyPort;
static io_iterator_t gAddedIter;
static CFRunLoopRef gRunLoop;

static CFMutableDictionaryRef matchingDict;
static CFRunLoopSourceRef runLoopSource;

static uv_work_t work_req;
static uv_signal_t term_signal;
static uv_signal_t int_signal;

static uv_async_t async_handler;
static uv_mutex_t notify_mutex;
static uv_cond_t notifyDeviceHandled;

static bool deviceHandled = true;

static bool isRunning = false;

/**********************************
 * Local Helper Functions protoypes
 **********************************/

static void WaitForDeviceHandled();
static void SignalDeviceHandled();
static void cbTerminate(uv_signal_t *handle, int signum);
static void cbWork(uv_work_t *req);
static void cbAfter(uv_work_t *req, int status);
static void cbAsync(uv_async_t *handle);

/**********************************
 * Public Functions
 **********************************/
//================================================================================================
//
//  DeviceRemoved
//
//  This routine will get called whenever any kIOGeneralInterest notification happens.  We are
//  interested in the kIOMessageServiceIsTerminated message so that's what we look for.  Other
//  messages are defined in IOMessage.h.
//
//================================================================================================
static void DeviceRemoved(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) {
	kern_return_t kr;
	stDeviceListItem* deviceListItem = (stDeviceListItem *) refCon;
	DeviceItem_t* deviceItem = deviceListItem->deviceItem;

	if(messageType == kIOMessageServiceIsTerminated) {
		if(deviceListItem->deviceInterface) {
			kr = (*deviceListItem->deviceInterface)->Release(deviceListItem->deviceInterface);
		}

		kr = IOObjectRelease(deviceListItem->notification);

		ListResultItem_t* item = NULL;
		if(deviceItem) {
			item = CopyElement(&deviceItem->deviceParams);
			RemoveItemFromList(deviceItem);
			delete deviceItem;
		}
		else {
			item = new ListResultItem_t();
		}

		WaitForDeviceHandled();
		currentItem = item;
		isAdded = false;
		uv_async_send(&async_handler);
	}
}

//================================================================================================
//
//  DeviceAdded
//
//  This routine is the callback for our IOServiceAddMatchingNotification.  When we get called
//  we will look at all the devices that were added and we will:
//
//  1.  Create some private data to relate to each device (in this case we use the service's name
//      and the location ID of the device
//  2.  Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device,
//      using the refCon field to store a pointer to our private data.  When we get called with
//      this interest notification, we can grab the refCon and access our private data.
//
//================================================================================================
static void DeviceAdded(void *refCon, io_iterator_t iterator) {
	kern_return_t kr;
	io_service_t usbDevice;
	IOCFPlugInInterface **plugInInterface = NULL;
	SInt32 score;
	HRESULT res;

	while((usbDevice = IOIteratorNext(iterator))) {
		io_name_t deviceName;
		CFStringRef deviceNameAsCFString;
		UInt32 locationID;
		UInt16 vendorId;
		UInt16 productId;
		UInt16 addr;

		DeviceItem_t* deviceItem = new DeviceItem_t();

		// Get the USB device's name.
		kr = IORegistryEntryGetName(usbDevice, deviceName);
		if(KERN_SUCCESS != kr) {
			deviceName[0] = '\0';
		}

		deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII);

		if(deviceNameAsCFString) {
			Boolean result;
			char    deviceName[MAXPATHLEN];

			// Convert from a CFString to a C (NUL-terminated)
			result = CFStringGetCString(deviceNameAsCFString,
										deviceName,
										sizeof(deviceName),
										kCFStringEncodingUTF8);

			if(result) {
				deviceItem->deviceParams.deviceName = deviceName;
			}

			CFRelease(deviceNameAsCFString);
		}

		CFStringRef manufacturerAsCFString = (CFStringRef)IORegistryEntrySearchCFProperty(
				usbDevice,
				kIOServicePlane,
				CFSTR(kUSBVendorString),
				kCFAllocatorDefault,
				kIORegistryIterateRecursively
			);

		if(manufacturerAsCFString) {
			Boolean result;
			char    manufacturer[MAXPATHLEN];

			// Convert from a CFString to a C (NUL-terminated)
			result = CFStringGetCString(
					manufacturerAsCFString,
					manufacturer,
					sizeof(manufacturer),
					kCFStringEncodingUTF8
				);

			if(result) {
				deviceItem->deviceParams.manufacturer = manufacturer;
			}

			CFRelease(manufacturerAsCFString);
		}

		CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(
				usbDevice,
				kIOServicePlane,
				CFSTR(kUSBSerialNumberString),
				kCFAllocatorDefault,
				kIORegistryIterateRecursively
			);

		if(serialNumberAsCFString) {
			Boolean result;
			char    serialNumber[MAXPATHLEN];

			// Convert from a CFString to a C (NUL-terminated)
			result = CFStringGetCString(
					serialNumberAsCFString,
					serialNumber,
					sizeof(serialNumber),
					kCFStringEncodingUTF8
				);

			if(result) {
				deviceItem->deviceParams.serialNumber = serialNumber;
			}

			CFRelease(serialNumberAsCFString);
		}

		// Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface
		// for our device. This will create the necessary connections between our userland application and the
		// kernel object for the USB Device.
		kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);

		if((kIOReturnSuccess != kr) || !plugInInterface) {
			DEBUG_LOG("IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr);
			continue;
		}

		stDeviceListItem *deviceListItem = new stDeviceListItem();

		// Use the plugin interface to retrieve the device interface.
		res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceListItem->deviceInterface);

		// Now done with the plugin interface.
		(*plugInInterface)->Release(plugInInterface);

		if(res || deviceListItem->deviceInterface == NULL) {
			DEBUG_LOG("QueryInterface returned %d.\n", (int) res);
			continue;
		}

		// Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h.
		// In this case, fetch the locationID. The locationID uniquely identifies the device
		// and will remain the same, even across reboots, so long as the bus topology doesn't change.

		kr = (*deviceListItem->deviceInterface)->GetLocationID(deviceListItem->deviceInterface, &locationID);
		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("GetLocationID returned 0x%08x.\n", kr);
			continue;
		}
		deviceItem->deviceParams.locationId = locationID;

		kr = (*deviceListItem->deviceInterface)->GetDeviceAddress(deviceListItem->deviceInterface, &addr);
		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("GetDeviceAddress returned 0x%08x.\n", kr);
			continue;
		}
		deviceItem->deviceParams.deviceAddress = addr;

		kr = (*deviceListItem->deviceInterface)->GetDeviceVendor(deviceListItem->deviceInterface, &vendorId);
		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("GetDeviceVendor returned 0x%08x.\n", kr);
			continue;
		}
		deviceItem->deviceParams.vendorId = vendorId;

		kr = (*deviceListItem->deviceInterface)->GetDeviceProduct(deviceListItem->deviceInterface, &productId);
		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("GetDeviceProduct returned 0x%08x.\n", kr);
			continue;
		}
		deviceItem->deviceParams.productId = productId;

		// Extract path name as unique key
		io_string_t pathName;
		IORegistryEntryGetPath(usbDevice, kIOServicePlane, pathName);
		deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, pathName, kCFStringEncodingASCII);
		char cPathName[MAXPATHLEN];

		if(deviceNameAsCFString) {
			Boolean result;

			// Convert from a CFString to a C (NUL-terminated)
			result = CFStringGetCString(
					deviceNameAsCFString,
					cPathName,
					sizeof(cPathName),
					kCFStringEncodingUTF8
				);

			CFRelease(deviceNameAsCFString);
		}

		AddItemToList(cPathName, deviceItem);
		deviceListItem->deviceItem = deviceItem;

		if(initialDeviceImport == false) {
			WaitForDeviceHandled();
			currentItem = &deviceItem->deviceParams;
			isAdded = true;
			uv_async_send(&async_handler);
		}

		// Register for an interest notification of this device being removed. Use a reference to our
		// private data as the refCon which will be passed to the notification callback.
		kr = IOServiceAddInterestNotification(
				gNotifyPort, // notifyPort
				usbDevice, // service
				kIOGeneralInterest, // interestType
				DeviceRemoved, // callback
				deviceListItem, // refCon
				&(deviceListItem->notification) // notification
			);

		if(KERN_SUCCESS != kr) {
			DEBUG_LOG("IOServiceAddInterestNotification returned 0x%08x.\n", kr);
		}

		// Done with this USB device; release the reference added by IOIteratorNext
		kr = IOObjectRelease(usbDevice);
	}
}

void Start() {
	if(isRunning) {
		return;
	}

	isRunning = true;

	uv_mutex_init(&notify_mutex);
	uv_async_init(uv_default_loop(), &async_handler, cbAsync);
	uv_signal_init(uv_default_loop(), &term_signal);
	uv_signal_init(uv_default_loop(), &int_signal);
	uv_cond_init(&notifyDeviceHandled);

	uv_queue_work(uv_default_loop(), &work_req, cbWork, cbAfter);
}

void Stop() {
	if(!isRunning) {
		return;
	}

	isRunning = false;

	uv_mutex_destroy(&notify_mutex);
	uv_signal_stop(&int_signal);
	uv_signal_stop(&term_signal);
	uv_close((uv_handle_t *) &async_handler, NULL);
	uv_cond_destroy(&notifyDeviceHandled);

	if (gRunLoop) {
		CFRunLoopStop(gRunLoop);
	}
}

void InitDetection() {
	kern_return_t kr;

	// Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow
	// the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7.
	// See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X"
	// <http://developer.apple.com/qa/qa2001/qa1076.html>.
	// One exception is that you can use the matching dictionary "as is", i.e. without adding any matching
	// criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will
	// consume this dictionary reference, so there is no need to release it later on.

	// Interested in instances of class
	// IOUSBDevice and its subclasses
	matchingDict = IOServiceMatching(kIOUSBDeviceClassName);

	if (matchingDict == NULL) {
		DEBUG_LOG("IOServiceMatching returned NULL.\n");
	}

	// Create a notification port and add its run loop event source to our run loop
	// This is how async notifications get set up.

	gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);

	// Now set up a notification to be called when a device is first matched by I/O Kit.
	kr = IOServiceAddMatchingNotification(
			gNotifyPort, // notifyPort
			kIOFirstMatchNotification, // notificationType
			matchingDict, // matching
			DeviceAdded, // callback
			NULL, // refCon
			&gAddedIter // notification
		);

	if (KERN_SUCCESS != kr) {
		DEBUG_LOG("IOServiceAddMatchingNotification returned 0x%08x.\n", kr);
	}

	// Iterate once to get already-present devices and arm the notification
	DeviceAdded(NULL, gAddedIter);
	initialDeviceImport = false;
}

void EIO_Find(uv_work_t* req) {
	ListBaton* data = static_cast<ListBaton*>(req->data);

	CreateFilteredList(&data->results, data->vid, data->pid);
}

static void WaitForDeviceHandled() {
	uv_mutex_lock(&notify_mutex);
	if(deviceHandled == false) {
		uv_cond_wait(&notifyDeviceHandled, &notify_mutex);
	}
	deviceHandled = false;
	uv_mutex_unlock(&notify_mutex);
}

static void SignalDeviceHandled() {
	uv_mutex_lock(&notify_mutex);
	deviceHandled = true;
	uv_cond_signal(&notifyDeviceHandled);
	uv_mutex_unlock(&notify_mutex);
}

static void cbWork(uv_work_t *req) {
	// We have this check in case we `Stop` before this thread starts,
	// otherwise the process will hang
	if(!isRunning) {
		return;
	}

	uv_signal_start(&int_signal, cbTerminate, SIGINT);
	uv_signal_start(&term_signal, cbTerminate, SIGTERM);

	runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);

	gRunLoop = CFRunLoopGetCurrent();
	CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);

	// Creating `gRunLoop` can take some cycles so we also need this second
	// `isRunning` check here because it happens at a future time
	if(isRunning) {
		// Start the run loop. Now we'll receive notifications.
		CFRunLoopRun();
	}

	// The `CFRunLoopRun` is a blocking call so we also need this second
	// `isRunning` check here because it happens at a future time
	if(isRunning) {
		// We should never get here while running
		DEBUG_LOG("Unexpectedly back from CFRunLoopRun()!\n");
	}
}

static void cbAfter(uv_work_t *req, int status) {
	Stop();
}

static void cbAsync(uv_async_t *handle) {
	if(!isRunning) {
		return;
	}

	if(isAdded) {
		NotifyAdded(currentItem);
	}
	else {
		NotifyRemoved(currentItem);
	}

	// Delete Item in case of removal
	if(isAdded == false) {
		delete currentItem;
	}

	SignalDeviceHandled();
}

static void cbTerminate(uv_signal_t *handle, int signum) {
	Stop();
}
<|endoftext|>


<commit_before>/*=========================================================================

  Library:   CTK

  Copyright (c) Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=========================================================================*/

// ctk includes
#include "ctkDICOMTableManager.h"
#include "ctkDICOMTableView.h"

// Qt includes
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QRadioButton>
#include <QButtonGroup>
#include <QPushButton>
#include <QSplitter>

class ctkDICOMTableManagerPrivate
{
  Q_DECLARE_PUBLIC(ctkDICOMTableManager)

protected:
  ctkDICOMTableManager* const q_ptr;

public:
  ctkDICOMTableManagerPrivate(ctkDICOMTableManager& obj);
  ~ctkDICOMTableManagerPrivate();

  QVBoxLayout* layout;
  QBoxLayout* layoutTables;
  QPushButton* changeLayoutButton;
  QSplitter* tableSplitter;

  ctkDICOMTableView* patientsTable;
  ctkDICOMTableView* studiesTable;
  ctkDICOMTableView* seriesTable;

  void init();
  void setCTKDICOMDatabase(QSharedPointer<ctkDICOMDatabase> db);
};

ctkDICOMTableManagerPrivate::ctkDICOMTableManagerPrivate(ctkDICOMTableManager &obj)
  : q_ptr(&obj)
{

}

ctkDICOMTableManagerPrivate::~ctkDICOMTableManagerPrivate()
{

}

void ctkDICOMTableManagerPrivate::init()
{
  //setup UI
  Q_Q(ctkDICOMTableManager);

  this->layout = new QVBoxLayout();
  this->layoutTables = new QBoxLayout(QBoxLayout::LeftToRight);
  this->patientsTable = new ctkDICOMTableView(q, "Patients");
  this->studiesTable = new ctkDICOMTableView(q, "Studies");
  this->studiesTable->setQueryForeignKey("PatientsUID");
  this->seriesTable = new ctkDICOMTableView(q, "Series");
  this->seriesTable->setQueryForeignKey("StudyInstanceUID");

  QObject::connect(this->patientsTable, SIGNAL(signalSelectionChanged(QStringList)),
                   this->studiesTable, SLOT(onUpdateQuery(QStringList)));
  QObject::connect(this->studiesTable, SIGNAL(signalSelectionChanged(QStringList)),
                   this->seriesTable, SLOT(onUpdateQuery(QStringList)));
  QObject::connect(this->patientsTable, SIGNAL(signalFilterChanged(const QStringList&)),
                   this->studiesTable, SLOT(onUpdateQuery(const QStringList&)));
  QObject::connect(this->studiesTable, SIGNAL(signalFilterChanged(const QStringList&)),
                   this->seriesTable, SLOT(onUpdateQuery(const QStringList&)));
  QObject::connect(this->studiesTable, SIGNAL(signalQueryChanged(QStringList)),
                   this->seriesTable, SLOT(onUpdateQuery(const QStringList&)));

  //TODO Set the right sizepolicy
  this->patientsTable->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
  this->studiesTable->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
  this->seriesTable->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);

  tableSplitter = new QSplitter();
  tableSplitter->setChildrenCollapsible(false);

//  tableSplitter->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);

  tableSplitter->addWidget(this->patientsTable);
  tableSplitter->addWidget(this->studiesTable);
  tableSplitter->addWidget(this->seriesTable);

  QHBoxLayout* buttonLayout = new QHBoxLayout();
  this->changeLayoutButton = new QPushButton();
  this->changeLayoutButton->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum);
  QPixmap icon(":/Icons/vertical.png");
  this->changeLayoutButton->setIcon(icon);
  QObject::connect(this->changeLayoutButton, SIGNAL(clicked()), q, SLOT(onChangeLayoutPushed()));
  buttonLayout->addWidget(this->changeLayoutButton);

  QSpacerItem* spacer = new QSpacerItem(20,20,QSizePolicy::Expanding,QSizePolicy::Fixed);

  buttonLayout->addItem(spacer);
  this->layout->addLayout(buttonLayout);
  this->layout->addWidget(this->tableSplitter);

  q->setLayout(layout);
}

void ctkDICOMTableManagerPrivate::setCTKDICOMDatabase(QSharedPointer<ctkDICOMDatabase> db)
{
  this->patientsTable->setCTKDicomDataBase(db);
  this->studiesTable->setCTKDicomDataBase(db);
  this->seriesTable->setCTKDicomDataBase(db);
}

//----------------------------------------------------------------------------
// ctkDICOMTableManager methods

//----------------------------------------------------------------------------

ctkDICOMTableManager::ctkDICOMTableManager(QWidget *parent)
  :Superclass(parent)
  , d_ptr(new ctkDICOMTableManagerPrivate(*this))
{
  Q_D(ctkDICOMTableManager);
  d->init();
}

ctkDICOMTableManager::ctkDICOMTableManager(QSharedPointer<ctkDICOMDatabase> db, QWidget *parent)
  : Superclass(parent)
  , d_ptr(new ctkDICOMTableManagerPrivate(*this))
{
  Q_D(ctkDICOMTableManager);
  d->init();
  d->setCTKDICOMDatabase(db);
}

ctkDICOMTableManager::~ctkDICOMTableManager()
{

}

void ctkDICOMTableManager::onChangeLayoutPushed()
{
  Q_D(ctkDICOMTableManager);
  if(d->tableSplitter->orientation() == Qt::Vertical)
  {
    QPixmap icon(":/Icons/vertical.png");
    d->changeLayoutButton->setIcon(icon);
    d->tableSplitter->setOrientation(Qt::Horizontal);
  }
  else
  {
    QPixmap icon(":/Icons/horizontal.png");
    d->changeLayoutButton->setIcon(icon);
    d->tableSplitter->setOrientation(Qt::Vertical);
  }
}

void ctkDICOMTableManager::setCTKDICOMDatabase(QSharedPointer<ctkDICOMDatabase> db)
{
  Q_D(ctkDICOMTableManager);
  d->setCTKDICOMDatabase(db);
}
<commit_msg>Added stylesheet for QSplitter<commit_after>/*=========================================================================

  Library:   CTK

  Copyright (c) Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=========================================================================*/

// ctk includes
#include "ctkDICOMTableManager.h"
#include "ctkDICOMTableView.h"

// Qt includes
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QRadioButton>
#include <QButtonGroup>
#include <QPushButton>
#include <QSplitter>

class ctkDICOMTableManagerPrivate
{
  Q_DECLARE_PUBLIC(ctkDICOMTableManager)

protected:
  ctkDICOMTableManager* const q_ptr;

public:
  ctkDICOMTableManagerPrivate(ctkDICOMTableManager& obj);
  ~ctkDICOMTableManagerPrivate();

  QVBoxLayout* layout;
  QBoxLayout* layoutTables;
  QPushButton* changeLayoutButton;
  QSplitter* tableSplitter;

  ctkDICOMTableView* patientsTable;
  ctkDICOMTableView* studiesTable;
  ctkDICOMTableView* seriesTable;

  void init();
  void setCTKDICOMDatabase(QSharedPointer<ctkDICOMDatabase> db);
};

ctkDICOMTableManagerPrivate::ctkDICOMTableManagerPrivate(ctkDICOMTableManager &obj)
  : q_ptr(&obj)
{

}

ctkDICOMTableManagerPrivate::~ctkDICOMTableManagerPrivate()
{

}

void ctkDICOMTableManagerPrivate::init()
{
  //setup UI
  Q_Q(ctkDICOMTableManager);

  this->layout = new QVBoxLayout();
  this->layoutTables = new QBoxLayout(QBoxLayout::LeftToRight);
  this->patientsTable = new ctkDICOMTableView(q, "Patients");
  this->studiesTable = new ctkDICOMTableView(q, "Studies");
  this->studiesTable->setQueryForeignKey("PatientsUID");
  this->seriesTable = new ctkDICOMTableView(q, "Series");
  this->seriesTable->setQueryForeignKey("StudyInstanceUID");

  QObject::connect(this->patientsTable, SIGNAL(signalSelectionChanged(QStringList)),
                   this->studiesTable, SLOT(onUpdateQuery(QStringList)));
  QObject::connect(this->studiesTable, SIGNAL(signalSelectionChanged(QStringList)),
                   this->seriesTable, SLOT(onUpdateQuery(QStringList)));
  QObject::connect(this->patientsTable, SIGNAL(signalFilterChanged(const QStringList&)),
                   this->studiesTable, SLOT(onUpdateQuery(const QStringList&)));
  QObject::connect(this->studiesTable, SIGNAL(signalFilterChanged(const QStringList&)),
                   this->seriesTable, SLOT(onUpdateQuery(const QStringList&)));
  QObject::connect(this->studiesTable, SIGNAL(signalQueryChanged(QStringList)),
                   this->seriesTable, SLOT(onUpdateQuery(const QStringList&)));

  //TODO Set the right sizepolicy
  this->patientsTable->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
  this->studiesTable->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
  this->seriesTable->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);

  tableSplitter = new QSplitter();
  tableSplitter->setChildrenCollapsible(false);

  tableSplitter->addWidget(this->patientsTable);
  tableSplitter->addWidget(this->studiesTable);
  tableSplitter->addWidget(this->seriesTable);

  tableSplitter->setStyleSheet("QSplitter::handle {background-color: rgb(224,224,224);}QSplitter::handle:horizontal {width: 2px;}QSplitter::handle:vertical {height: 2px;}");

  QHBoxLayout* buttonLayout = new QHBoxLayout();
  this->changeLayoutButton = new QPushButton();
  this->changeLayoutButton->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum);
  QPixmap icon(":/Icons/vertical.png");
  this->changeLayoutButton->setIcon(icon);
  QObject::connect(this->changeLayoutButton, SIGNAL(clicked()), q, SLOT(onChangeLayoutPushed()));
  buttonLayout->addWidget(this->changeLayoutButton);

  QSpacerItem* spacer = new QSpacerItem(20,20,QSizePolicy::Expanding,QSizePolicy::Fixed);

  buttonLayout->addItem(spacer);
  this->layout->addLayout(buttonLayout);
  this->layout->addWidget(this->tableSplitter);

  q->setLayout(layout);
}

void ctkDICOMTableManagerPrivate::setCTKDICOMDatabase(QSharedPointer<ctkDICOMDatabase> db)
{
  this->patientsTable->setCTKDicomDataBase(db);
  this->studiesTable->setCTKDicomDataBase(db);
  this->seriesTable->setCTKDicomDataBase(db);
}

//----------------------------------------------------------------------------
// ctkDICOMTableManager methods

//----------------------------------------------------------------------------

ctkDICOMTableManager::ctkDICOMTableManager(QWidget *parent)
  :Superclass(parent)
  , d_ptr(new ctkDICOMTableManagerPrivate(*this))
{
  Q_D(ctkDICOMTableManager);
  d->init();
}

ctkDICOMTableManager::ctkDICOMTableManager(QSharedPointer<ctkDICOMDatabase> db, QWidget *parent)
  : Superclass(parent)
  , d_ptr(new ctkDICOMTableManagerPrivate(*this))
{
  Q_D(ctkDICOMTableManager);
  d->init();
  d->setCTKDICOMDatabase(db);
}

ctkDICOMTableManager::~ctkDICOMTableManager()
{

}

void ctkDICOMTableManager::onChangeLayoutPushed()
{
  Q_D(ctkDICOMTableManager);
  if(d->tableSplitter->orientation() == Qt::Vertical)
  {
    QPixmap icon(":/Icons/vertical.png");
    d->changeLayoutButton->setIcon(icon);
    d->tableSplitter->setOrientation(Qt::Horizontal);
  }
  else
  {
    QPixmap icon(":/Icons/horizontal.png");
    d->changeLayoutButton->setIcon(icon);
    d->tableSplitter->setOrientation(Qt::Vertical);
  }
}

void ctkDICOMTableManager::setCTKDICOMDatabase(QSharedPointer<ctkDICOMDatabase> db)
{
  Q_D(ctkDICOMTableManager);
  d->setCTKDICOMDatabase(db);
}
<|endoftext|>


<commit_before>/*
 * Functions deal with the regex banning.
 *
 * Copyright (c) eQualit.ie 2013 under GNU AGPL v3.0 or later
 *
 *  Vmon: June 2013, Initial version
 */
#include "regex_manager.h"

using namespace std;

/**
  reads all the regular expressions from the database.
  and compile them
 */
void RegexManager::load_config()
{
  try
  {
    print::debug("RegexManager::load_config");

    RE2::Options opt;
    opt.set_log_errors(false);
    opt.set_perl_classes(true);
    opt.set_posix_syntax(true);

    for (const auto& node : cfg) {
      string cur_rule = node["rule"].as<std::string>();

      print::debug("RegexManager: Initiating rule ", cur_rule);

      unsigned int observation_interval = node["interval"].as<unsigned int>();
      unsigned int threshold = node["hits_per_interval"].as<unsigned int>();

      std::vector<std::string> hosts_to_skip {};
      if (node["hosts_to_skip"]) {
        for (auto it: node["hosts_to_skip"].as<vector<string>>()) {
          hosts_to_skip.emplace_back(it);
        }
      }

      rated_banning_regexes.emplace_back(
          new RatedRegex(rated_banning_regexes.size(),
                         cur_rule,
                         new RE2(node["regex"].as<std::string>(), opt),
                         observation_interval * 1000,
                         threshold /(double)(observation_interval* 1000),
                         hosts_to_skip));
    }

    total_no_of_rules = rated_banning_regexes.size();
  }
  catch(YAML::RepresentationException& e)
  {
    print::debug("RegexManager: Error loading regex manager conf [",e.what(),"].");
    throw;
  }
}

RegexManager::RatedRegex*
RegexManager::try_match(const string& ip, const string& ats_record, const string& client_request_host) const
{
  boost::optional<IpDb::IpState> ip_state;

  for(const auto& rule : rated_banning_regexes) {
    if (rule->hosts_to_skip.find(client_request_host) != rule->hosts_to_skip.end()) {
      return nullptr;
    }

    if (RE2::FullMatch(ats_record, *(rule->re2_regex))) {
      print::debug("RegexManager: Request matched ", rule->re2_regex->pattern());
      //if it is a simple regex i.e. with rate 0 we bans immidiately without
      //wasting time and mem
      if (rule->rate == 0) {
        print::debug("RegexManager: Simple regex, ban immediately");
        return rule.get();
      }
      //select appropriate rate, dependent on whether GET or POST request

      /* we need to check the rate condition here */
      //getting current time in msec
      timeval cur_time; gettimeofday(&cur_time, NULL);
      long cur_time_msec = cur_time.tv_sec * 1000 + cur_time.tv_usec / 1000.0;

      /* first we check if we already have retreived the state for this ip */
      if (!ip_state || ip_state->size() == 0) {
        ip_state =  regex_manager_ip_db->get_ip_state(ip);
        if (!ip_state) //we failed to read the database we make no judgement
          continue;
      }

      //if we succeeded in retreiving but the size is zero then it meaens we don't have a record of this ip at all
      if (ip_state->size() == 0) {
        ip_state->resize(total_no_of_rules);
      }

      //now we check the begining of the hit for this regex (if the vector is just created everything is 0, in case
      //this is the first regex this ip hits)
      RegexState state = (*ip_state)[rule->id];

      //if it is 0, then we don't have a record for
      //the current regex
      if (state.begin_msec == 0) {
        state.begin_msec = cur_time_msec;
      }

      //now we have a record, update the rate and ban if necessary.
      //we move the interval by the differences of the "begin_in_ms - cur_time_msec - interval*1000"
      //if it is less than zero we don't do anything just augument the rate
      long time_window_movement = cur_time_msec - state.begin_msec - rule->interval;
      if (time_window_movement > 0) { //we need to move
        state.begin_msec += time_window_movement;
        state.rate = state.rate - (state.rate * time_window_movement - 1)/(double) rule->interval;
        state.rate = state.rate < 0 ? 1/(double) rule->interval : state.rate; //if time_window_movement > time_window_movementrule->interval, then we just wipe history and start as a new interval
      }
      else {
        //we are still in the same interval so just increase the hit by 1
        state.rate += 1/(double) rule->interval;
      }

      print::debug("RegexManager: with rate ",state.rate," /msec");

      (*ip_state)[rule->id] = state;

      if (state.rate >= rule->rate) {
        print::debug("Exceeding excessive rate ",rule->rate," /msec");
        //clear the record to avoid multiple reporting to swabber
        //we are not clearing the state cause it is not for sure that
        //swabber ban the ip due to possible failure of acquiring lock
        // ip_state.detail.begin_msec = 0;
        // ip_state.detail.rate = 0;
        // regex_manager_ip_db->set_ip_state(ip, ip_state.state_allocator);

        //before calling swabber we need to delete the memory as swabber will
        //delete the ip database entery and the memory will be lost.
        //However, if swabber fails to acquire a lock this means
        //the ip can escape the banning, this however is a miner
        //concern compared to the fact that we might ran out of
        //memory.

        regex_manager_ip_db->set_ip_state(ip, *ip_state);
        return rule.get();
      }
    }
  }

  //if we managed to get/make a valid state we are going to store it
  if (ip_state && ip_state->size() > 0)
    regex_manager_ip_db->set_ip_state(ip, *ip_state);

  //no match
  return nullptr;
}

FilterResponse RegexManager::on_http_request(const TransactionParts& transaction_parts)
{
  TransactionParts ats_record_parts = const_cast<TransactionParts&>(transaction_parts);

  string ats_record;
  ats_record.reserve(ats_record_parts[TransactionMuncher::METHOD].size() +
                     ats_record_parts[TransactionMuncher::URL].size() +
                     ats_record_parts[TransactionMuncher::HOST].size() +
                     ats_record_parts[TransactionMuncher::UA].size() +
                     1 + 3);
  ats_record += ats_record_parts[TransactionMuncher::METHOD];
  ats_record += " ";
  ats_record += ats_record_parts[TransactionMuncher::URL];
  ats_record += " ";
  ats_record += ats_record_parts[TransactionMuncher::HOST];
  ats_record += " ";
  ats_record += ats_record_parts[TransactionMuncher::UA];

  print::debug("RegexManager: Examining '",ats_record,"' for banned matches");

  RatedRegex* result = try_match(
						ats_record_parts[TransactionMuncher::IP],
						ats_record,
						ats_record_parts[TransactionMuncher::HOST]);

  if (result) {
    print::debug("Asking swabber to ban client ip: ", ats_record_parts[TransactionMuncher::IP]);

    //here instead we are calling nosmos's banning client
    string banning_reason
      = "matched regex rule " + result->rule_name + ", "
      + ats_record_parts[TransactionMuncher::METHOD] + ", "
      + encapsulate_in_quotes(ats_record_parts[TransactionMuncher::URL]) + ", "
      + ats_record_parts[TransactionMuncher::HOST] + ", "
      + encapsulate_in_quotes(ats_record_parts[TransactionMuncher::UA]);

    swabber->ban(ats_record_parts[TransactionMuncher::IP], banning_reason);

    return FilterResponse([&](const TransactionParts& a, const FilterResponse& b) { return this->generate_response(a, b); });
  }

  return FilterResponse(FilterResponse::GO_AHEAD_NO_COMMENT);
}

std::string RegexManager::generate_response(const TransactionParts& transaction_parts, const FilterResponse& response_info)
{
  (void)transaction_parts; (void)response_info;
  return forbidden_message;
}
<commit_msg>avoid extra copy construction<commit_after>/*
 * Functions deal with the regex banning.
 *
 * Copyright (c) eQualit.ie 2013 under GNU AGPL v3.0 or later
 *
 *  Vmon: June 2013, Initial version
 */
#include "regex_manager.h"

using namespace std;

/**
  reads all the regular expressions from the database.
  and compile them
 */
void RegexManager::load_config()
{
  try
  {
    print::debug("RegexManager::load_config");

    RE2::Options opt;
    opt.set_log_errors(false);
    opt.set_perl_classes(true);
    opt.set_posix_syntax(true);

    for (const auto& node : cfg) {
      string cur_rule = node["rule"].as<std::string>();

      print::debug("RegexManager: Initiating rule ", cur_rule);

      unsigned int observation_interval = node["interval"].as<unsigned int>();
      unsigned int threshold = node["hits_per_interval"].as<unsigned int>();

      std::vector<std::string> hosts_to_skip {};
      if (node["hosts_to_skip"]) {
        for (auto it: node["hosts_to_skip"].as<vector<string>>()) {
          hosts_to_skip.emplace_back(it);
        }
      }

      rated_banning_regexes.emplace_back(
          new RatedRegex(rated_banning_regexes.size(),
                         cur_rule,
                         new RE2(node["regex"].as<std::string>(), opt),
                         observation_interval * 1000,
                         threshold /(double)(observation_interval* 1000),
                         hosts_to_skip));
    }

    total_no_of_rules = rated_banning_regexes.size();
  }
  catch(YAML::RepresentationException& e)
  {
    print::debug("RegexManager: Error loading regex manager conf [",e.what(),"].");
    throw;
  }
}

RegexManager::RatedRegex*
RegexManager::try_match(const string& ip, const string& ats_record, const string& client_request_host) const
{
  boost::optional<IpDb::IpState> ip_state;

  for(const auto& rule : rated_banning_regexes) {
    if (rule->hosts_to_skip.find(client_request_host) != rule->hosts_to_skip.end()) {
      return nullptr;
    }

    if (RE2::FullMatch(ats_record, *(rule->re2_regex))) {
      print::debug("RegexManager: Request matched ", rule->re2_regex->pattern());
      //if it is a simple regex i.e. with rate 0 we bans immidiately without
      //wasting time and mem
      if (rule->rate == 0) {
        print::debug("RegexManager: Simple regex, ban immediately");
        return rule.get();
      }
      //select appropriate rate, dependent on whether GET or POST request

      /* we need to check the rate condition here */
      //getting current time in msec
      timeval cur_time; gettimeofday(&cur_time, NULL);
      long cur_time_msec = cur_time.tv_sec * 1000 + cur_time.tv_usec / 1000.0;

      /* first we check if we already have retreived the state for this ip */
      if (!ip_state || ip_state->size() == 0) {
        ip_state =  regex_manager_ip_db->get_ip_state(ip);
        if (!ip_state) //we failed to read the database we make no judgement
          continue;
      }

      //if we succeeded in retreiving but the size is zero then it meaens we don't have a record of this ip at all
      if (ip_state->size() == 0) {
        ip_state->resize(total_no_of_rules);
      }

      //now we check the begining of the hit for this regex (if the vector is just created everything is 0, in case
      //this is the first regex this ip hits)
      RegexState state = (*ip_state)[rule->id];

      //if it is 0, then we don't have a record for
      //the current regex
      if (state.begin_msec == 0) {
        state.begin_msec = cur_time_msec;
      }

      //now we have a record, update the rate and ban if necessary.
      //we move the interval by the differences of the "begin_in_ms - cur_time_msec - interval*1000"
      //if it is less than zero we don't do anything just augument the rate
      long time_window_movement = cur_time_msec - state.begin_msec - rule->interval;
      if (time_window_movement > 0) { //we need to move
        state.begin_msec += time_window_movement;
        state.rate = state.rate - (state.rate * time_window_movement - 1)/(double) rule->interval;
        state.rate = state.rate < 0 ? 1/(double) rule->interval : state.rate; //if time_window_movement > time_window_movementrule->interval, then we just wipe history and start as a new interval
      }
      else {
        //we are still in the same interval so just increase the hit by 1
        state.rate += 1/(double) rule->interval;
      }

      print::debug("RegexManager: with rate ",state.rate," /msec");

      (*ip_state)[rule->id] = state;

      if (state.rate >= rule->rate) {
        print::debug("Exceeding excessive rate ",rule->rate," /msec");
        //clear the record to avoid multiple reporting to swabber
        //we are not clearing the state cause it is not for sure that
        //swabber ban the ip due to possible failure of acquiring lock
        // ip_state.detail.begin_msec = 0;
        // ip_state.detail.rate = 0;
        // regex_manager_ip_db->set_ip_state(ip, ip_state.state_allocator);

        //before calling swabber we need to delete the memory as swabber will
        //delete the ip database entery and the memory will be lost.
        //However, if swabber fails to acquire a lock this means
        //the ip can escape the banning, this however is a miner
        //concern compared to the fact that we might ran out of
        //memory.

        regex_manager_ip_db->set_ip_state(ip, *ip_state);
        return rule.get();
      }
    }
  }

  //if we managed to get/make a valid state we are going to store it
  if (ip_state && ip_state->size() > 0)
    regex_manager_ip_db->set_ip_state(ip, *ip_state);

  //no match
  return nullptr;
}

FilterResponse RegexManager::on_http_request(const TransactionParts& transaction_parts)
{
  TransactionParts& ats_record_parts = const_cast<TransactionParts&>(transaction_parts);

  string ats_record;
  ats_record.reserve(ats_record_parts[TransactionMuncher::METHOD].size() +
                     ats_record_parts[TransactionMuncher::URL].size() +
                     ats_record_parts[TransactionMuncher::HOST].size() +
                     ats_record_parts[TransactionMuncher::UA].size() +
                     1 + 3);
  ats_record += ats_record_parts[TransactionMuncher::METHOD];
  ats_record += " ";
  ats_record += ats_record_parts[TransactionMuncher::URL];
  ats_record += " ";
  ats_record += ats_record_parts[TransactionMuncher::HOST];
  ats_record += " ";
  ats_record += ats_record_parts[TransactionMuncher::UA];

  print::debug("RegexManager: Examining '",ats_record,"' for banned matches");

  RatedRegex* result = try_match(
						ats_record_parts[TransactionMuncher::IP],
						ats_record,
						ats_record_parts[TransactionMuncher::HOST]);

  if (result) {
    print::debug("Asking swabber to ban client ip: ", ats_record_parts[TransactionMuncher::IP]);

    //here instead we are calling nosmos's banning client
    string banning_reason
      = "matched regex rule " + result->rule_name + ", "
      + ats_record_parts[TransactionMuncher::METHOD] + ", "
      + encapsulate_in_quotes(ats_record_parts[TransactionMuncher::URL]) + ", "
      + ats_record_parts[TransactionMuncher::HOST] + ", "
      + encapsulate_in_quotes(ats_record_parts[TransactionMuncher::UA]);

    swabber->ban(ats_record_parts[TransactionMuncher::IP], banning_reason);

    return FilterResponse([&](const TransactionParts& a, const FilterResponse& b) { return this->generate_response(a, b); });
  }

  return FilterResponse(FilterResponse::GO_AHEAD_NO_COMMENT);
}

std::string RegexManager::generate_response(const TransactionParts& transaction_parts, const FilterResponse& response_info)
{
  (void)transaction_parts; (void)response_info;
  return forbidden_message;
}
<|endoftext|>


<commit_before>#include <gbBase/Assert.hpp>

#ifndef GHULBUS_CONFIG_BASE_BARE_BUILD
#include <gbBase/Exception.hpp>

#include <boost/exception/all.hpp>
#endif

#include <chrono>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <thread>

namespace GHULBUS_BASE_NAMESPACE
{
namespace
{
Assert::Handler g_AssertionHandler(&Assert::failAbort);

std::ostream& operator<<(std::ostream& os, Assert::HandlerParameters const& handler_param)
{
    os << handler_param.file << "(" << handler_param.line << "): Assertion failed "
       << " in function " << handler_param.function << ": " << handler_param.condition;
    if (handler_param.message) {
        os << " - " << handler_param.message;
    }
    return os;
}

std::string toString(Assert::HandlerParameters const& handler_param)
{
    std::stringstream sstr;
    sstr << handler_param;
    return sstr.str();
}
}

namespace Assert
{
void setAssertionHandler(Handler handler) noexcept
{
    g_AssertionHandler = handler;
}

Handler getAssertionHandler() noexcept
{
    return g_AssertionHandler;
}

void assertionFailed(HandlerParameters const& param)
{
    getAssertionHandler()(param);
}

void failAbort(HandlerParameters const& param)
{
    // keep the error message in a string for easy inspection by the debugger
    std::string const str = toString(param);
    std::cerr << str << std::endl;
    std::abort();
}

void failHalt(HandlerParameters const& param)
{
    // keep the error message in a string for easy inspection by the debugger
    std::string const str = toString(param);
    std::cerr << str << std::endl;
    for(;;) { std::this_thread::sleep_for(std::chrono::hours(1)); }
}

#ifndef GHULBUS_CONFIG_BASE_BARE_BUILD
void failThrow(HandlerParameters const& param)
{
    boost::throw_exception(boost::enable_error_info(Exceptions::AssertFailed()) <<
                           boost::throw_file(param.file) <<
                           boost::throw_line(param.line) <<
                           boost::throw_function(param.function) <<
                           Exception_Info::description(std::string(param.condition) +
                               (param.message ? " - " : "") + (param.message ? param.message : "")));
}
#endif
}
}
<commit_msg>added local variables to help debugging<commit_after>#include <gbBase/Assert.hpp>

#ifndef GHULBUS_CONFIG_BASE_BARE_BUILD
#include <gbBase/Exception.hpp>

#include <boost/exception/all.hpp>
#endif

#include <chrono>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <thread>

namespace GHULBUS_BASE_NAMESPACE
{
namespace
{
Assert::Handler g_AssertionHandler(&Assert::failAbort);

std::ostream& operator<<(std::ostream& os, Assert::HandlerParameters const& handler_param)
{
    os << handler_param.file << "(" << handler_param.line << "): Assertion failed "
       << " in function " << handler_param.function << ": " << handler_param.condition;
    if (handler_param.message) {
        os << " - " << handler_param.message;
    }
    return os;
}

std::string toString(Assert::HandlerParameters const& handler_param)
{
    std::stringstream sstr;
    sstr << handler_param;
    return sstr.str();
}
}

namespace Assert
{
void setAssertionHandler(Handler handler) noexcept
{
    auto& static_handler = g_AssertionHandler;
    static_handler = handler;
}

Handler getAssertionHandler() noexcept
{
    auto const& static_handler = g_AssertionHandler;
    return static_handler;
}

void assertionFailed(HandlerParameters const& param)
{
    getAssertionHandler()(param);
}

void failAbort(HandlerParameters const& param)
{
    // keep the error message in a string for easy inspection by the debugger
    std::string const str = toString(param);
    std::cerr << str << std::endl;
    std::abort();
}

void failHalt(HandlerParameters const& param)
{
    // keep the error message in a string for easy inspection by the debugger
    std::string const str = toString(param);
    std::cerr << str << std::endl;
    for(;;) { std::this_thread::sleep_for(std::chrono::hours(1)); }
}

#ifndef GHULBUS_CONFIG_BASE_BARE_BUILD
void failThrow(HandlerParameters const& param)
{
    boost::throw_exception(boost::enable_error_info(Exceptions::AssertFailed()) <<
                           boost::throw_file(param.file) <<
                           boost::throw_line(param.line) <<
                           boost::throw_function(param.function) <<
                           Exception_Info::description(std::string(param.condition) +
                               (param.message ? " - " : "") + (param.message ? param.message : "")));
}
#endif
}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
#include "dict.h"
#include "dictglobal.h"

typedef std::unordered_map<std::string, std::string> Dict;
typedef unsigned long IdentificatorType;

// consider map<unsigned long, unordered_map>
// somehow initialize dictglobal dictionary
std::map<IdentificatorType, Dict> dictionaries;
IdentificatorType dictCounter = dict_global();

IdentificatorType dict_new() {
    dictionaries.insert(std::pair<IdentificatorType, Dict>(++dictCounter, Dict()));

    return dictCounter;
}

void dict_remove(IdentificatorType id, const char* key) {
    auto dictionaryIt = dictionaries.find(id);
    if (dictionaryIt != dictionaries.end()) {
        dictionaryIt->second.erase(key);
    }
}

const char* dict_find(IdentificatorType id, const char* key) {
    auto dictionaryIt = dictionaries.find(id);
    if (dictionaryIt != dictionaries.end()) {
        auto stringIt = dictionaryIt->second.find(key);
        if (stringIt != dictionaryIt->second.end()) {
            return stringIt->second.c_str();
        }
    }

    auto stringIt = dictionaries.at(0).find(key);
    if (stringIt != dictionaries.at(0).end()) {
        return stringIt->second.c_str();
    }

    return NULL;
}

void dict_clear(IdentificatorType id) {
    auto dictionaryIt = dictionaries.find(id);
    if (dictionaryIt != dictionaries.end()) {
        dictionaryIt->second.clear();
    }
}

void dict_copy(IdentificatorType src_id, IdentificatorType dst_id) {
    auto srcDictionaryIt = dictionaries.find(src_id);
    auto dstDictionaryIt = dictionaries.find(dst_id);
    if (srcDictionaryIt != dictionaries.end() &&
        dstDictionaryIt != dictionaries.end()) {
        // do not copy if destination dictionary is dictglobal and amount of keys exceeds size

        // copy contents
    }
}<commit_msg>Several error message handling functions<commit_after>#include <iostream>
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
#include "dict.h"
#include "dictglobal.h"

typedef std::unordered_map<std::string, std::string> Dict;
typedef unsigned long IdentificatorType;

// consider map<unsigned long, unordered_map>
// somehow initialize dictglobal dictionary
std::map<IdentificatorType, Dict> dictionaries;
IdentificatorType dictCounter = dict_global();

IdentificatorType dict_new() {
    dictionaries.insert(std::pair<IdentificatorType, Dict>(++dictCounter, Dict()));

    return dictCounter;
}

void dict_remove(IdentificatorType id, const char* key) {
    auto dictionaryIt = dictionaries.find(id);
    if (dictionaryIt != dictionaries.end()) {
        dictionaryIt->second.erase(key);
    }
}

const char* dict_find(IdentificatorType id, const char* key) {
    auto dictionaryIt = dictionaries.find(id);
    if (dictionaryIt != dictionaries.end()) {
        auto stringIt = dictionaryIt->second.find(key);
        if (stringIt != dictionaryIt->second.end()) {
            return stringIt->second.c_str();
        }
    }

    auto stringIt = dictionaries.at(0).find(key);
    if (stringIt != dictionaries.at(0).end()) {
        return stringIt->second.c_str();
    }

    return NULL;
}

void dict_clear(IdentificatorType id) {
    auto dictionaryIt = dictionaries.find(id);
    if (dictionaryIt != dictionaries.end()) {
        dictionaryIt->second.clear();
    }
}

void dict_copy(IdentificatorType src_id, IdentificatorType dst_id) {
    auto srcDictionaryIt = dictionaries.find(src_id);
    auto dstDictionaryIt = dictionaries.find(dst_id);
    if (srcDictionaryIt != dictionaries.end() &&
        dstDictionaryIt != dictionaries.end()) {
        // do not copy if destination dictionary is dictglobal and amount of keys exceeds size

        // copy contents
    }
}

static void dict_not_found_error(std::string funcName, IdentificatorType id) {
    std::cerr << funcName << ": dict " << id << " does not exist" << std::endl;
}

static void key_not_in_dictionary_error(std::string funcName,
                                        IdentificatorType id,
                                        const char* key) {
    std::cerr << funcName << ": dict " << id
              << " does not contain the key " << key << std::endl;
}<|endoftext|>


<commit_before>#pragma once

#include <mbed.h>
#include <rtos.h>

#include <algorithm>
#include <map>
#include <stdexcept>
#include <functional>

template <class T>
class CommPort {
public:
    CommPort(uint8_t number = 0, std::function<T> rxC = nullptr,
             std::function<T> txC = nullptr)
        : _isOpen(false), _rxCallback(rxC), _txCallback(txC) {
        portNbr(number);
        resetPacketCount();
    };

    // Compare between 2 CommPort objects
    bool operator==(const CommPort& p) const {
        return portNbr() == p.portNbr();
    }

    // Overload the less than operator for sorting/finding ports using iterators
    bool operator<(const CommPort& p) const { return portNbr() < p.portNbr(); }

    uint8_t portNbr() const { return _nbr; }
    void portNbr(uint8_t nbr) { _nbr = nbr; }

    bool valid() const { return _nbr != 0; }

    // Open a port or check if a port is capable of providing communication.
    void open() { _isOpen = true; }
    void close() { _isOpen = false; }

    bool isOpen() const { return _isOpen; }

    bool isReady() const {
        return isOpen() && hasTxCallback() && hasRxCallback();
    }

    // Set functions for each RX/TX callback.
    void setRxCallback(const std::function<T>& func) { _rxCallback = func; }
    void setTxCallback(const std::function<T>& func) { _txCallback = func; }

    // Methods that return a reference to the TX/RX callback function pointers
    std::function<T>& rxCallback() { return _rxCallback; }
    std::function<T>& txCallback() { return _txCallback; }

    // Check if an RX/TX callback function has been set for the port.
    bool hasTxCallback() const { return _txCallback != nullptr; }
    bool hasRxCallback() const { return _rxCallback != nullptr; }

    // Get a value or reference to the TX/RX packet count for modifying
    unsigned int txCount() const { return _txCount; }
    unsigned int rxCount() const { return _rxCount; }

    void incTxCount() { _txCount++; }
    void incRxCount() { _rxCount++; }

    // Standard display function for a CommPort
    void printPort() const {
        printf("%2u\t\t%u\t%u\t%s\t\t%s\t\t%s\r\n", portNbr(), rxCount(),
               txCount(), hasRxCallback() ? "YES" : "NO",
               hasTxCallback() ? "YES" : "NO", isOpen() ? "OPEN" : "CLOSED");

        Console::Instance()->Flush();
    }

    // Returns the current packet counts to zero
    void resetPacketCount() {
        _rxCount = 0;
        _txCount = 0;
    }

private:
    // The number assigned to the port
    uint8_t _nbr;

    // If the port is open, it will also be valid
    bool _isOpen;

    // Where each upstream & downstream packet count is stored
    unsigned int _rxCount, _txCount;

    // the class members that hold the function pointers
    std::function<T> _rxCallback, _txCallback;
};

/// Class to manage the available/unavailable ports
template <class T>
class CommPorts {
public:
    CommPorts<T> operator+=(const CommPort<T>& p) {
        if (_ports.find(p.portNbr()) != _ports.end()) {
            // the port already exists
            LOG(WARNING, "Overwriting existing port '%d'", p.portNbr());
        }

        _ports[p.portNbr()] = p;

        return *this;
    }

    CommPort<T>& operator[](uint8_t portportNbr) { return _ports[portportNbr]; }

    bool hasPort(uint8_t portportNbr) const {
        return _ports.find(portportNbr) != _ports.end();
    }

    int count() const { return _ports.size(); }

    int countOpen() const {
        int count = 0;

        for (auto& kvpair : _ports) {
            if (kvpair.second.isOpen()) count++;
        }

        return count;
    }

    bool empty() const { return _ports.empty(); }

    // Get the total count (across all ports) of each RX/TX packet count
    unsigned int totalRxCount() const {
        unsigned int count = 0;

        for (auto& kvpair : _ports) {
            count += kvpair.second.rxCount();
        }

        return count;
    }
    unsigned int totalTxCount() const {
        unsigned int count = 0;

        for (auto& kvpair : _ports) {
            count += kvpair.second.txCount();
        }

        return count;
    }

    void printPorts() const {
        for (auto& kvpair : _ports) {
            kvpair.second.printPort();
        }
    }

    void printHeader() const {
        printf("PORT\t\tIN\tOUT\tRX CBCK\t\tTX CBCK\t\tSTATE\r\n");
        Console::Instance()->Flush();
    }

    void printFooter() const {
        printf(
            "==========================\r\n"
            "Total:\t\t%u\t%u\r\n",
            totalRxCount(), totalTxCount());
        Console::Instance()->Flush();
    }

private:
    std::map<uint8_t, CommPort<T>> _ports;
};
<commit_msg>fix typo<commit_after>#pragma once

#include <mbed.h>
#include <rtos.h>
#include "logger.hpp"

#include <algorithm>
#include <map>
#include <stdexcept>
#include <functional>

template <class T>
class CommPort {
public:
    CommPort(uint8_t number = 0, std::function<T> rxC = nullptr,
             std::function<T> txC = nullptr)
        : _isOpen(false), _rxCallback(rxC), _txCallback(txC) {
        portNbr(number);
        resetPacketCount();
    };

    // Compare between 2 CommPort objects
    bool operator==(const CommPort& p) const {
        return portNbr() == p.portNbr();
    }

    // Overload the less than operator for sorting/finding ports using iterators
    bool operator<(const CommPort& p) const { return portNbr() < p.portNbr(); }

    uint8_t portNbr() const { return _nbr; }
    void portNbr(uint8_t nbr) { _nbr = nbr; }

    bool valid() const { return _nbr != 0; }

    // Open a port or check if a port is capable of providing communication.
    void open() { _isOpen = true; }
    void close() { _isOpen = false; }

    bool isOpen() const { return _isOpen; }

    bool isReady() const {
        return isOpen() && hasTxCallback() && hasRxCallback();
    }

    // Set functions for each RX/TX callback.
    void setRxCallback(const std::function<T>& func) { _rxCallback = func; }
    void setTxCallback(const std::function<T>& func) { _txCallback = func; }

    // Methods that return a reference to the TX/RX callback function pointers
    std::function<T>& rxCallback() { return _rxCallback; }
    std::function<T>& txCallback() { return _txCallback; }

    // Check if an RX/TX callback function has been set for the port.
    bool hasTxCallback() const { return _txCallback != nullptr; }
    bool hasRxCallback() const { return _rxCallback != nullptr; }

    // Get a value or reference to the TX/RX packet count for modifying
    unsigned int txCount() const { return _txCount; }
    unsigned int rxCount() const { return _rxCount; }

    void incTxCount() { _txCount++; }
    void incRxCount() { _rxCount++; }

    // Standard display function for a CommPort
    void printPort() const {
        printf("%2u\t\t%u\t%u\t%s\t\t%s\t\t%s\r\n", portNbr(), rxCount(),
               txCount(), hasRxCallback() ? "YES" : "NO",
               hasTxCallback() ? "YES" : "NO", isOpen() ? "OPEN" : "CLOSED");

        Console::Instance()->Flush();
    }

    // Returns the current packet counts to zero
    void resetPacketCount() {
        _rxCount = 0;
        _txCount = 0;
    }

private:
    // The number assigned to the port
    uint8_t _nbr;

    // If the port is open, it will also be valid
    bool _isOpen;

    // Where each upstream & downstream packet count is stored
    unsigned int _rxCount, _txCount;

    // the class members that hold the function pointers
    std::function<T> _rxCallback, _txCallback;
};

/// Class to manage the available/unavailable ports
template <class T>
class CommPorts {
public:
    CommPorts<T> operator+=(const CommPort<T>& p) {
        if (_ports.find(p.portNbr()) != _ports.end()) {
            // the port already exists
            LOG(WARN, "Overwriting existing port '%d'", p.portNbr());
        }

        _ports[p.portNbr()] = p;

        return *this;
    }

    CommPort<T>& operator[](uint8_t portportNbr) { return _ports[portportNbr]; }

    bool hasPort(uint8_t portportNbr) const {
        return _ports.find(portportNbr) != _ports.end();
    }

    int count() const { return _ports.size(); }

    int countOpen() const {
        int count = 0;

        for (auto& kvpair : _ports) {
            if (kvpair.second.isOpen()) count++;
        }

        return count;
    }

    bool empty() const { return _ports.empty(); }

    // Get the total count (across all ports) of each RX/TX packet count
    unsigned int totalRxCount() const {
        unsigned int count = 0;

        for (auto& kvpair : _ports) {
            count += kvpair.second.rxCount();
        }

        return count;
    }
    unsigned int totalTxCount() const {
        unsigned int count = 0;

        for (auto& kvpair : _ports) {
            count += kvpair.second.txCount();
        }

        return count;
    }

    void printPorts() const {
        for (auto& kvpair : _ports) {
            kvpair.second.printPort();
        }
    }

    void printHeader() const {
        printf("PORT\t\tIN\tOUT\tRX CBCK\t\tTX CBCK\t\tSTATE\r\n");
        Console::Instance()->Flush();
    }

    void printFooter() const {
        printf(
            "==========================\r\n"
            "Total:\t\t%u\t%u\r\n",
            totalRxCount(), totalTxCount());
        Console::Instance()->Flush();
    }

private:
    std::map<uint8_t, CommPort<T>> _ports;
};
<|endoftext|>


<commit_before>class Solution {
public:
    int majorityElement(vector<int>& nums) {
        stack<int> nums_stack;
        for( auto i : nums)
        {
            if( nums_stack.empty() || i == nums_stack.top())
                nums_stack.push( i );
            else
                nums_stack.pop();
        }
        return nums_stack.top();
    }
};<commit_msg>Delete 169.cpp<commit_after><|endoftext|>


<commit_before>/*
 * Written by Nitin Kumar Maharana
 * nitin.maharana@gmail.com
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
    pair<int,int> robUtil(TreeNode* root)
    {
        if(root == NULL)
            return make_pair(0, 0);

        pair<int, int> left, right;

        left = robUtil(root->left);
        right = robUtil(root->right);

        int include, notInclude;

        include = root->val + left.second + right.second;
        notInclude = max(left.first, left.second) + max(right.first, right.second);

        return make_pair(include, notInclude);
    }
public:
    int rob(TreeNode* root) {
        pair<int,int> result;
        result = robUtil(root);
        return max(result.first, result.second);
    }
};<commit_msg>337. House Robber III<commit_after>/*
 * Written by Nitin Kumar Maharana
 * nitin.maharana@gmail.com
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
    pair<int,int> robUtil(TreeNode* root)
    {
        if(root == nullptr)
            return make_pair(0, 0);

        pair<int, int> left, right;

        left = robUtil(root->left);
        right = robUtil(root->right);

        int include, notInclude;

        include = root->val + left.second + right.second;
        notInclude = max(left.first, left.second) + max(right.first, right.second);

        return make_pair(include, notInclude);
    }
public:
    int rob(TreeNode* root) {
        pair<int,int> result;
        result = robUtil(root);
        return max(result.first, result.second);
    }
};
<|endoftext|>


<commit_before>

#pragma pack(push,1)
struct FILE_Object3dHeader
{
    int32_t Version;
    int32_t NumberOfVertexes;
    int32_t NumberOfPrimitives;
    int32_t OffsetToSelectionPrimitive;//Appears to be index into primitive array
    int32_t XFromParent;
    int32_t YFromParent;
    int32_t ZFromParent;
    int32_t OffsetToObjectName;
    int32_t Always_0;
    int32_t OffsetToVertexArray;
    int32_t OffsetToPrimitiveArray;
    int32_t OffsetToSiblingObject;
    int32_t OffsetToChildObject;
};

struct FILE_Object3dVertex
{
    int32_t x;
    int32_t y;
    int32_t z;
};

struct FILE_Object3dPrimitive
{
    int32_t ColorIndex;
    int32_t NumberOfVertexIndexes;
    int32_t Always_0;
    int32_t OffsetToVertexIndexArray;
    int32_t OffsetToTextureName;
    //Apparently Cavedog editor specific:
    int32_t Unknown_1;
    int32_t Unknown_2;
    int32_t Unknown_3;
};
#pragma pack(pop)

struct Position3d
{
    double X,Y,Z;
};

struct Object3d
{
    char * Name;
    Object3d * Children;
    int NumberOfChildren;
    Position3d Position;
    int NumberOfPrimitives;
    struct Object3dPrimitive * Primitives;
    int NumberOfVertices;
    GLfloat * Vertices;
    GLuint VertexBuffer;
    int NumTriangles;
};

struct Object3dPrimitive
{
    Texture * Texture;
    int NumberOfVertices;
    int ColorIndex;
    int * VertexIndexes;
};

void FillObject3dData(GLfloat * Data, int CurrentTriangle,int * VertexIndices,GLfloat * UV, Texture * Texture, Object3d * Object, Object3dPrimitive * Primitive)
{
    int Offset=CurrentTriangle*(3+2+3)*3;
    Data+=Offset;

    for(int i=0;i<3;i++)
    {
	Data[i*8+0]=Object->Vertices[Primitive->VertexIndexes[VertexIndices[i]]*3+0];
	Data[i*8+1]=Object->Vertices[Primitive->VertexIndexes[VertexIndices[i]]*3+1];
	Data[i*8+2]=Object->Vertices[Primitive->VertexIndexes[VertexIndices[i]]*3+2];

	Data[i*8+3]=Texture->U+Texture->Width*UV[i*2+0];
	Data[i*8+4]=Texture->V+Texture->Height*UV[i*2+1];

	Data[i*8+5]=(uint8_t)PaletteData[Primitive->ColorIndex*3+0]/255.0;
	Data[i*8+6]=(uint8_t)PaletteData[Primitive->ColorIndex*3+1]/255.0;
	Data[i*8+7]=(uint8_t)PaletteData[Primitive->ColorIndex*3+2]/255.0;
    }
}

Texture NoTexture={"",0,-2,-2,-1,-1,0};

bool32 PrepareObject3dForRendering(Object3d * Object)
{
    if(Object->VertexBuffer)
    {
	glDeleteVertexArrays(1,&Object->VertexBuffer);
    }

    Object->NumTriangles=0;
    for(int i=0;i<Object->NumberOfPrimitives;i++)
    {
	Object->NumTriangles += Object->Primitives[i].NumberOfVertices-2>0?Object->Primitives[i].NumberOfVertices-2:0;
    }
    GLfloat Data[Object->NumTriangles * (3+2+3)*3];//3 coord, 2 tex, 3 color
    //NOTE: signal no texture in some way, perhaps negative texture coords?
    int CurrentTriangle=0;
    for(int PrimitiveIndex=0;PrimitiveIndex<Object->NumberOfPrimitives;PrimitiveIndex++)
    {
	Object3dPrimitive * CurrentPrimitive=&Object->Primitives[PrimitiveIndex];
	Texture * Texture=CurrentPrimitive->Texture;
	if(!Texture)
	    Texture=&NoTexture;
	switch(CurrentPrimitive->NumberOfVertices)
	{
	case 1:
	case 2:
	    LogDebug("ignoring line or point (%d) in %s",CurrentPrimitive->NumberOfVertices,Object->Name);
	    break;
	case 3:
	{
	    GLfloat UVCoords[]={0,1, 1,1, 0,0};
	    int Vertexes[]={0,1,2};
	    FillObject3dData(Data,CurrentTriangle,Vertexes,UVCoords,Texture,Object,CurrentPrimitive);
	    CurrentTriangle++;
	}
	    break;
	case 4:
	{
	    GLfloat UVCoords1[]={1,0, 0,1, 1,1};
	    int Vertexes1[]={0,2,3};
	    FillObject3dData(Data,CurrentTriangle,Vertexes1,UVCoords1,Texture,Object,CurrentPrimitive);
	    CurrentTriangle++;
	    GLfloat UVCoords2[]={1,0, 0,0, 0,1};
	    int Vertexes2[]={0,1,2};
	    FillObject3dData(Data,CurrentTriangle,Vertexes2,UVCoords2,Texture,Object,CurrentPrimitive);
	    CurrentTriangle++;
	}
	    break;
	default:
	{
	    GLfloat UVCoords[6];
	    //Map sin/cos unit circle at (0,0) onto 1/2 unit circle at (0.5,0.5)
	    UVCoords[2*2]=0.5f*(1-(sin((2.0f*(CurrentPrimitive->NumberOfVertices-1)+1)*PI/float(CurrentPrimitive->NumberOfVertices))/cos(PI/CurrentPrimitive->NumberOfVertices)));
	    UVCoords[2*2+1]=0.5f*(1-(cos(PI/CurrentPrimitive->NumberOfVertices*(2.0f*(CurrentPrimitive->NumberOfVertices-1)+1))/cos(PI/CurrentPrimitive->NumberOfVertices)));

	    for(int i=0;i<CurrentPrimitive->NumberOfVertices-2;i++)
	    {
		int Vertexes[]={i,i+1,CurrentPrimitive->NumberOfVertices-1};

		for(int j=0;j<2;j++)
		{
		    //Map sin/cos unit circle at (0,0) onto 1/2 unit circle at (0.5,0.5)
		    UVCoords[j*2]=0.5f*(1-(sin((2.0f*(i+j)+1)*PI/float(CurrentPrimitive->NumberOfVertices))/cos(PI/CurrentPrimitive->NumberOfVertices)));
		    UVCoords[j*2+1]=0.5f*(1-(cos(PI/CurrentPrimitive->NumberOfVertices*(2.0f*(i+j)+1))/cos(PI/CurrentPrimitive->NumberOfVertices)));
		}
		FillObject3dData(Data,CurrentTriangle,Vertexes,UVCoords,Texture,Object,CurrentPrimitive);
		CurrentTriangle++;
	    }
	}
	    break;
	}
    }

    glGenVertexArrays(1,&Object->VertexBuffer);
    glBindVertexArray(Object->VertexBuffer);

    GLuint VertexBuffer;
    glGenBuffers(1,&VertexBuffer);

    glBindBuffer(GL_ARRAY_BUFFER,VertexBuffer);
    glBufferData(GL_ARRAY_BUFFER,sizeof(GLfloat)*Object->NumTriangles*(3+2+3)*3,Data,GL_STATIC_DRAW);
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(GLfloat)*(3+2+3),0);
    glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,sizeof(GLfloat)*(3+2+3),(GLvoid*)(sizeof(GLfloat)*3));

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,sizeof(GLfloat)*(3+2+3),(GLvoid*)(sizeof(GLfloat)*5));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0);
    //glDeleteBuffers(1,&VertexBuffer);

    for(int i=0;i<Object->NumberOfChildren;i++)
    {
	if(!PrepareObject3dForRendering(&Object->Children[i]))
	    return 0;
    }

    return 1;
}

struct Object3dTransformationDetails
{

};

void RenderObject3d(Object3d * Object,Object3dTransformationDetails * TransformationDetails)
{
    //TODO(Christof): Actually make use of TransformationDetails
    for(int i=0;i<Object->NumberOfChildren;i++)
    {
	RenderObject3d(&Object->Children[i],0);
    }
    glBindVertexArray(Object->VertexBuffer);
    glDrawArrays(GL_TRIANGLES, 0, Object->NumTriangles);
    GLenum ErrorValue = glGetError();
    if(ErrorValue!=GL_NO_ERROR)
    {
	LogError("failed to render : %s",gluErrorString(ErrorValue));
    }
}

//TODO(Christof): Load Primitives from file to memory
//TODO(Christof): Generate Collision Meshes from Primitives?
//TODO(Christof): Generate Render Data from Primitives

/*TODO(Christof): Figure out if we can/need to use bindless textures or something similar for rendering, for now lets just go with the simple solution
 Probably just load all the textures into one big (1024x1024 or 2048x2048 - will need to check what size we need) texture and use offsets, since the textures are all quite small*/

int Count3DOChildren(char * Buffer,FILE_Object3dHeader * header)
{
    int NumChildren = 0;
    if(header->OffsetToChildObject)
    {
	NumChildren=1;
	header = (FILE_Object3dHeader *)(Buffer+header->OffsetToChildObject);
	while(header->OffsetToSiblingObject)
	{
	    header = (FILE_Object3dHeader *)(Buffer+header->OffsetToSiblingObject);
	    NumChildren++;
	}
    }
    return NumChildren;
}

const double TA_TO_GL_SCALE=1.0f/2500000.0f;

bool32 Load3DOFromBuffer(char * Buffer, Object3d * Object, int Offset=0)
{
    FILE_Object3dHeader * header = (FILE_Object3dHeader *)(Buffer+Offset);
    if(header->Version != 1)
    {
	LogError("Unknown Version in 3DO file: %d",header->Version);
	return 0;
    }
    if(header->OffsetToSiblingObject && !Offset)
    {
	LogError("Top Level 3DO Objects should not have siblings!");
	return 0;
    }

    int NameLength=strlen(Buffer + header->OffsetToObjectName)+1;
    Object->Name = (char *)malloc(NameLength);
    memcpy(Object->Name,Buffer+header->OffsetToObjectName,NameLength);
    LogDebug(Object->Name);
    Object->Position.X=header->XFromParent*TA_TO_GL_SCALE;
    Object->Position.Y=header->YFromParent*TA_TO_GL_SCALE;
    Object->Position.Z=header->ZFromParent*TA_TO_GL_SCALE;

    FILE_Object3dPrimitive * Primitives=(FILE_Object3dPrimitive*)(Buffer+header->OffsetToPrimitiveArray);
    FILE_Object3dPrimitive * CurrentPrimitive = Primitives;
    Object->NumberOfPrimitives = header->NumberOfPrimitives;
    Object->Primitives = (Object3dPrimitive *)malloc(sizeof(Object3dPrimitive)*Object->NumberOfPrimitives);
    for(int i=0;i<header->NumberOfPrimitives;i++)
    {
	if(CurrentPrimitive->OffsetToTextureName)
	{
	    Object->Primitives[i].Texture = GetTexture(Buffer+CurrentPrimitive->OffsetToTextureName,0);
	    if(!Object->Primitives[i].Texture)
	    {
		LogError("Could not get texture for primitive %d in %s, %s",i,Object->Name,Buffer+CurrentPrimitive->OffsetToTextureName);
	    }
	}
	else
	{
	    Object->Primitives[i].Texture=0;
	}
	Object->Primitives[i].NumberOfVertices = CurrentPrimitive->NumberOfVertexIndexes;
	Object->Primitives[i].VertexIndexes = (int *)malloc(sizeof(int)*Object->Primitives[i].NumberOfVertices);
	int16_t * VertexIndexes = (int16_t *)(Buffer + CurrentPrimitive->OffsetToVertexIndexArray);
	for(int j=0;j<Object->Primitives[i].NumberOfVertices;j++)
	    Object->Primitives[i].VertexIndexes[j]=VertexIndexes[j];

	Object->Primitives[i].ColorIndex = CurrentPrimitive->ColorIndex&0xFF;

	CurrentPrimitive++;
    }

    Object->NumberOfVertices = header->NumberOfVertexes;
    Object->Vertices = (float *)malloc(sizeof(float)*3*Object->NumberOfVertices);

    FILE_Object3dVertex * Vertices= (FILE_Object3dVertex *)(Buffer + header->OffsetToVertexArray);
    for(int i=0;i<Object->NumberOfVertices;i++)
    {
	Object->Vertices[i*3+0]=Vertices[i].x*TA_TO_GL_SCALE;
	Object->Vertices[i*3+1]=Vertices[i].y*TA_TO_GL_SCALE;
	Object->Vertices[i*3+2]=Vertices[i].z*TA_TO_GL_SCALE;
    }

    Object->NumberOfChildren = Count3DOChildren(Buffer,header);
    Object->Children = (Object3d *)malloc(sizeof(Object3d)*Object->NumberOfChildren);
    int ChildOffset=header->OffsetToChildObject;
    for(int i=0;i<Object->NumberOfChildren;i++)
    {
	Load3DOFromBuffer(Buffer, &(Object->Children[i]),ChildOffset);
	header = (FILE_Object3dHeader *)(Buffer+ChildOffset);
	ChildOffset=header->OffsetToSiblingObject;
    }

    return 1;
}

/**
   Frees all contents allocated by loading the object, leaving it in a state to be reused for loading without memory leaks
 **/
void Unload3DO(Object3d * Object)
{
    if(!Object)
	return;
    if(Object->Name)
	free(Object->Name);
    if(Object->Vertices)
	free(Object->Vertices);
    if(Object->Primitives)
    {
	for(int i=0;i<Object->NumberOfPrimitives;i++)
	    free(Object->Primitives[i].VertexIndexes);
	free(Object->Primitives);
    }
    for(int i=0;i<Object->NumberOfChildren;i++)
    {
	Unload3DO(&Object->Children[i]);
    }
    if(Object->Children)
	free(Object->Children);
}
<commit_msg>Do .3do file specified object translations.<commit_after>

#pragma pack(push,1)
struct FILE_Object3dHeader
{
    int32_t Version;
    int32_t NumberOfVertexes;
    int32_t NumberOfPrimitives;
    int32_t OffsetToSelectionPrimitive;//Appears to be index into primitive array
    int32_t XFromParent;
    int32_t YFromParent;
    int32_t ZFromParent;
    int32_t OffsetToObjectName;
    int32_t Always_0;
    int32_t OffsetToVertexArray;
    int32_t OffsetToPrimitiveArray;
    int32_t OffsetToSiblingObject;
    int32_t OffsetToChildObject;
};

struct FILE_Object3dVertex
{
    int32_t x;
    int32_t y;
    int32_t z;
};

struct FILE_Object3dPrimitive
{
    int32_t ColorIndex;
    int32_t NumberOfVertexIndexes;
    int32_t Always_0;
    int32_t OffsetToVertexIndexArray;
    int32_t OffsetToTextureName;
    //Apparently Cavedog editor specific:
    int32_t Unknown_1;
    int32_t Unknown_2;
    int32_t Unknown_3;
};
#pragma pack(pop)

struct Position3d
{
    double X,Y,Z;
};

struct Object3d
{
    char * Name;
    Object3d * Children;
    int NumberOfChildren;
    Position3d Position;
    int NumberOfPrimitives;
    struct Object3dPrimitive * Primitives;
    int NumberOfVertices;
    GLfloat * Vertices;
    GLuint VertexBuffer;
    int NumTriangles;
};

struct Object3dPrimitive
{
    Texture * Texture;
    int NumberOfVertices;
    int ColorIndex;
    int * VertexIndexes;
};

void FillObject3dData(GLfloat * Data, int CurrentTriangle,int * VertexIndices,GLfloat * UV, Texture * Texture, Object3d * Object, Object3dPrimitive * Primitive)
{
    int Offset=CurrentTriangle*(3+2+3)*3;
    Data+=Offset;

    for(int i=0;i<3;i++)
    {
	Data[i*8+0]=Object->Vertices[Primitive->VertexIndexes[VertexIndices[i]]*3+0];
	Data[i*8+1]=Object->Vertices[Primitive->VertexIndexes[VertexIndices[i]]*3+1];
	Data[i*8+2]=Object->Vertices[Primitive->VertexIndexes[VertexIndices[i]]*3+2];

	Data[i*8+3]=Texture->U+Texture->Width*UV[i*2+0];
	Data[i*8+4]=Texture->V+Texture->Height*UV[i*2+1];

	Data[i*8+5]=(uint8_t)PaletteData[Primitive->ColorIndex*3+0]/255.0;
	Data[i*8+6]=(uint8_t)PaletteData[Primitive->ColorIndex*3+1]/255.0;
	Data[i*8+7]=(uint8_t)PaletteData[Primitive->ColorIndex*3+2]/255.0;
    }
}

Texture NoTexture={"",0,-2,-2,-1,-1,0};

bool32 PrepareObject3dForRendering(Object3d * Object)
{
    if(Object->VertexBuffer)
    {
	glDeleteVertexArrays(1,&Object->VertexBuffer);
    }

    Object->NumTriangles=0;
    for(int i=0;i<Object->NumberOfPrimitives;i++)
    {
	Object->NumTriangles += Object->Primitives[i].NumberOfVertices-2>0?Object->Primitives[i].NumberOfVertices-2:0;
    }
    GLfloat Data[Object->NumTriangles * (3+2+3)*3];//3 coord, 2 tex, 3 color
    //NOTE: signal no texture in some way, perhaps negative texture coords?
    int CurrentTriangle=0;
    for(int PrimitiveIndex=0;PrimitiveIndex<Object->NumberOfPrimitives;PrimitiveIndex++)
    {
	Object3dPrimitive * CurrentPrimitive=&Object->Primitives[PrimitiveIndex];
	Texture * Texture=CurrentPrimitive->Texture;
	if(!Texture)
	    Texture=&NoTexture;
	switch(CurrentPrimitive->NumberOfVertices)
	{
	case 1:
	case 2:
	    LogDebug("ignoring line or point (%d) in %s",CurrentPrimitive->NumberOfVertices,Object->Name);
	    break;
	case 3:
	{
	    GLfloat UVCoords[]={0,1, 1,1, 0,0};
	    int Vertexes[]={0,1,2};
	    FillObject3dData(Data,CurrentTriangle,Vertexes,UVCoords,Texture,Object,CurrentPrimitive);
	    CurrentTriangle++;
	}
	    break;
	case 4:
	{
	    GLfloat UVCoords1[]={1,0, 0,1, 1,1};
	    int Vertexes1[]={0,2,3};
	    FillObject3dData(Data,CurrentTriangle,Vertexes1,UVCoords1,Texture,Object,CurrentPrimitive);
	    CurrentTriangle++;
	    GLfloat UVCoords2[]={1,0, 0,0, 0,1};
	    int Vertexes2[]={0,1,2};
	    FillObject3dData(Data,CurrentTriangle,Vertexes2,UVCoords2,Texture,Object,CurrentPrimitive);
	    CurrentTriangle++;
	}
	    break;
	default:
	{
	    GLfloat UVCoords[6];
	    //Map sin/cos unit circle at (0,0) onto 1/2 unit circle at (0.5,0.5)
	    UVCoords[2*2]=0.5f*(1-(sin((2.0f*(CurrentPrimitive->NumberOfVertices-1)+1)*PI/float(CurrentPrimitive->NumberOfVertices))/cos(PI/CurrentPrimitive->NumberOfVertices)));
	    UVCoords[2*2+1]=0.5f*(1-(cos(PI/CurrentPrimitive->NumberOfVertices*(2.0f*(CurrentPrimitive->NumberOfVertices-1)+1))/cos(PI/CurrentPrimitive->NumberOfVertices)));

	    for(int i=0;i<CurrentPrimitive->NumberOfVertices-2;i++)
	    {
		int Vertexes[]={i,i+1,CurrentPrimitive->NumberOfVertices-1};

		for(int j=0;j<2;j++)
		{
		    //Map sin/cos unit circle at (0,0) onto 1/2 unit circle at (0.5,0.5)
		    UVCoords[j*2]=0.5f*(1-(sin((2.0f*(i+j)+1)*PI/float(CurrentPrimitive->NumberOfVertices))/cos(PI/CurrentPrimitive->NumberOfVertices)));
		    UVCoords[j*2+1]=0.5f*(1-(cos(PI/CurrentPrimitive->NumberOfVertices*(2.0f*(i+j)+1))/cos(PI/CurrentPrimitive->NumberOfVertices)));
		}
		FillObject3dData(Data,CurrentTriangle,Vertexes,UVCoords,Texture,Object,CurrentPrimitive);
		CurrentTriangle++;
	    }
	}
	    break;
	}
    }

    glGenVertexArrays(1,&Object->VertexBuffer);
    glBindVertexArray(Object->VertexBuffer);

    GLuint VertexBuffer;
    glGenBuffers(1,&VertexBuffer);

    glBindBuffer(GL_ARRAY_BUFFER,VertexBuffer);
    glBufferData(GL_ARRAY_BUFFER,sizeof(GLfloat)*Object->NumTriangles*(3+2+3)*3,Data,GL_STATIC_DRAW);
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(GLfloat)*(3+2+3),0);
    glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,sizeof(GLfloat)*(3+2+3),(GLvoid*)(sizeof(GLfloat)*3));

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,sizeof(GLfloat)*(3+2+3),(GLvoid*)(sizeof(GLfloat)*5));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0);
    //glDeleteBuffers(1,&VertexBuffer);

    for(int i=0;i<Object->NumberOfChildren;i++)
    {
	if(!PrepareObject3dForRendering(&Object->Children[i]))
	    return 0;
    }

    return 1;
}

struct Object3dTransformationDetails
{

};

Matrix Identity={{1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}};
void RenderObject3d(Object3d * Object,Object3dTransformationDetails * TransformationDetails,GLuint ModelMatrixLocation, Matrix ParentMatrix=Identity)
{
    //TODO(Christof): Actually make use of TransformationDetails
    Matrix CurrentMatrix;
    SetTranslationMatrix(Object->Position.X,Object->Position.Y,Object->Position.Z,&CurrentMatrix);
    CurrentMatrix = CurrentMatrix * ParentMatrix;

    for(int i=0;i<Object->NumberOfChildren;i++)
    {
	RenderObject3d(&Object->Children[i],TransformationDetails,ModelMatrixLocation,CurrentMatrix);
    }
    UploadMatrix(&CurrentMatrix,ModelMatrixLocation);
    glBindVertexArray(Object->VertexBuffer);
    glDrawArrays(GL_TRIANGLES, 0, Object->NumTriangles);
    GLenum ErrorValue = glGetError();
    if(ErrorValue!=GL_NO_ERROR)
    {
	LogError("failed to render : %s",gluErrorString(ErrorValue));
    }
}

//TODO(Christof): Load Primitives from file to memory
//TODO(Christof): Generate Collision Meshes from Primitives?
//TODO(Christof): Generate Render Data from Primitives

/*TODO(Christof): Figure out if we can/need to use bindless textures or something similar for rendering, for now lets just go with the simple solution
 Probably just load all the textures into one big (1024x1024 or 2048x2048 - will need to check what size we need) texture and use offsets, since the textures are all quite small*/

int Count3DOChildren(char * Buffer,FILE_Object3dHeader * header)
{
    int NumChildren = 0;
    if(header->OffsetToChildObject)
    {
	NumChildren=1;
	header = (FILE_Object3dHeader *)(Buffer+header->OffsetToChildObject);
	while(header->OffsetToSiblingObject)
	{
	    header = (FILE_Object3dHeader *)(Buffer+header->OffsetToSiblingObject);
	    NumChildren++;
	}
    }
    return NumChildren;
}

const double TA_TO_GL_SCALE=1.0f/2500000.0f;

bool32 Load3DOFromBuffer(char * Buffer, Object3d * Object, int Offset=0)
{
    FILE_Object3dHeader * header = (FILE_Object3dHeader *)(Buffer+Offset);
    if(header->Version != 1)
    {
	LogError("Unknown Version in 3DO file: %d",header->Version);
	return 0;
    }
    if(header->OffsetToSiblingObject && !Offset)
    {
	LogError("Top Level 3DO Objects should not have siblings!");
	return 0;
    }

    int NameLength=strlen(Buffer + header->OffsetToObjectName)+1;
    Object->Name = (char *)malloc(NameLength);
    memcpy(Object->Name,Buffer+header->OffsetToObjectName,NameLength);
    LogDebug(Object->Name);
    Object->Position.X=header->XFromParent*TA_TO_GL_SCALE;
    Object->Position.Y=header->YFromParent*TA_TO_GL_SCALE;
    Object->Position.Z=header->ZFromParent*TA_TO_GL_SCALE;

    FILE_Object3dPrimitive * Primitives=(FILE_Object3dPrimitive*)(Buffer+header->OffsetToPrimitiveArray);
    FILE_Object3dPrimitive * CurrentPrimitive = Primitives;
    Object->NumberOfPrimitives = header->NumberOfPrimitives;
    Object->Primitives = (Object3dPrimitive *)malloc(sizeof(Object3dPrimitive)*Object->NumberOfPrimitives);
    for(int i=0;i<header->NumberOfPrimitives;i++)
    {
	if(CurrentPrimitive->OffsetToTextureName)
	{
	    Object->Primitives[i].Texture = GetTexture(Buffer+CurrentPrimitive->OffsetToTextureName,0);
	    if(!Object->Primitives[i].Texture)
	    {
		LogError("Could not get texture for primitive %d in %s, %s",i,Object->Name,Buffer+CurrentPrimitive->OffsetToTextureName);
	    }
	}
	else
	{
	    Object->Primitives[i].Texture=0;
	}
	Object->Primitives[i].NumberOfVertices = CurrentPrimitive->NumberOfVertexIndexes;
	Object->Primitives[i].VertexIndexes = (int *)malloc(sizeof(int)*Object->Primitives[i].NumberOfVertices);
	int16_t * VertexIndexes = (int16_t *)(Buffer + CurrentPrimitive->OffsetToVertexIndexArray);
	for(int j=0;j<Object->Primitives[i].NumberOfVertices;j++)
	    Object->Primitives[i].VertexIndexes[j]=VertexIndexes[j];

	Object->Primitives[i].ColorIndex = CurrentPrimitive->ColorIndex&0xFF;

	CurrentPrimitive++;
    }

    Object->NumberOfVertices = header->NumberOfVertexes;
    Object->Vertices = (float *)malloc(sizeof(float)*3*Object->NumberOfVertices);

    FILE_Object3dVertex * Vertices= (FILE_Object3dVertex *)(Buffer + header->OffsetToVertexArray);
    for(int i=0;i<Object->NumberOfVertices;i++)
    {
	Object->Vertices[i*3+0]=Vertices[i].x*TA_TO_GL_SCALE;
	Object->Vertices[i*3+1]=Vertices[i].y*TA_TO_GL_SCALE;
	Object->Vertices[i*3+2]=Vertices[i].z*TA_TO_GL_SCALE;
    }

    Object->NumberOfChildren = Count3DOChildren(Buffer,header);
    Object->Children = (Object3d *)malloc(sizeof(Object3d)*Object->NumberOfChildren);
    int ChildOffset=header->OffsetToChildObject;
    for(int i=0;i<Object->NumberOfChildren;i++)
    {
	Load3DOFromBuffer(Buffer, &(Object->Children[i]),ChildOffset);
	header = (FILE_Object3dHeader *)(Buffer+ChildOffset);
	ChildOffset=header->OffsetToSiblingObject;
    }

    return 1;
}

/**
   Frees all contents allocated by loading the object, leaving it in a state to be reused for loading without memory leaks
 **/
void Unload3DO(Object3d * Object)
{
    if(!Object)
	return;
    if(Object->Name)
	free(Object->Name);
    if(Object->Vertices)
	free(Object->Vertices);
    if(Object->Primitives)
    {
	for(int i=0;i<Object->NumberOfPrimitives;i++)
	    free(Object->Primitives[i].VertexIndexes);
	free(Object->Primitives);
    }
    for(int i=0;i<Object->NumberOfChildren;i++)
    {
	Unload3DO(&Object->Children[i]);
    }
    if(Object->Children)
	free(Object->Children);
}
<|endoftext|>