text:
<?php

// @codingStandardsIgnoreFile

namespace Magento\Framework\DB;

/**
 * Class QueryFactory
 */
class QueryFactory
{
    /**
     * @var \Magento\Framework\ObjectManagerInterface
     */
    protected $objectManager;

    /**
     * @param \Magento\Framework\ObjectManagerInterface $objectManager
     */
    public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager)
    {
        $this->objectManager = $objectManager;
    }

    /**
     * Create Query object
     *
     * @param string $className
     * @param array $arguments
     * @return QueryInterface
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function create($className, array $arguments = [])
    {
        $query = $this->objectManager->create($className, $arguments);
        if (!$query instanceof QueryInterface) {
            throw new \Magento\Framework\Exception\LocalizedException(
                new \Magento\Framework\Phrase(
                    '%1 doesn\'t implement \Magento\Framework\DB\QueryInterface',
                    [$className]
                )
            );
        }
        return $query;
    }
}


text:
module Cequel
  module Metal
    #
    # A result row from a CQL query. Acts as a hash of column names to values,
    # but also exposes TTLs and writetimes
    #
    # @since 1.0.0
    #
    class Row < DelegateClass(ActiveSupport::HashWithIndifferentAccess)
      #
      # Encapsulate a result row from the driver
      #
      # @param result_row [Hash] row from underlying driver
      # @return [Row] encapsulated row
      #
      # @api private
      #
      def self.from_result_row(result_row)
        if result_row
          new.tap do |row|
            result_row.each_pair do |name, value|
              if name =~ /^(ttl|writetime)\((.+)\)$/
                if $1 == 'ttl' then row.set_ttl($2, value)
                else row.set_writetime($2, value)
                end
              else row[name] = value
              end
            end
          end
        end
      end

      #
      # @api private
      #
      def initialize
        super(ActiveSupport::HashWithIndifferentAccess.new)
        @ttls = ActiveSupport::HashWithIndifferentAccess.new
        @writetimes = ActiveSupport::HashWithIndifferentAccess.new
      end

      #
      # Get the TTL (time-to-live) of a column
      #
      # @param column [Symbol] column name
      # @return [Integer] TTL of column in seconds
      #
      def ttl(column)
        @ttls[column]
      end

      #
      # Get the writetime of a column
      #
      # @param column [Symbol] column name
      # @return [Integer] writetime of column in nanoseconds since epoch
      #
      def writetime(column)
        @writetimes[column]
      end
      alias_method :timestamp, :writetime

      # @private
      def set_ttl(column, value)
        @ttls[column] = value
      end

      # @private
      def set_writetime(column, value)
        @writetimes[column] = value
      end
    end
  end
end

redpajama_set_name: RedPajamaGithub


text:
@implementation CommonTools
+ (void)setObjectValue:(id)obj forKey:(NSString *)key
{
    [self removeValueForKey:key];
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    [userDefault setObject:obj forKey:key];
    [userDefault synchronize];
}

+ (id)getObjectValueForKey:(NSString *)key
{
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    id value = [userDefault objectForKey:key];
    return value;
}

+ (void)setBoolValue:(BOOL)obj forKey:(NSString *)key
{
    [self removeValueForKey:key];
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    [userDefault setBool:obj forKey:key];
    [userDefault synchronize];
}

+ (BOOL)getBoolValueForKey:(NSString *)key
{
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    BOOL value = [userDefault boolForKey:key];
    return value;
}

+ (void)removeValueForKey:(NSString *)key
{
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    [userDefault removeObjectForKey:key];
    [userDefault synchronize];
}

+ (id)getCustomObjectForKey:(NSString *)key
{
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    NSData * dataobject = [userDefault objectForKey:key];

    //add by hgc 2016年10月25日
    NSLog(@"--%@", key);

    NSLog(@"--%@", dataobject);

    id object = [NSKeyedUnarchiver unarchiveObjectWithData:dataobject];
    return object;
}

+ (void)setCustomObject:(id)obj forKey:(NSString *)key
{
    NSData * dataobject = [NSKeyedArchiver archivedDataWithRootObject:obj];
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    [self removeValueForKey:key];
    [userDefault setObject:dataobject forKey:key];
    [userDefault synchronize];
}

+ (BOOL)validatePhone:(NSString *)phoneNum {

    NSString *string = [phoneNum stringByReplacingOccurrencesOfString:@" " withString:@""];
    if (string.length > 0) {
        NSString *str = [string substringToIndex:1];
        if (string.length == 11) {
            if ([str isEqualToString:@"1"]) {
                return YES;
            } else {
                return NO;
            }
        } else {
            return NO;
        }
    }else{
        return NO;
    }

}
// 密码 8 - 20 位数字字母组合
+ (BOOL)checkPassword:(NSString *) password
{
    NSString *pattern = @"^(?![0-9]+$)(?![a-zA-Z]+$)[a-zA-Z0-9.]{8,20}";
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
    BOOL isMatch = [pred evaluateWithObject:password];
    return isMatch;

}
#pragma mark - 判断密码安全性
+ (BOOL)judgePassword:(NSString *)password {

    BOOL nums = NO;
    BOOL en = NO;
    BOOL fuhao = NO;
    for (int i = 0 ; i < password.length; i ++) {
        char chs = [password characterAtIndex:i];
        int ch = chs;
        if (!nums && (ch <= 57 && ch >= 48)) {
            nums = YES;
        }
        if (!en && ((ch >=65 && ch <= 90) || (ch >=97 && ch <= 122))) {
            en = YES;
        }

        if (!fuhao && ((ch >=32 && ch <= 47) || (ch >=58 && ch <= 64)|| (ch >=91 && ch <= 96)|| (ch >=123 && ch <= 126))) {
            fuhao = YES;
        }
    }

    if ((nums == YES && en == YES) || (nums == YES && fuhao == YES) || (fuhao == YES && en == YES) || (nums == YES && en == YES && fuhao == YES)) {

        return YES;
    } else {
        return NO;
    }

}

// 最少 6 位
+ (BOOL)judgePsd:(NSString *)psdString {
    if (psdString != 0) {
        BOOL result = [CommonTools judgePassword:psdString];
        if (result && psdString.length >= 6) {
            return YES;
        } else {
            return NO;
        }
    } else {
        return NO;
    }
}

#pragma mark - 检测密码安全性
+ (BOOL)checkPasswordForSafe:(NSString *)password{
    BOOL nums = NO;
    BOOL en = NO;
    BOOL fuhao = NO;
    if (password.length > 20 || password.length < 6) {

        return NO;
    }
    for (int i = 0 ; i < password.length; i ++) {
        char chs = [password characterAtIndex:i];
        int ch = chs;
        if (!nums && (ch <= 57 && ch >= 48)) {
            nums = YES;
        }
        if (!en && ((ch >=65 && ch <= 90) || (ch >=97 && ch <= 122))) {
            en = YES;
        }

        if (!fuhao && ((ch >=32 && ch <= 47) || (ch >=58 && ch <= 64)|| (ch >=91 && ch <= 96)|| (ch >=123 && ch <= 126))) {
            fuhao = YES;
        }
    }

    if ((nums == YES && en == YES) || (nums == YES && fuhao == YES) || (fuhao == YES && en == YES) || (nums == YES && en == YES && fuhao == YES)) {

        return YES;
    } else {
        return NO;
    }
}
@end


text:
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: soc2013/dpl/head/sys/dev/isci/isci.c 242580 2012-10-10 18:01:56Z jimharris $");

#include <dev/isci/isci.h>

#include <sys/sysctl.h>
#include <sys/malloc.h>

#include <cam/cam_periph.h>

#include <dev/led/led.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>

#include <dev/isci/scil/scic_logger.h>
#include <dev/isci/scil/scic_library.h>
#include <dev/isci/scil/scic_sgpio.h>
#include <dev/isci/scil/scic_user_callback.h>

#include <dev/isci/scil/scif_controller.h>
#include <dev/isci/scil/scif_library.h>
#include <dev/isci/scil/scif_logger.h>
#include <dev/isci/scil/scif_user_callback.h>

MALLOC_DEFINE(M_ISCI, "isci", "isci driver memory allocations");

struct isci_softc *g_isci;
uint32_t g_isci_debug_level = 0;

static int isci_probe(device_t);
static int isci_attach(device_t);
static int isci_detach(device_t);

int isci_initialize(struct isci_softc *isci);

void isci_allocate_dma_buffer_callback(void *arg, bus_dma_segment_t *seg,
    int nseg, int error);

static devclass_t isci_devclass;

static device_method_t isci_pci_methods[] = {
	 /* Device interface */
	 DEVMETHOD(device_probe,  isci_probe),
	 DEVMETHOD(device_attach, isci_attach),
	 DEVMETHOD(device_detach, isci_detach),
	 { 0, 0 }
};

static driver_t isci_pci_driver = {
	 "isci",
	 isci_pci_methods,
	 sizeof(struct isci_softc),
};

DRIVER_MODULE(isci, pci, isci_pci_driver, isci_devclass, 0, 0);

static struct _pcsid
{
	 u_int32_t	type;
	 const char	*desc;
} pci_ids[] = {
	 { 0x1d608086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d618086,	"Intel(R) C600 Series Chipset SAS Controller (SATA mode)"  },
	 { 0x1d628086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d638086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d648086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d658086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d668086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d678086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d688086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d698086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d6a8086,	"Intel(R) C600 Series Chipset SAS Controller (SATA mode)"  },
	 { 0x1d6b8086,  "Intel(R) C600 Series Chipset SAS Controller (SATA mode)"  },
	 { 0x1d6c8086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d6d8086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d6e8086,	"Intel(R) C600 Series Chipset SAS Controller"  },
	 { 0x1d6f8086,	"Intel(R) C600 Series Chipset SAS Controller (SATA mode)"  },
	 { 0x00000000,	NULL				}
};

static int
isci_probe (device_t device)
{
	u_int32_t	type = pci_get_devid(device);
	struct _pcsid	*ep = pci_ids;

	while (ep->type && ep->type != type)
		++ep;

	if (ep->desc)
	{
		device_set_desc(device, ep->desc);
		return (BUS_PROBE_DEFAULT);
	}
	else
		return (ENXIO);
}

static int
isci_allocate_pci_memory(struct isci_softc *isci)
{
	int i;

	for (i = 0; i < ISCI_NUM_PCI_BARS; i++)
	{
		struct ISCI_PCI_BAR *pci_bar = &isci->pci_bar[i];

		pci_bar->resource_id = PCIR_BAR(i*2);
		pci_bar->resource = bus_alloc_resource(isci->device,
		    SYS_RES_MEMORY, &pci_bar->resource_id, 0, ~0, 1,
		    RF_ACTIVE);

		if(pci_bar->resource == NULL)
			isci_log_message(0, "ISCI",
			    "unable to allocate pci resource\n");
		else {
			pci_bar->bus_tag = rman_get_bustag(pci_bar->resource);
			pci_bar->bus_handle =
			    rman_get_bushandle(pci_bar->resource);
		}
	}

	return (0);
}

static int
isci_attach(device_t device)
{
	int error;
	struct isci_softc *isci = DEVICE2SOFTC(device);

	g_isci = isci;
	isci->device = device;

	isci_allocate_pci_memory(isci);

	error = isci_initialize(isci);

	if (error)
	{
		isci_detach(device);
		return (error);
	}

	isci_interrupt_setup(isci);
	isci_sysctl_initialize(isci);

	return (0);
}

static int
isci_detach(device_t device)
{
	struct isci_softc *isci = DEVICE2SOFTC(device);
	int i, phy;

	for (i = 0; i < isci->controller_count; i++) {
		struct ISCI_CONTROLLER *controller = &isci->controllers[i];
		SCI_STATUS status;
		void *unmap_buffer;

		if (controller->scif_controller_handle != NULL) {
			scic_controller_disable_interrupts(
			    scif_controller_get_scic_handle(controller->scif_controller_handle));

			mtx_lock(&controller->lock);
			status = scif_controller_stop(controller->scif_controller_handle, 0);
			mtx_unlock(&controller->lock);

			while (controller->is_started == TRUE) {
				/* Now poll for interrupts until the controller stop complete
				 *  callback is received.
				 */
				mtx_lock(&controller->lock);
				isci_interrupt_poll_handler(controller);
				mtx_unlock(&controller->lock);
				pause("isci", 1);
			}

			if(controller->sim != NULL) {
				mtx_lock(&controller->lock);
				xpt_free_path(controller->path);
				xpt_bus_deregister(cam_sim_path(controller->sim));
				cam_sim_free(controller->sim, TRUE);
				mtx_unlock(&controller->lock);
			}
		}

		if (controller->timer_memory != NULL)
			free(controller->timer_memory, M_ISCI);

		if (controller->remote_device_memory != NULL)
			free(controller->remote_device_memory, M_ISCI);

		for (phy = 0; phy < SCI_MAX_PHYS; phy++) {
			if (controller->phys[phy].cdev_fault)
				led_destroy(controller->phys[phy].cdev_fault);

			if (controller->phys[phy].cdev_locate)
				led_destroy(controller->phys[phy].cdev_locate);
		}

		while (1) {
			sci_pool_get(controller->unmap_buffer_pool, unmap_buffer);
			if (unmap_buffer == NULL)
				break;
			contigfree(unmap_buffer, PAGE_SIZE, M_ISCI);
		}
	}

	/* The SCIF controllers have been stopped, so we can now
	 *  free the SCI library memory.
	 */
	if (isci->sci_library_memory != NULL)
		free(isci->sci_library_memory, M_ISCI);

	for (i = 0; i < ISCI_NUM_PCI_BARS; i++)
	{
		struct ISCI_PCI_BAR *pci_bar = &isci->pci_bar[i];

		if (pci_bar->resource != NULL)
			bus_release_resource(device, SYS_RES_MEMORY,
			    pci_bar->resource_id, pci_bar->resource);
	}

	for (i = 0; i < isci->num_interrupts; i++)
	{
		struct ISCI_INTERRUPT_INFO *interrupt_info;

		interrupt_info = &isci->interrupt_info[i];

		if(interrupt_info->tag != NULL)
			bus_teardown_intr(device, interrupt_info->res,
			    interrupt_info->tag);

		if(interrupt_info->res != NULL)
			bus_release_resource(device, SYS_RES_IRQ,
			    rman_get_rid(interrupt_info->res),
			    interrupt_info->res);

		pci_release_msi(device);
	}

	return (0);
}

int
isci_initialize(struct isci_softc *isci)
{
	int error;
	uint32_t status = 0;
	uint32_t library_object_size;
	uint32_t verbosity_mask;
	uint32_t scic_log_object_mask;
	uint32_t scif_log_object_mask;
	uint8_t *header_buffer;

	library_object_size = scif_library_get_object_size(SCI_MAX_CONTROLLERS);

	isci->sci_library_memory =
	    malloc(library_object_size, M_ISCI, M_NOWAIT | M_ZERO );

	isci->sci_library_handle = scif_library_construct(
	    isci->sci_library_memory, SCI_MAX_CONTROLLERS);

	sci_object_set_association( isci->sci_library_handle, (void *)isci);

	verbosity_mask = (1<<SCI_LOG_VERBOSITY_ERROR) |
	    (1<<SCI_LOG_VERBOSITY_WARNING) | (1<<SCI_LOG_VERBOSITY_INFO) |
	    (1<<SCI_LOG_VERBOSITY_TRACE);

	scic_log_object_mask = 0xFFFFFFFF;
	scic_log_object_mask &= ~SCIC_LOG_OBJECT_COMPLETION_QUEUE;
	scic_log_object_mask &= ~SCIC_LOG_OBJECT_SSP_IO_REQUEST;
	scic_log_object_mask &= ~SCIC_LOG_OBJECT_STP_IO_REQUEST;
	scic_log_object_mask &= ~SCIC_LOG_OBJECT_SMP_IO_REQUEST;
	scic_log_object_mask &= ~SCIC_LOG_OBJECT_CONTROLLER;

	scif_log_object_mask = 0xFFFFFFFF;
	scif_log_object_mask &= ~SCIF_LOG_OBJECT_CONTROLLER;
	scif_log_object_mask &= ~SCIF_LOG_OBJECT_IO_REQUEST;

	TUNABLE_INT_FETCH("hw.isci.debug_level", &g_isci_debug_level);

	sci_logger_enable(sci_object_get_logger(isci->sci_library_handle),
	    scif_log_object_mask, verbosity_mask);

	sci_logger_enable(sci_object_get_logger(
	    scif_library_get_scic_handle(isci->sci_library_handle)),
	    scic_log_object_mask, verbosity_mask);

	header_buffer = (uint8_t *)&isci->pci_common_header;
	for (uint8_t i = 0; i < sizeof(isci->pci_common_header); i++)
		header_buffer[i] = pci_read_config(isci->device, i, 1);

	scic_library_set_pci_info(
	    scif_library_get_scic_handle(isci->sci_library_handle),
	    &isci->pci_common_header);

	isci->oem_parameters_found = FALSE;

	isci_get_oem_parameters(isci);

	/* trigger interrupt if 32 completions occur before timeout expires */
	isci->coalesce_number = 32;

	/* trigger interrupt if 2 microseconds elapse after a completion occurs,
	 *  regardless if "coalesce_number" completions have occurred
	 */
	isci->coalesce_timeout = 2;

	isci->controller_count = scic_library_get_pci_device_controller_count(
	    scif_library_get_scic_handle(isci->sci_library_handle));

	for (int index = 0; index < isci->controller_count; index++) {
		struct ISCI_CONTROLLER *controller = &isci->controllers[index];
		SCI_CONTROLLER_HANDLE_T scif_controller_handle;

		controller->index = index;
		isci_controller_construct(controller, isci);

		scif_controller_handle = controller->scif_controller_handle;

		status = isci_controller_initialize(controller);

		if(status != SCI_SUCCESS) {
			isci_log_message(0, "ISCI",
			    "isci_controller_initialize FAILED: %x\n",
			    status);
			return (status);
		}

		error = isci_controller_allocate_memory(controller);

		if (error != 0)
			return (error);

		scif_controller_set_interrupt_coalescence(
		    scif_controller_handle, isci->coalesce_number,
		    isci->coalesce_timeout);
	}

	/* FreeBSD provides us a hook to ensure we get a chance to start
	 *  our controllers and complete initial domain discovery before
	 *  it searches for the boot device.  Once we're done, we'll
	 *  disestablish the hook, signaling the kernel that is can proceed
	 *  with the boot process.
	 */
	isci->config_hook.ich_func = &isci_controller_start;
	isci->config_hook.ich_arg = &isci->controllers[0];

	if (config_intrhook_establish(&isci->config_hook) != 0)
		isci_log_message(0, "ISCI",
		    "config_intrhook_establish failed!\n");

	return (status);
}

void
isci_allocate_dma_buffer_callback(void *arg, bus_dma_segment_t *seg,
    int nseg, int error)
{
	struct ISCI_MEMORY *memory = (struct ISCI_MEMORY *)arg;

	memory->error = error;

	if (nseg != 1 || error != 0)
		isci_log_message(0, "ISCI",
		    "Failed to allocate physically contiguous memory!\n");
	else
		memory->physical_address = seg->ds_addr;
}

int
isci_allocate_dma_buffer(device_t device, struct ISCI_MEMORY *memory)
{
	uint32_t status;

	status = bus_dma_tag_create(bus_get_dma_tag(device),
	    0x40 /* cacheline alignment */, 0x0, BUS_SPACE_MAXADDR,
	    BUS_SPACE_MAXADDR, NULL, NULL, memory->size,
	    0x1 /* we want physically contiguous */,
	    memory->size, 0, NULL, NULL, &memory->dma_tag);

	if(status == ENOMEM) {
		isci_log_message(0, "ISCI", "bus_dma_tag_create failed\n");
		return (status);
	}

	status = bus_dmamem_alloc(memory->dma_tag,
	    (void **)&memory->virtual_address, BUS_DMA_ZERO, &memory->dma_map);

	if(status == ENOMEM)
	{
		isci_log_message(0, "ISCI", "bus_dmamem_alloc failed\n");
		return (status);
	}

	status = bus_dmamap_load(memory->dma_tag, memory->dma_map,
	    (void *)memory->virtual_address, memory->size,
	    isci_allocate_dma_buffer_callback, memory, 0);

	if(status == EINVAL)
	{
		isci_log_message(0, "ISCI", "bus_dmamap_load failed\n");
		return (status);
	}

	return (0);
}

/**
 * @brief This callback method asks the user to associate the supplied
 *        lock with an operating environment specific locking construct.
 *
 * @param[in]  controller This parameter specifies the controller with
 *             which this lock is to be associated.
 * @param[in]  lock This parameter specifies the lock for which the
 *             user should associate an operating environment specific
 *             locking object.
 *
 * @see The SCI_LOCK_LEVEL enumeration for more information.
 *
 * @return none.
 */
void
scif_cb_lock_associate(SCI_CONTROLLER_HANDLE_T controller,
    SCI_LOCK_HANDLE_T lock)
{

}

/**
 * @brief This callback method asks the user to de-associate the supplied
 *        lock with an operating environment specific locking construct.
 *
 * @param[in]  controller This parameter specifies the controller with
 *             which this lock is to be de-associated.
 * @param[in]  lock This parameter specifies the lock for which the
 *             user should de-associate an operating environment specific
 *             locking object.
 *
 * @see The SCI_LOCK_LEVEL enumeration for more information.
 *
 * @return none.
 */
void
scif_cb_lock_disassociate(SCI_CONTROLLER_HANDLE_T controller,
    SCI_LOCK_HANDLE_T lock)
{

}

/**
 * @brief This callback method asks the user to acquire/get the lock.
 *        This method should pend until the lock has been acquired.
 *
 * @param[in]  controller This parameter specifies the controller with
 *             which this lock is associated.
 * @param[in]  lock This parameter specifies the lock to be acquired.
 *
 * @return none
 */
void
scif_cb_lock_acquire(SCI_CONTROLLER_HANDLE_T controller,
    SCI_LOCK_HANDLE_T lock)
{

}

/**
 * @brief This callback method asks the user to release a lock.
 *
 * @param[in]  controller This parameter specifies the controller with
 *             which this lock is associated.
 * @param[in]  lock This parameter specifies the lock to be released.
 *
 * @return none
 */
void
scif_cb_lock_release(SCI_CONTROLLER_HANDLE_T controller,
    SCI_LOCK_HANDLE_T lock)
{
}

/**
 * @brief This callback method creates an OS specific deferred task
 *        for internal usage. The handler to deferred task is stored by OS
 *        driver.
 *
 * @param[in] controller This parameter specifies the controller object
 *            with which this callback is associated.
 *
 * @return none
 */
void
scif_cb_start_internal_io_task_create(SCI_CONTROLLER_HANDLE_T controller)
{

}

/**
 * @brief This callback method schedules a OS specific deferred task.
 *
 * @param[in] controller This parameter specifies the controller
 *            object with which this callback is associated.
 * @param[in] start_internal_io_task_routine This parameter specifies the
 *            sci start_internal_io routine.
 * @param[in] context This parameter specifies a handle to a parameter
 *            that will be passed into the "start_internal_io_task_routine"
 *            when it is invoked.
 *
 * @return none
 */
void
scif_cb_start_internal_io_task_schedule(SCI_CONTROLLER_HANDLE_T scif_controller,
    FUNCPTR start_internal_io_task_routine, void *context)
{
	/** @todo Use FreeBSD tasklet to defer this routine to a later time,
	 *  rather than calling the routine inline.
	 */
	SCI_START_INTERNAL_IO_ROUTINE sci_start_internal_io_routine =
	    (SCI_START_INTERNAL_IO_ROUTINE)start_internal_io_task_routine;

	sci_start_internal_io_routine(context);
}

/**
 * @brief In this method the user must write to PCI memory via access.
 *        This method is used for access to memory space and IO space.
 *
 * @param[in]  controller The controller for which to read a DWORD.
 * @param[in]  address This parameter depicts the address into
 *             which to write.
 * @param[out] write_value This parameter depicts the value being written
 *             into the PCI memory location.
 *
 * @todo These PCI memory access calls likely needs to be optimized into macros?
 */
void
scic_cb_pci_write_dword(SCI_CONTROLLER_HANDLE_T scic_controller,
    void *address, uint32_t write_value)
{
	SCI_CONTROLLER_HANDLE_T scif_controller =
	    (SCI_CONTROLLER_HANDLE_T) sci_object_get_association(scic_controller);
	struct ISCI_CONTROLLER *isci_controller =
	    (struct ISCI_CONTROLLER *) sci_object_get_association(scif_controller);
	struct isci_softc *isci = isci_controller->isci;
	uint32_t bar = (uint32_t)(((POINTER_UINT)address & 0xF0000000) >> 28);
	bus_size_t offset = (bus_size_t)((POINTER_UINT)address & 0x0FFFFFFF);

	bus_space_write_4(isci->pci_bar[bar].bus_tag,
	    isci->pci_bar[bar].bus_handle, offset, write_value);
}

/**
 * @brief In this method the user must read from PCI memory via access.
 *        This method is used for access to memory space and IO space.
 *
 * @param[in]  controller The controller for which to read a DWORD.
 * @param[in]  address This parameter depicts the address from
 *             which to read.
 *
 * @return The value being returned from the PCI memory location.
 *
 * @todo This PCI memory access calls likely need to be optimized into macro?
 */
uint32_t
scic_cb_pci_read_dword(SCI_CONTROLLER_HANDLE_T scic_controller, void *address)
{
	SCI_CONTROLLER_HANDLE_T scif_controller =
		(SCI_CONTROLLER_HANDLE_T)sci_object_get_association(scic_controller);
	struct ISCI_CONTROLLER *isci_controller =
		(struct ISCI_CONTROLLER *)sci_object_get_association(scif_controller);
	struct isci_softc *isci = isci_controller->isci;
	uint32_t bar = (uint32_t)(((POINTER_UINT)address & 0xF0000000) >> 28);
	bus_size_t offset = (bus_size_t)((POINTER_UINT)address & 0x0FFFFFFF);

	return (bus_space_read_4(isci->pci_bar[bar].bus_tag,
	    isci->pci_bar[bar].bus_handle, offset));
}

/**
 * @brief This method is called when the core requires the OS driver
 *        to stall execution.  This method is utilized during initialization
 *        or non-performance paths only.
 *
 * @param[in]  microseconds This parameter specifies the number of
 *             microseconds for which to stall.  The operating system driver
 *             is allowed to round this value up where necessary.
 *
 * @return none.
 */
void
scic_cb_stall_execution(uint32_t microseconds)
{

	DELAY(microseconds);
}

/**
 * @brief In this method the user must return the base address register (BAR)
 *        value for the supplied base address register number.
 *
 * @param[in] controller The controller for which to retrieve the bar number.
 * @param[in] bar_number This parameter depicts the BAR index/number to be read.
 *
 * @return Return a pointer value indicating the contents of the BAR.
 * @retval NULL indicates an invalid BAR index/number was specified.
 * @retval All other values indicate a valid VIRTUAL address from the BAR.
 */
void *
scic_cb_pci_get_bar(SCI_CONTROLLER_HANDLE_T controller,
    uint16_t bar_number)
{

	return ((void *)(POINTER_UINT)((uint32_t)bar_number << 28));
}

/**
 * @brief This method informs the SCI Core user that a phy/link became
 *        ready, but the phy is not allowed in the port.  In some
 *        situations the underlying hardware only allows for certain phy
 *        to port mappings.  If these mappings are violated, then this
 *        API is invoked.
 *
 * @param[in] controller This parameter represents the controller which
 *            contains the port.
 * @param[in] port This parameter specifies the SCI port object for which
 *            the callback is being invoked.
 * @param[in] phy This parameter specifies the phy that came ready, but the
 *            phy can't be a valid member of the port.
 *
 * @return none
 */
void
scic_cb_port_invalid_link_up(SCI_CONTROLLER_HANDLE_T controller,
    SCI_PORT_HANDLE_T port, SCI_PHY_HANDLE_T phy)
{

}


text:
#include <sqlite3ext.h>
SQLITE_EXTENSION_INIT1

#include "list.h"
#include "vector.h"
#include "address.h"
#include "sentinel.h"
#include "connection.h"
#include "redis.h"

#include <hiredis/hiredis.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

/* A redis backed sqlite3 virtual table implementation.
 * prefix.db.table:[rowid]      = hash of the row data.
 * prefix.db.table.rowid        = sequence from which rowids are generated.
 * prefix.db.table.index.rowid  = master index (zset) of rows in the table
 * prefix.db.table.indices      = master index (set) of indices on the table
 * prefix.db.table.index:x      = value zset index for column x
 * prefix.db.table.index:x:val  = rowid map for value val in column x */

typedef struct redis_vtbl_vtab {
    sqlite3_vtab base;

    redis_vtbl_connection conn;
    char *key_base;

    vector_t columns;
} redis_vtbl_vtab;

static int redis_vtbl_create(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr);
static int redis_vtbl_connect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr);
static int redis_vtbl_bestindex(sqlite3_vtab *pVTab, sqlite3_index_info *pIndexInfo);
static int redis_vtbl_update(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid);
static int redis_vtbl_disconnect(sqlite3_vtab *pVTab);
static int redis_vtbl_destroy(sqlite3_vtab *pVTab);

static void redis_vtbl_func_createindex(sqlite3_context *ctx, int argc, sqlite3_value **argv);

enum {
    CURSOR_INDEX_SCAN,

    CURSOR_INDEX_ROWID_EQ,
    CURSOR_INDEX_ROWID_GT,
    CURSOR_INDEX_ROWID_LT,
    CURSOR_INDEX_ROWID_GE,
    CURSOR_INDEX_ROWID_LE,

    CURSOR_INDEX_NAMED_EQ,
    CURSOR_INDEX_NAMED_GT,
    CURSOR_INDEX_NAMED_LT,
    CURSOR_INDEX_NAMED_GE,
    CURSOR_INDEX_NAMED_LE,
};

typedef struct redis_vtbl_cursor {
    sqlite3_vtab_cursor base;
    redis_vtbl_vtab *vtab;

    vector_t rows;
    sqlite3_int64 *current_row;

    list_t column_data;
    int column_data_valid;
} redis_vtbl_cursor;

static int redis_vtbl_cursor_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
static int redis_vtbl_cursor_close(sqlite3_vtab_cursor *pCursor);
static int redis_vtbl_cursor_filter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv);
static int redis_vtbl_cursor_next(sqlite3_vtab_cursor *pCursor);
static int redis_vtbl_cursor_eof(sqlite3_vtab_cursor *pCursor);
static int redis_vtbl_cursor_column(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int N);
static int redis_vtbl_cursor_rowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid);

/*-----------------------------------------------------------------------------
 * String helpers
 *----------------------------------------------------------------------------*/

static int string_append(char** str, const char* src) {
    char* s;
    size_t str_sz = *str ? strlen(*str) : 0;
    size_t src_sz = strlen(src);

    s = realloc(*str, str_sz+src_sz+1);
    if(!s) return 1;
    *str = s;
    strcpy(s + str_sz, src);
    s[str_sz+src_sz] = 0;
    return 0;
}

/*-----------------------------------------------------------------------------
 * Column definition
 *----------------------------------------------------------------------------*/

typedef struct redis_vtbl_column_spec {
    char *name;
    int data_type;
    int indexed;
} redis_vtbl_column_spec;

static int column_type_int_p(const char *data_type) {
    return !strncasecmp(data_type, "INT", 3);
}
static int column_type_text_p(const char *data_type) {
    return !strncasecmp(data_type, "CHAR", 4) || !strncasecmp(data_type, "CLOB", 4) || !strncasecmp(data_type, "TEXT", 4);
}
static int column_type_float_p(const char *data_type) {
    return !strncasecmp(data_type, "REAL", 4) || !strncasecmp(data_type, "FLOA", 4) || !strncasecmp(data_type, "DOUB", 4);
}

static int redis_vtbl_column_spec_init(redis_vtbl_column_spec *cspec, const char *column_def) {
    int err;
    list_t tok_list;
    const char *data_type;

    err = list_strtok(&tok_list, column_def, " \t\n");
    if(err) return 1;

    if(tok_list.size < 1) {
        list_free(&tok_list);
        return 1;
    }

    /* Column name */
    cspec->name = list_set(&tok_list, 0, 0);

    /* If the next token is not CONSTRAINT then it's a type name
     * Determine column type based on sqlite affinity rules.
     * Default case is TEXT where whereas the sqlite default is NUMERIC */
    cspec->data_type = SQLITE_TEXT;

    data_type = list_get(&tok_list, 1);
    if(tok_list.size > 1 && strcasecmp(data_type, "CONSTRAINT")) {

        if(column_type_int_p(data_type))
            cspec->data_type = SQLITE_INTEGER;

        else if(column_type_text_p(data_type))
            cspec->data_type = SQLITE_TEXT;

        else if(column_type_float_p(data_type))
            cspec->data_type = SQLITE_FLOAT;
    }

    cspec->indexed = 0;

    list_free(&tok_list);
    return 0;
}

static int redis_vtbl_column_spec_name_cmp(const char *l, const redis_vtbl_column_spec *r) {
    return strcmp(l, r->name);
}

static void redis_vtbl_column_spec_free(redis_vtbl_column_spec *cspec) {
    free(cspec->name);
}

/*-----------------------------------------------------------------------------
 * Redis backed Virtual table
 *----------------------------------------------------------------------------*/

static int redis_vtbl_vtab_init(redis_vtbl_vtab *vtab, const char *conn_config, const char *db, const char *table, const char *prefix, char **pzErr);
static int redis_vtbl_vtab_update_indices(redis_vtbl_vtab *vtab);
static int redis_vtbl_vtab_generate_rowid(redis_vtbl_vtab *vtab, sqlite3_int64 *rowid);
static void redis_vtbl_vtab_free(redis_vtbl_vtab *vtab);

static int redis_vtbl_vtab_init(redis_vtbl_vtab *vtab, const char *conn_config, const char *db, const char *table, const char *prefix, char **pzErr) {
    int err;

    memset(&vtab->base, 0, sizeof(sqlite3_vtab));

    err = redis_vtbl_connection_init(&vtab->conn, conn_config);
    if(err) {
        switch(err) {
            case CONNECTION_BAD_FORMAT:
                *pzErr = sqlite3_mprintf("Bad format; Expected ip[:port] | sentinel service ip[:port] [ip[:port]...], key_prefix, column_def0, ...column_defN");
                return SQLITE_ERROR;
            case CONNECTION_ENOMEM:
                return SQLITE_NOMEM;
        }
    }

    vtab->key_base = 0;

    string_append(&vtab->key_base, prefix);
    string_append(&vtab->key_base, ".");
    string_append(&vtab->key_base, db);
    string_append(&vtab->key_base, ".");
    string_append(&vtab->key_base, table);

    if(!vtab->key_base) {
        redis_vtbl_connection_free(&vtab->conn);
        return SQLITE_NOMEM;
    }

    vector_init(&vtab->columns, sizeof(redis_vtbl_column_spec), (void(*)(void*))redis_vtbl_column_spec_free);

    return SQLITE_OK;
}

static int redis_vtbl_vtab_update_indices(redis_vtbl_vtab *vtab) {
    int err;
    redis_vtbl_command cmd;
    list_t indexes;
    redisReply *reply;
    size_t i;
    redis_vtbl_column_spec *cspec;
    void *indexed;

    /* retrieve indices from redis */
    redis_vtbl_command_init_arg(&cmd, "SMEMBERS");
    redis_vtbl_command_arg_fmt(&cmd, "%s.indices", vtab->key_base);
    reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
    if(!reply) return 1;

    list_init(&indexes, free);
    err = redis_reply_string_list(&indexes, reply);
    freeReplyObject(reply);

    for(i = 0; i < vtab->columns.size; ++i) {
        cspec = vector_get(&vtab->columns, i);
        indexed = list_find(&indexes, cspec->name, (int(*)(const void*, const void*))strcmp);
        cspec->indexed = indexed ? 1 : 0;
    }

    list_free(&indexes);

    if(err)  return 1;
    return 0;
}

static int redis_vtbl_vtab_generate_rowid(redis_vtbl_vtab *vtab, sqlite3_int64 *rowid) {
    redis_vtbl_command cmd;
    redisReply *reply;

    redis_vtbl_command_init_arg(&cmd, "INCR");
    redis_vtbl_command_arg_fmt(&cmd, "%s.rowid", vtab->key_base);
    reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
    if(!reply) return 1;

    if(reply->type != REDIS_REPLY_INTEGER) {
        freeReplyObject(reply);
        return 1;
    }

    *rowid = reply->integer;
    freeReplyObject(reply);
    return 0;
}

static void redis_vtbl_vtab_free(redis_vtbl_vtab *vtab) {
    redis_vtbl_connection_free(&vtab->conn);
    free(vtab->key_base);
    vector_free(&vtab->columns);
}

/* argv[1]    - database name
 * argv[2]    - table name
 * argv[3]    - address of redis e.g. "127.0.0.1:6379"
 *  -or-      - address(es) of sentinel "sentinel service-name 127.0.0.1:6379;192.168.1.1"
 * argv[4]    - key prefix
 * argv[5...] - column defintions */
static int redis_vtbl_create(sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVTab, char **pzErr) {
    int i;
    size_t n;
    int err;
    list_t column;
    redis_vtbl_vtab *vtab;
    redis_vtbl_column_spec cspec;

    (void)pAux; /* unused */

    if(argc < 6) {
        *pzErr = sqlite3_mprintf("Bad format; Expected ip[:port] | sentinel service ip[:port] [ip[:port]...], key_prefix, column_def0, ...column_defN");
        return SQLITE_ERROR;
    }

    const char *db_name = argv[1];
    const char *table = argv[2];
    const char *conn_config = argv[3];
    const char *prefix = argv[4];

    vtab = malloc(sizeof(redis_vtbl_vtab));
    if(!vtab) return SQLITE_NOMEM;

    /* Initialises structure parameters
     * Parses and validates configuration
     * returns an sqlite error code on failure */
    err = redis_vtbl_vtab_init(vtab, conn_config, db_name, table, prefix, pzErr);
    if(err) {
        /* error message is already set */
        free(vtab);
        return err;
    }

    /* Attempt to connect to redis.
     * Will either connect via sentinel or directly to redis depending on the configuration. */
    err = redis_vtbl_connection_connect(&vtab->conn);
    if(err) {
        if(vtab->conn.service) {
            *pzErr = sqlite3_mprintf("Sentinel: %s", vtab->conn.errstr);
        } else {
            *pzErr = sqlite3_mprintf("Redis: %s", vtab->conn.errstr);
        }

        redis_vtbl_vtab_free(vtab);
        free(vtab);
        return SQLITE_ERROR;
    }

    list_init(&column, 0);
    for(i = 5; i < argc; ++i)
        list_push(&column, (char*)argv[i]);

    /* parse column names and types */
    for(n = 0; n < column.size; ++n) {
        err = redis_vtbl_column_spec_init(&cspec, list_get(&column, n));
        if(err) {
            *pzErr = sqlite3_mprintf("Unable to parse column definition '%s'", list_get(&column, n));

            list_free(&column);
            redis_vtbl_vtab_free(vtab);
            free(vtab);
            return SQLITE_ERROR;
        }

        vector_push(&vtab->columns, &cspec);
    }

    /* retrieve indices from redis */
    err = redis_vtbl_vtab_update_indices(vtab);
    if(err) {
        *pzErr = sqlite3_mprintf("Unable to retrieve indices from redis");

        list_free(&column);
        redis_vtbl_vtab_free(vtab);
        free(vtab);
        return SQLITE_ERROR;
    }

    /* Create table definition and pass to sqlite */
    char* s = 0;
    string_append(&s, "CREATE TABLE xxxx(");
    for(n = 0; n < column.size; ) {
        string_append(&s, list_get(&column, n));
        ++n;
        if(n < column.size)
            string_append(&s, ", ");
    }
    string_append(&s, ")");
    if(!s) return SQLITE_NOMEM;

    err = sqlite3_declare_vtab(db, s);
    if(err) return err;
    free(s);

    list_free(&column);

    *ppVTab = (sqlite3_vtab*)vtab;
    return SQLITE_OK;
}

static int redis_vtbl_connect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) {
    return redis_vtbl_create(db, pAux, argc, argv, ppVTab, pzErr);
}

static int redis_vtbl_bestindex(sqlite3_vtab *pVTab, sqlite3_index_info *pIndexInfo) {
    redis_vtbl_vtab *vtab;
    int i;

    vtab = (redis_vtbl_vtab*)pVTab;

    /* Explanation of cost constants
     * 10000.0  Guess at cost of full table scan
     * 1.0      Lookup by rowid is O(1)
     * 2500.0   Lookup by relative rowid is fairly cheap
     * 10.0     Lookup by index incurs some lookup overhead
     * 5000.0   Lookup by relative index requires multiple ops */
    pIndexInfo->idxNum = CURSOR_INDEX_SCAN;
    pIndexInfo->estimatedCost = 10000.0;

    for(i = 0; i < pIndexInfo->nConstraint; ++i) {
        struct sqlite3_index_constraint *constraint = &pIndexInfo->aConstraint[i];
        if(!constraint->usable) continue;

        if(constraint->iColumn == /* rowid */ -1) {
            pIndexInfo->aConstraintUsage[i].argvIndex = 1;
            pIndexInfo->estimatedCost = constraint->op == SQLITE_INDEX_CONSTRAINT_EQ ? 1.0 : 2500.0;

            switch(constraint->op) {
                case SQLITE_INDEX_CONSTRAINT_EQ:
                    pIndexInfo->idxNum = CURSOR_INDEX_ROWID_EQ;
                    break;
                case SQLITE_INDEX_CONSTRAINT_GT:
                    pIndexInfo->idxNum = CURSOR_INDEX_ROWID_GT;
                    break;
                case SQLITE_INDEX_CONSTRAINT_LE:
                    pIndexInfo->idxNum = CURSOR_INDEX_ROWID_LE;
                    break;
                case SQLITE_INDEX_CONSTRAINT_LT:
                    pIndexInfo->idxNum = CURSOR_INDEX_ROWID_LT;
                    break;
                case SQLITE_INDEX_CONSTRAINT_GE:
                    pIndexInfo->idxNum = CURSOR_INDEX_ROWID_GE;
                    break;
            }
            break;
        } else {
            redis_vtbl_column_spec *cspec;

            /* lookup the column by id
             * Determine if that column is indexed */
            cspec = vector_get(&vtab->columns, constraint->iColumn);
            if(!cspec) return SQLITE_ERROR;

            if(cspec->indexed) {
                pIndexInfo->aConstraintUsage[i].argvIndex = 1;
                pIndexInfo->estimatedCost = constraint->op == SQLITE_INDEX_CONSTRAINT_EQ ? 10.0 : 5000.0;
                pIndexInfo->idxStr = cspec->name;

                switch(constraint->op) {
                    case SQLITE_INDEX_CONSTRAINT_EQ:
                        pIndexInfo->idxNum = CURSOR_INDEX_NAMED_EQ;
                        break;
                    case SQLITE_INDEX_CONSTRAINT_GT:
                        pIndexInfo->idxNum = CURSOR_INDEX_NAMED_GT;
                        break;
                    case SQLITE_INDEX_CONSTRAINT_LE:
                        pIndexInfo->idxNum = CURSOR_INDEX_NAMED_LE;
                        break;
                    case SQLITE_INDEX_CONSTRAINT_LT:
                        pIndexInfo->idxNum = CURSOR_INDEX_NAMED_LT;
                        break;
                    case SQLITE_INDEX_CONSTRAINT_GE:
                        pIndexInfo->idxNum = CURSOR_INDEX_NAMED_GE;
                        break;
                }
                break;
            }
        }
    }

    return SQLITE_OK;
}

static int redis_vtbl_findfunction(sqlite3_vtab *pVtab, int nArg, const char *zName, void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg) {
    redis_vtbl_vtab *vtab;

    vtab = (redis_vtbl_vtab*)pVtab;
    if(nArg == 1 && !strcmp(zName, "redis_create_index")) {
        *pxFunc = redis_vtbl_func_createindex;
        *ppArg = vtab;          /* may need to pass some extra data through the context. */
        return 1;
    }

    /* todo overload other functions to improve performance.
     * min and max are simple examples that atm require a table scan. */

    return 0;
}

static int redis_vtbl_exec_insert(redis_vtbl_vtab *vtab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid);
static int redis_vtbl_exec_update(redis_vtbl_vtab *vtab, int argc, sqlite3_value **argv);
static int redis_vtbl_exec_delete(redis_vtbl_vtab *vtab, sqlite3_int64 row_id);

static int redis_vtbl_update(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) {
    int err;
    redis_vtbl_vtab *vtab;

    vtab = (redis_vtbl_vtab*)pVTab;

    if(argc == 1) {                                                     /* delete */
        sqlite3_int64 row_id;

        row_id = sqlite3_value_int64(argv[0]);
        err = redis_vtbl_exec_delete(vtab, row_id);

    } else if(sqlite3_value_type(argv[0]) == SQLITE_NULL) {             /* insert */
        err = redis_vtbl_exec_insert(vtab, argc, argv, pRowid);

    } else if(argv[0] == argv[1]) {                                     /* update */
        err = redis_vtbl_exec_update(vtab, argc, argv);

    } else {
        pVTab->zErrMsg = sqlite3_mprintf("User provided rowid disallowed.");
        return SQLITE_ERROR;        /* attempt to update rowid disallowed */
    }

    return err;
}

static int redis_vtbl_exec_insert(redis_vtbl_vtab *vtab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) {
    int err;
    sqlite3_int64 row_id;
    redis_vtbl_command cmd;
    size_t i;
    redis_vtbl_column_spec *cspec;
    list_t replies;
    list_t expected;
    list_t expected_exec;

    if(sqlite3_value_type(argv[1]) != SQLITE_NULL) {
        vtab->base.zErrMsg = sqlite3_mprintf("User provided rowid disallowed.");
        return SQLITE_ERROR;            /* attempt to specify manual rowid disallowed */
    }

    if((unsigned)argc != vtab->columns.size + 2) {
        return SQLITE_ERROR;            /* correct number of columns not provided */
    }

    err = redis_vtbl_vtab_generate_rowid(vtab, &row_id);
    if(err) {
        vtab->base.zErrMsg = sqlite3_mprintf("Unable to generate new rowid");
        return SQLITE_ERROR;
    }
    *pRowid = row_id;

    list_init(&expected, 0);
    list_init(&expected_exec, 0);

    redis_vtbl_command_init_arg(&cmd, "MULTI");
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_reply_p);

    /* HMSET key column0 value0 ...columnN valueN */                                                    /* create object */
    redis_vtbl_command_init_arg(&cmd, "HMSET");
    redis_vtbl_command_arg_fmt(&cmd, "%s:%lld", vtab->key_base, row_id);
    for(i = 0; i < vtab->columns.size; ++i) {
        cspec = vector_get(&vtab->columns, i);
        redis_vtbl_command_arg(&cmd, cspec->name);
        redis_vtbl_command_arg(&cmd, (const char*)sqlite3_value_text(argv[2 + i]));
    }
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_queued_reply_p);
    list_push(&expected_exec, redis_status_reply_p);

    redis_vtbl_command_init_arg(&cmd, "ZADD");                                                          /* add to rowids */
    redis_vtbl_command_arg_fmt(&cmd, "%s.index.rowid", vtab->key_base);
    redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
    redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_queued_reply_p);
    list_push(&expected_exec, redis_integer_reply_p);

    for(i = 0; i < vtab->columns.size; ++i) {                                                           /* add to indexes */
        cspec = vector_get(&vtab->columns, i);
        if(!cspec->indexed) continue;

        if(cspec->data_type == SQLITE_INTEGER) {
            sqlite3_int64 value;
            value = sqlite3_value_int64(argv[2 + i]);

            redis_vtbl_command_init_arg(&cmd, "ZADD");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, cspec->name);
            redis_vtbl_command_arg_fmt(&cmd, "%lld", value);
            redis_vtbl_command_arg_fmt(&cmd, "%lld", value);
            redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
            list_push(&expected, redis_status_queued_reply_p);
            list_push(&expected_exec, redis_integer_reply_p);
        } else if(cspec->data_type == SQLITE_FLOAT) {
            double value;
            value = sqlite3_value_double(argv[2 + i]);

            redis_vtbl_command_init_arg(&cmd, "ZADD");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, cspec->name);
            redis_vtbl_command_arg_fmt(&cmd, "%f", value);
            redis_vtbl_command_arg_fmt(&cmd, "%f", value);
            redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
            list_push(&expected, redis_status_queued_reply_p);
            list_push(&expected_exec, redis_integer_reply_p);
        } else {
            const char *value;
            value = (const char*)sqlite3_value_text(argv[2 + i]);

            redis_vtbl_command_init_arg(&cmd, "ZADD");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, cspec->name);
            redis_vtbl_command_arg(&cmd, "0");
            redis_vtbl_command_arg_fmt(&cmd, "%s", value);
            redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
            list_push(&expected, redis_status_queued_reply_p);
            list_push(&expected_exec, redis_integer_reply_p);
        }

        redis_vtbl_command_init_arg(&cmd, "SADD");
        redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s:%s", vtab->key_base, cspec->name, (const char*)sqlite3_value_text(argv[2 + i]));
        redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
        redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
        list_push(&expected, redis_status_queued_reply_p);
        list_push(&expected_exec, redis_integer_reply_p);
    }

    redis_vtbl_command_init_arg(&cmd, "EXEC");
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_bulk_reply_p);

    list_init(&replies, freeReplyObject);
    redis_vtbl_connection_read_queued(&vtab->conn, &replies);

    err = redis_check_expected_list(&replies, &expected);
    list_free(&expected);

    if(err) {
        list_free(&replies);
        list_free(&expected_exec);
        return SQLITE_ERROR;

    } else {
        redisReply *exec_reply;
        exec_reply = list_get(&replies, replies.size-1);
        err = redis_check_expected_bulk_list(exec_reply, &expected_exec);
        list_free(&expected_exec);
        if(err) {
            list_free(&replies);
            return SQLITE_ERROR;
        }
    }
    list_free(&replies);

    return SQLITE_OK;
}
static int redis_vtbl_exec_update(redis_vtbl_vtab *vtab, int argc, sqlite3_value **argv) {
    int err;
    sqlite3_int64 row_id;
    redis_vtbl_command cmd;
    size_t i;
    redis_vtbl_column_spec *cspec;
    list_t replies;
    list_t expected;
    list_t expected_exec;

    if((unsigned)argc != vtab->columns.size + 2) {
        return SQLITE_ERROR;            /* correct number of columns not provided */
    }

    list_init(&expected, 0);
    list_init(&expected_exec, 0);

    row_id = sqlite3_value_int64(argv[0]);
    redis_vtbl_command_init_arg(&cmd, "WATCH");                                                         /* WATCH key */
    redis_vtbl_command_arg_fmt(&cmd, "%s:%lld", vtab->key_base, row_id);
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_reply_p);

    redis_vtbl_command_init_arg(&cmd, "MULTI");
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_reply_p);

    redis_vtbl_command_init_arg(&cmd, "EVAL");                                                          /* update indexes (a) */
    redis_vtbl_command_arg(&cmd, "\
        local key_base = ARGV[1];\n\
        local row_id = ARGV[2];\n\
        \n\
        local indexed_columns = redis.call('SMEMBERS', key_base..'.indices');\n\
        for _,column_name in ipairs(indexed_columns) do\n\
            local column_value = redis.call('HGET', key_base..':'..row_id, column_name);\n\
            local value_index = key_base..'.index:'..column_name..':'..column_value;\n\
            redis.call('SREM', value_index, row_id);\n\
            if(redis.call('EXISTS', value_index) == 0) then\n\
                redis.call('ZREM', key_base..'.index:'..column_name, column_value);\n\
            end\n\
        end\n\
        return 1;\n");
    redis_vtbl_command_arg(&cmd, "2");
    redis_vtbl_command_arg_fmt(&cmd, "%s.indices", vtab->key_base);
    redis_vtbl_command_arg_fmt(&cmd, "%s:%lld", vtab->key_base, row_id);
    redis_vtbl_command_arg(&cmd, vtab->key_base);
    redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_queued_reply_p);
    list_push(&expected_exec, redis_integer_reply_p);

    /* HMSET key column0 value0 ...columnN valueN */                                                    /* update object */
    redis_vtbl_command_init_arg(&cmd, "HMSET");
    redis_vtbl_command_arg_fmt(&cmd, "%s:%lld", vtab->key_base, row_id);

    for(i = 0; i < vtab->columns.size; ++i) {
        cspec = vector_get(&vtab->columns, i);
        redis_vtbl_command_arg(&cmd, cspec->name);
        redis_vtbl_command_arg(&cmd, (const char*)sqlite3_value_text(argv[2 + i]));
    }
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_queued_reply_p);
    list_push(&expected_exec, redis_status_reply_p);

    for(i = 0; i < vtab->columns.size; ++i) {                                                           /* update indexes (b) */
        cspec = vector_get(&vtab->columns, i);
        if(!cspec->indexed) continue;

        if(cspec->data_type == SQLITE_INTEGER) {
            sqlite3_int64 value;
            value = sqlite3_value_int64(argv[2 + i]);

            redis_vtbl_command_init_arg(&cmd, "ZADD");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, cspec->name);
            redis_vtbl_command_arg_fmt(&cmd, "%lld", value);
            redis_vtbl_command_arg_fmt(&cmd, "%lld", value);
            redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
            list_push(&expected, redis_status_queued_reply_p);
            list_push(&expected_exec, redis_integer_reply_p);
        } else if(cspec->data_type == SQLITE_FLOAT) {
            double value;
            value = sqlite3_value_double(argv[2 + i]);

            redis_vtbl_command_init_arg(&cmd, "ZADD");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, cspec->name);
            redis_vtbl_command_arg_fmt(&cmd, "%f", value);
            redis_vtbl_command_arg_fmt(&cmd, "%f", value);
            redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
            list_push(&expected, redis_status_queued_reply_p);
            list_push(&expected_exec, redis_integer_reply_p);
        } else {
            const char *value;
            value = (const char*)sqlite3_value_text(argv[2 + i]);

            redis_vtbl_command_init_arg(&cmd, "ZADD");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, cspec->name);
            redis_vtbl_command_arg(&cmd, "0");
            redis_vtbl_command_arg_fmt(&cmd, "%s", value);
            redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
            list_push(&expected, redis_status_queued_reply_p);
            list_push(&expected_exec, redis_integer_reply_p);
        }

        redis_vtbl_command_init_arg(&cmd, "SADD");
        redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s:%s", vtab->key_base, cspec->name, (const char*)sqlite3_value_text(argv[2 + i]));
        redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
        redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
        list_push(&expected, redis_status_queued_reply_p);
        list_push(&expected_exec, redis_integer_reply_p);
    }

    redis_vtbl_command_init_arg(&cmd, "EXEC");
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_bulk_reply_p);

    list_init(&replies, freeReplyObject);
    redis_vtbl_connection_read_queued(&vtab->conn, &replies);

    err = redis_check_expected_list(&replies, &expected);
    list_free(&expected);

    if(err) {
        list_free(&replies);
        list_free(&expected_exec);
        return SQLITE_ERROR;

    } else {
        redisReply *exec_reply;
        exec_reply = list_get(&replies, replies.size-1);
        err = redis_check_expected_bulk_list(exec_reply, &expected_exec);
        list_free(&expected_exec);
        if(err) {
            list_free(&replies);
            return SQLITE_ERROR;
        }
    }
    list_free(&replies);

    return SQLITE_OK;
}
static int redis_vtbl_exec_delete(redis_vtbl_vtab *vtab, sqlite3_int64 row_id) {
    int err;
    redis_vtbl_command cmd;
    list_t replies;
    list_t expected;
    list_t expected_exec;

    list_init(&expected, 0);
    list_init(&expected_exec, 0);

    redis_vtbl_command_init_arg(&cmd, "MULTI");
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_reply_p);

    redis_vtbl_command_init_arg(&cmd, "EVAL");                                                          /* erase from indexes */
    redis_vtbl_command_arg(&cmd, "\
        local key_base = ARGV[1];\n\
        local row_id = ARGV[2];\n\
        \n\
        local indexed_columns = redis.call('SMEMBERS', key_base..'.indices');\n\
        for _,column_name in ipairs(indexed_columns) do\n\
            local column_value = redis.call('HGET', key_base..':'..row_id, column_name);\n\
            local value_index = key_base..'.index:'..column_name..':'..column_value;\n\
            redis.call('SREM', value_index, row_id);\n\
            if(redis.call('EXISTS', value_index) == 0) then\n\
                redis.call('ZREM', key_base..'.index:'..column_name, column_value);\n\
            end\n\
        end\n\
        return 1;\n");
    redis_vtbl_command_arg(&cmd, "2");
    redis_vtbl_command_arg_fmt(&cmd, "%s.indices", vtab->key_base);
    redis_vtbl_command_arg_fmt(&cmd, "%s:%lld", vtab->key_base, row_id);
    redis_vtbl_command_arg(&cmd, vtab->key_base);
    redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_queued_reply_p);
    list_push(&expected_exec, redis_integer_reply_p);

    redis_vtbl_command_init_arg(&cmd, "DEL");                                                           /* erase object */
    redis_vtbl_command_arg_fmt(&cmd, "%s:%lld", vtab->key_base, row_id);
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_queued_reply_p);
    list_push(&expected_exec, redis_integer_reply_p);

    redis_vtbl_command_init_arg(&cmd, "ZREM");                                                          /* erase from rowids */
    redis_vtbl_command_arg_fmt(&cmd, "%s.index.rowid", vtab->key_base);
    redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_status_queued_reply_p);
    list_push(&expected_exec, redis_integer_reply_p);

    redis_vtbl_command_init_arg(&cmd, "EXEC");
    redis_vtbl_connection_command_enqueue(&vtab->conn, &cmd);
    list_push(&expected, redis_bulk_reply_p);

    list_init(&replies, freeReplyObject);
    redis_vtbl_connection_read_queued(&vtab->conn, &replies);

    err = redis_check_expected_list(&replies, &expected);
    list_free(&expected);

    if(err) {
        list_free(&replies);
        list_free(&expected_exec);
        return SQLITE_ERROR;

    } else {
        redisReply *exec_reply;
        exec_reply = list_get(&replies, replies.size-1);
        err = redis_check_expected_bulk_list(exec_reply, &expected_exec);
        list_free(&expected_exec);
        if(err) {
            list_free(&replies);
            return SQLITE_ERROR;
        }
    }
    list_free(&replies);

    return SQLITE_OK;
}

static int redis_vtbl_disconnect(sqlite3_vtab *pVTab) {
    return redis_vtbl_destroy(pVTab);
}

static int redis_vtbl_destroy(sqlite3_vtab *pVTab) {
    redis_vtbl_vtab *vtab;

    vtab = (redis_vtbl_vtab*)pVTab;
    redis_vtbl_vtab_free(vtab);
    free(vtab);
    return SQLITE_OK;
}

/*-----------------------------------------------------------------------------
 * Utility function to define indexes
 *----------------------------------------------------------------------------*/

/* column name */
static void redis_vtbl_func_createindex(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
    redis_vtbl_vtab *vtab;

    vtab = sqlite3_user_data(ctx);

    (void)vtab; /* pending impl */
    (void)argc; /* pending impl */
    (void)argv; /* pending impl */

    /* todo create the index
     * Seems it may be necessary to pass some data through the context. */

    sqlite3_result_int(ctx, 1);
}

/*-----------------------------------------------------------------------------
 * Redis backed virtual table cursor
 *----------------------------------------------------------------------------*/

static int redis_vtbl_cursor_init(redis_vtbl_cursor *cur, redis_vtbl_vtab *vtab) {
    memset(&cur->base, 0, sizeof(sqlite3_vtab_cursor));

    cur->vtab = vtab;

    vector_init(&cur->rows, sizeof(int64_t), 0);
    cur->current_row = 0;

    list_init(&cur->column_data, free);
    cur->column_data_valid = 0;

    return SQLITE_OK;
}

/*
Thoughts for cursor get:
It would be good to retrieve batched row data.
i.e. first get retrieves one row
next 2
next 4, etc (n * 1.618)

Additionally it would be good to not retrieve the entire row,
but only the subset of columns that are going to be requested.
*/
/* retrieve column_data for current_row */
static void redis_vtbl_cursor_get(redis_vtbl_cursor *cur) {
    int err;
    int eof;
    redis_vtbl_vtab *vtab;
    redis_vtbl_command cmd;
    redisReply *reply;
    size_t i;
    redis_vtbl_column_spec *cspec;

    eof = cur->current_row == vector_end(&cur->rows);
    if(eof) return;

    vtab = cur->vtab;
    list_clear(&cur->column_data);

    /* HMGET key_base.current_row column0 ...columnN */
    redis_vtbl_command_init_arg(&cmd, "HMGET");
    redis_vtbl_command_arg_fmt(&cmd, "%s:%lld", vtab->key_base, *cur->current_row);

    for(i = 0; i < vtab->columns.size; ++i) {
        cspec = vector_get(&vtab->columns, i);
        redis_vtbl_command_arg(&cmd, cspec->name);
    }

    reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
    if(!reply) return;

    /* note: This implementation differs from the prototype.
     * In the original if a record was concurrently removed,
     * the next record was automatically retrieved.
     * This version will return a row full of null values.
     * It is uncertain at this time which approach is better. */
    err = redis_reply_string_list(&cur->column_data, reply);
    freeReplyObject(reply);
    if(err) return;
    cur->column_data_valid = 1;
}

static void redis_vtbl_cursor_free(redis_vtbl_cursor *cur) {
    vector_free(&cur->rows);
    list_free(&cur->column_data);
}

static int redis_vtbl_cursor_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
    int err;
    redis_vtbl_vtab *vtab;
    redis_vtbl_cursor *cursor;

    vtab = (redis_vtbl_vtab*)pVTab;

    cursor = malloc(sizeof(redis_vtbl_cursor));
    if(!cursor) return SQLITE_NOMEM;

    err = redis_vtbl_cursor_init(cursor, vtab);
    if(err) {
        free(cursor);
        return err;
    }

    *ppCursor = (sqlite3_vtab_cursor*)cursor;
    return SQLITE_OK;
}

static int redis_vtbl_cursor_close(sqlite3_vtab_cursor *pCursor) {
    redis_vtbl_cursor *cursor;

    cursor = (redis_vtbl_cursor*)pCursor;
    redis_vtbl_cursor_free(cursor);
    free(cursor);

    return SQLITE_OK;
}

static int redis_vtbl_cursor_filter_scan(redis_vtbl_cursor *cursor);
static int redis_vtbl_cursor_filter_rowid(redis_vtbl_cursor *cursor, sqlite3_int64 row_id, int idxNum);
static int redis_vtbl_cursor_filter_index(redis_vtbl_cursor *cursor, int idxNum, const char *idxStr, sqlite3_value *value);
static int redis_vtbl_cursor_filter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) {
    int err;
    redis_vtbl_cursor *cursor;
    sqlite3_int64 row_id;

    cursor = (redis_vtbl_cursor*)pCursor;
    vector_clear(&cursor->rows);
    err = SQLITE_ERROR;

    switch(idxNum) {
        case CURSOR_INDEX_SCAN:
            err = redis_vtbl_cursor_filter_scan(cursor);
            break;
        case CURSOR_INDEX_ROWID_EQ:
        case CURSOR_INDEX_ROWID_GT:
        case CURSOR_INDEX_ROWID_LT:
        case CURSOR_INDEX_ROWID_GE:
        case CURSOR_INDEX_ROWID_LE:
            if(argc == 0) return SQLITE_ERROR;          /* Internal error. row_id not passed after request in bestindex */
            row_id = sqlite3_value_int64(argv[0]);
            err = redis_vtbl_cursor_filter_rowid(cursor, row_id, idxNum);
            break;
        case CURSOR_INDEX_NAMED_EQ:
        case CURSOR_INDEX_NAMED_GT:
        case CURSOR_INDEX_NAMED_LT:
        case CURSOR_INDEX_NAMED_GE:
        case CURSOR_INDEX_NAMED_LE:
            if(argc == 0) return SQLITE_ERROR;          /* Internal error. value not passed after request in bestindex */
            err = redis_vtbl_cursor_filter_index(cursor, idxNum, idxStr, argv[0]);
            break;
    }

    if(!err) {
        cursor->current_row = vector_begin(&cursor->rows);
        cursor->column_data_valid = 0;
    }
    return err;
}
static int redis_vtbl_cursor_filter_scan(redis_vtbl_cursor *cursor) {
    int err;
    redis_vtbl_vtab *vtab;
    redis_vtbl_command cmd;
    redisReply *reply;

    vtab = cursor->vtab;

    redis_vtbl_command_init_arg(&cmd, "ZRANGE");
    redis_vtbl_command_arg_fmt(&cmd, "%s.index.rowid", vtab->key_base);
    redis_vtbl_command_arg(&cmd, "0");
    redis_vtbl_command_arg(&cmd, "-1");
    reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
    if(!reply) return SQLITE_ERROR;

    err = redis_reply_numeric_array(&cursor->rows, reply);
    freeReplyObject(reply);

    if(err) return SQLITE_ERROR;
    return SQLITE_OK;
}
static int redis_vtbl_cursor_filter_rowid(redis_vtbl_cursor *cursor, sqlite3_int64 row_id, int idxNum) {
    int err;
    redis_vtbl_vtab *vtab;
    redis_vtbl_command cmd;
    redisReply *reply;

    vtab = cursor->vtab;

    switch(idxNum) {
        case CURSOR_INDEX_ROWID_EQ:
            redis_vtbl_command_init_arg(&cmd, "EXISTS");
            redis_vtbl_command_arg_fmt(&cmd, "%s:%lld", vtab->key_base, row_id);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_ROWID_GT:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index.rowid", vtab->key_base);
            redis_vtbl_command_arg_fmt(&cmd, "(%lld", row_id);
            redis_vtbl_command_arg(&cmd, "+inf");
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_ROWID_LT:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index.rowid", vtab->key_base);
            redis_vtbl_command_arg(&cmd, "-inf");
            redis_vtbl_command_arg_fmt(&cmd, "(%lld", row_id);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_ROWID_GE:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index.rowid", vtab->key_base);
            redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
            redis_vtbl_command_arg(&cmd, "+inf");
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_ROWID_LE:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index.rowid", vtab->key_base);
            redis_vtbl_command_arg(&cmd, "-inf");
            redis_vtbl_command_arg_fmt(&cmd, "%lld", row_id);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
    }

    if(!reply) return SQLITE_ERROR;

    if(idxNum == CURSOR_INDEX_ROWID_EQ) {
        if(reply->type == REDIS_REPLY_INTEGER) {
            if(reply->integer) vector_push(&cursor->rows, &row_id);

        } else {
            freeReplyObject(reply);
            return SQLITE_ERROR;
        }
        freeReplyObject(reply);

    } else {
        err = redis_reply_numeric_array(&cursor->rows, reply);
        freeReplyObject(reply);
        if(err) return SQLITE_ERROR;
    }

    return SQLITE_OK;
}
static int redis_vtbl_cursor_filter_index_text(redis_vtbl_cursor *cursor, int idxNum, const char *idxStr, const char *value);
static int redis_vtbl_cursor_filter_index_integer(redis_vtbl_cursor *cursor, int idxNum, const char *idxStr, sqlite3_int64 value);
static int redis_vtbl_cursor_filter_index_float(redis_vtbl_cursor *cursor, int idxNum, const char *idxStr, double value);
static int redis_vtbl_cursor_filter_index(redis_vtbl_cursor *cursor, int idxNum, const char *idxStr, sqlite3_value *value) {
    int err;
    redis_vtbl_vtab *vtab;
    redis_vtbl_column_spec *cspec;

    vtab = cursor->vtab;

    cspec = vector_find(&vtab->columns, idxStr, (int (*)(const void *, const void *))redis_vtbl_column_spec_name_cmp);
    if(!cspec) return SQLITE_ERROR;

    err = SQLITE_ERROR;

    switch(cspec->data_type) {
        case SQLITE_INTEGER: {
            sqlite3_int64 ivalue;

            ivalue = sqlite3_value_int64(value);
            err = redis_vtbl_cursor_filter_index_integer(cursor, idxNum, idxStr, ivalue);
            break;
        }

        case SQLITE_TEXT: {
            const char *cvalue;

            cvalue = (const char*)sqlite3_value_text(value);
            err = redis_vtbl_cursor_filter_index_text(cursor, idxNum, idxStr, cvalue);
            break;
        }

        case SQLITE_FLOAT: {
            double fvalue;

            fvalue = sqlite3_value_double(value);
            err = redis_vtbl_cursor_filter_index_float(cursor, idxNum, idxStr, fvalue);
            break;
        }
    }

    return err;
}
static int redis_vtbl_cursor_filter_index_text(redis_vtbl_cursor *cursor, int idxNum, const char *idxStr, const char *value) {
    int err;
    redis_vtbl_vtab *vtab;
    redis_vtbl_command cmd;
    redisReply *reply;
    long long rank;

    vtab = cursor->vtab;

    redis_vtbl_command_init_arg(&cmd, "ZRANK");
    redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
    redis_vtbl_command_arg_fmt(&cmd, "%s", value);
    reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
    if(reply->type == REDIS_REPLY_INTEGER) {
        rank = reply->integer;

    } else if(reply->type == REDIS_REPLY_NIL) {
        rank = -1;

    } else {
        freeReplyObject(reply);
        return SQLITE_ERROR;
    }
    freeReplyObject(reply);

    if(idxNum == CURSOR_INDEX_NAMED_EQ) {
        if(rank == -1) return SQLITE_OK;    /* rank indicates item does not exist. */
        redis_vtbl_command_init_arg(&cmd, "SMEMBERS");
        redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s:%s", vtab->key_base, idxStr, value);
        reply = redis_vtbl_connection_command(&vtab->conn, &cmd);

    } else if(rank == -1) {
        /* todo fallback to a scan if the value does not exist in the index.
         * This can be improved by determining the approximate rank of the item
         * and then using zrange more efficiently. */
        redis_vtbl_command_init_arg(&cmd, "ZRANGE");
        redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
        redis_vtbl_command_arg(&cmd, "0");
        redis_vtbl_command_arg(&cmd, "-1");
        reply = redis_vtbl_connection_command(&vtab->conn, &cmd);

    } else {
        switch(idxNum) {
            case CURSOR_INDEX_NAMED_GT:
                redis_vtbl_command_init_arg(&cmd, "ZRANGE");
                redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
                redis_vtbl_command_arg_fmt(&cmd, "%lld", rank);
                redis_vtbl_command_arg(&cmd, "-1");
                reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
                break;
            case CURSOR_INDEX_NAMED_LT:
                redis_vtbl_command_init_arg(&cmd, "ZRANGE");
                redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
                redis_vtbl_command_arg(&cmd, "0");
                redis_vtbl_command_arg_fmt(&cmd, "%lld", rank);
                reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
                break;
            case CURSOR_INDEX_NAMED_GE:
                redis_vtbl_command_init_arg(&cmd, "ZRANGE");
                redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
                redis_vtbl_command_arg_fmt(&cmd, "%lld", rank);
                redis_vtbl_command_arg(&cmd, "-1");
                reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
                break;
            case CURSOR_INDEX_NAMED_LE:
                redis_vtbl_command_init_arg(&cmd, "ZRANGE");
                redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
                redis_vtbl_command_arg(&cmd, "0");
                redis_vtbl_command_arg_fmt(&cmd, "%lld", rank);
                reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
                break;
        }
    }

    if(!reply) return SQLITE_ERROR;

    err = redis_reply_numeric_array(&cursor->rows, reply);
    freeReplyObject(reply);
    if(err) return SQLITE_ERROR;

    return SQLITE_OK;
}
static int redis_vtbl_cursor_filter_index_integer(redis_vtbl_cursor *cursor, int idxNum, const char *idxStr, sqlite3_int64 value) {
    int err;
    redis_vtbl_vtab *vtab;
    redis_vtbl_command cmd;
    redisReply *reply;

    vtab = cursor->vtab;

    switch(idxNum) {
        case CURSOR_INDEX_NAMED_EQ:
            redis_vtbl_command_init_arg(&cmd, "SMEMBERS");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s:%lld", vtab->key_base, idxStr, value);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;

        case CURSOR_INDEX_NAMED_GT:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
            redis_vtbl_command_arg_fmt(&cmd, "(%lld", value);
            redis_vtbl_command_arg(&cmd, "+inf");
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_NAMED_LT:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
            redis_vtbl_command_arg(&cmd, "-inf");
            redis_vtbl_command_arg_fmt(&cmd, "(%lld", value);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_NAMED_GE:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
            redis_vtbl_command_arg_fmt(&cmd, "%lld", value);
            redis_vtbl_command_arg(&cmd, "+inf");
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_NAMED_LE:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
            redis_vtbl_command_arg(&cmd, "-inf");
            redis_vtbl_command_arg_fmt(&cmd, "%lld", value);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
    }

    if(!reply) return SQLITE_ERROR;

    err = redis_reply_numeric_array(&cursor->rows, reply);
    freeReplyObject(reply);
    if(err) return SQLITE_ERROR;

    return SQLITE_OK;
}
static int redis_vtbl_cursor_filter_index_float(redis_vtbl_cursor *cursor, int idxNum, const char *idxStr, double value) {
    int err;
    redis_vtbl_vtab *vtab;
    redis_vtbl_command cmd;
    redisReply *reply;

    vtab = cursor->vtab;

    switch(idxNum) {
        case CURSOR_INDEX_NAMED_EQ:
            redis_vtbl_command_init_arg(&cmd, "SMEMBERS");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s:%f", vtab->key_base, idxStr, value);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;

        case CURSOR_INDEX_NAMED_GT:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
            redis_vtbl_command_arg_fmt(&cmd, "(%f", value);
            redis_vtbl_command_arg(&cmd, "+inf");
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_NAMED_LT:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
            redis_vtbl_command_arg(&cmd, "-inf");
            redis_vtbl_command_arg_fmt(&cmd, "(%f", value);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_NAMED_GE:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
            redis_vtbl_command_arg_fmt(&cmd, "%f", value);
            redis_vtbl_command_arg(&cmd, "+inf");
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
        case CURSOR_INDEX_NAMED_LE:
            redis_vtbl_command_init_arg(&cmd, "ZRANGEBYSCORE");
            redis_vtbl_command_arg_fmt(&cmd, "%s.index:%s", vtab->key_base, idxStr);
            redis_vtbl_command_arg(&cmd, "-inf");
            redis_vtbl_command_arg_fmt(&cmd, "%f", value);
            reply = redis_vtbl_connection_command(&vtab->conn, &cmd);
            break;
    }

    if(!reply) return SQLITE_ERROR;

    err = redis_reply_numeric_array(&cursor->rows, reply);
    freeReplyObject(reply);
    if(err) return SQLITE_ERROR;

    return SQLITE_OK;
}

static int redis_vtbl_cursor_next(sqlite3_vtab_cursor *pCursor) {
    redis_vtbl_cursor *cursor;

    cursor = (redis_vtbl_cursor*)pCursor;
    ++cursor->current_row;
    cursor->column_data_valid = 0;

    return SQLITE_OK;
}

static int redis_vtbl_cursor_eof(sqlite3_vtab_cursor *pCursor) {
    redis_vtbl_cursor *cursor;
    cursor = (redis_vtbl_cursor*)pCursor;

    return cursor->current_row == vector_end(&cursor->rows);
}

static int redis_vtbl_cursor_column(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int N) {
    redis_vtbl_cursor *cursor;
    redis_vtbl_vtab *vtab;
    redis_vtbl_column_spec *cspec;
    const char *value;
    char *end;
    sqlite3_int64 i;
    double f;

    cursor = (redis_vtbl_cursor*)pCursor;
    vtab = cursor->vtab;

    cspec = vector_get(&vtab->columns, N);
    if(!cspec) return SQLITE_ERROR;

    if(!cursor->column_data_valid) {
        redis_vtbl_cursor_get(cursor);
    }

    value = list_get(&cursor->column_data, N);
    if(!value) {
        sqlite3_result_null(ctx);
        return SQLITE_OK;
    }

    switch(cspec->data_type) {
        case SQLITE_INTEGER:
            errno = 0;
            i = strtoll(value, &end, 10);
            if(errno || *end)      /* out-of-range | rubbish characters */
                break;
            sqlite3_result_int64(ctx, i);
            break;

        case SQLITE_TEXT:
            sqlite3_result_text(ctx, value, -1, SQLITE_TRANSIENT);
            break;

        case SQLITE_FLOAT:
            errno = 0;
            f = strtod(value, &end);
            if(errno || *end)      /* out-of-range | rubbish characters */
                break;
            sqlite3_result_double(ctx, f);
            break;
    }

    return SQLITE_OK;
}

static int redis_vtbl_cursor_rowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) {
    redis_vtbl_cursor *cursor;
    cursor = (redis_vtbl_cursor*)pCursor;

    if(!cursor->current_row) return SQLITE_ERROR;

    *pRowid = *cursor->current_row;
    return SQLITE_OK;
}

/*-----------------------------------------------------------------------------
 * sqlite3 extension machinery
 *----------------------------------------------------------------------------*/

static const sqlite3_module redis_vtbl_Module = {
    .iVersion     = 1,

    .xCreate       = redis_vtbl_create,
    .xConnect      = redis_vtbl_connect,
    .xBestIndex    = redis_vtbl_bestindex,
    .xFindFunction = redis_vtbl_findfunction,
    .xUpdate       = redis_vtbl_update,
    .xDisconnect   = redis_vtbl_disconnect,
    .xDestroy      = redis_vtbl_destroy,

    .xOpen         = redis_vtbl_cursor_open,
    .xClose        = redis_vtbl_cursor_close,
    .xFilter       = redis_vtbl_cursor_filter,
    .xNext         = redis_vtbl_cursor_next,
    .xEof          = redis_vtbl_cursor_eof,
    .xColumn       = redis_vtbl_cursor_column,
    .xRowid        = redis_vtbl_cursor_rowid
};

int sqlite3_redisvtbl_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) {
    SQLITE_EXTENSION_INIT2(pApi);
    int rc;
    (void)pzErrMsg; /* unused */

    rc = sqlite3_create_module(db, "redis", &redis_vtbl_Module, 0);
    if(rc != SQLITE_OK)
        return rc;

    rc = sqlite3_overload_function(db, "redis_create_index", 1);
    return rc;
}

#if !SQLITE_CORE
int sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) {
    return sqlite3_redisvtbl_init(db, pzErrMsg, pApi);
}
#endif


text:
package com.adaptris.core;

import static com.adaptris.annotation.AnnotationConstants.BEAN_INFO_PROPERTIES_FILE;
import static com.adaptris.annotation.AnnotationConstants.CDATA_PROPERTIES_FILE;
import static com.adaptris.annotation.AnnotationConstants.XSTREAM_ALIAS_PROPERTIES_FILE;
import static com.adaptris.annotation.AnnotationConstants.XSTREAM_IMPLICIT_PROPERTIES_FILE;
import static com.adaptris.core.marshaller.xstream.XStreamUtils.getClasses;
import static com.adaptris.core.marshaller.xstream.XStreamUtils.readResource;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adaptris.core.marshaller.xstream.LowerCaseHyphenatedMapper;
import com.adaptris.core.marshaller.xstream.PrettyStaxDriver;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.javabean.JavaBeanConverter;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;
import com.thoughtworks.xstream.mapper.MapperWrapper;

/**
 * XStream Marshaller Specific Factory class.
 *
 * @author bklair
 */
public class AdapterXStreamMarshallerFactory extends AdapterMarshallerFactory {

  private transient static Logger log = LoggerFactory.getLogger(AdapterXStreamMarshallerFactory.class);

  /**
   * Whether not extended debugging is emitted; defaults to false unless explicitly set via either the system property
   * {@code interlok.xstream.debug} or {@code adp.xtream.debug}.
   *
   */
  public static final transient boolean XSTREAM_DBG = Boolean.getBoolean("adp.xstream.debug")
      || Boolean.getBoolean("interlok.xstream.debug");
  /**
   * Whether or not we force XStream to use the JVMs internal Stax implementation; this defaults to
   * 'true' unless explicitly set via the system property {@code interlok.xstream.jdk.stax}.
   *
   */
  public static final transient boolean XSTREAM_JDK_STAX_ONLY =
      BooleanUtils.toBoolean(System.getProperty("interlok.xstream.jdk.stax", "true"));

  private enum XStreamTypes {
    XML {
      @Override
      XStream createXStreamInstance() {
        return new MyExtremeMarshaller(new PureJavaReflectionProvider(), new PrettyStaxDriver(cdataFields, XSTREAM_JDK_STAX_ONLY));
      }

      @Override
      AdaptrisMarshaller createMarshaller() throws CoreException {
        return new XStreamMarshaller();
      }
    },
    JSON {
      @Override
      XStream createXStreamInstance() {
        return new MyExtremeMarshaller(new PureJavaReflectionProvider(), new JettisonMappedXmlDriver());
      }

      @Override
      AdaptrisMarshaller createMarshaller() throws CoreException {
        return new XStreamJsonMarshaller();
      }

    };
    abstract XStream createXStreamInstance();

    abstract AdaptrisMarshaller createMarshaller() throws CoreException;
  }

  private static transient Collection<String> cdataFields = new HashSet<>();
  private static transient Collection<String> xstreamImplicits = new HashSet<>();
  private static transient Collection<Class<?>> xstreamAnnotatedClasses = new ArrayList<>();
  private static transient Collection<Class<?>> beanInfoAnnotatedClasses = new ArrayList<>();

  // TODO we should have a AdapterMarshallerFactory singleton located somewhere else instead of here
  // But for the time being just use this class as a singleton until a better solution is found
  protected static AdapterXStreamMarshallerFactory instance;

  private AdapterXStreamMarshallerFactory() {
    super();
    // Read in and store all the properties associated with xstream
    readXStreamConfigProperties();
  }

  // Singleton accessor method
  public static synchronized AdapterXStreamMarshallerFactory getInstance() {
    if (instance == null) {
      instance = new AdapterXStreamMarshallerFactory();
    }
    return instance;
  }

  // Just for tests to reset state.
  static synchronized void reset() {
    instance = null;
  }

  @Override
  public AdaptrisMarshaller createMarshaller() throws CoreException {
    return createMarshaller(MarshallingOutput.XML);
  }

  @Override
  public AdaptrisMarshaller createMarshaller(MarshallingOutput outputType) throws CoreException {
    // TODO The following Marshaller classes are problematic because they are
    // like Singletons and so have a callback to this factory class to complete
    // instantiation, we should ideally fully instantiate them here and now.
    return XStreamTypes.valueOf(outputType.name()).createMarshaller();
  }

  /**
   * Reads in and stores all of the xstream configuration items from various resources
   */
  protected static void readXStreamConfigProperties() {
    // Process annotations
    xstreamAnnotatedClasses = new PropertyClassListProcessor().process(XSTREAM_ALIAS_PROPERTIES_FILE, new ArrayList<Class<?>>());
    // Process classes with special field handling
    cdataFields = new PropertyStringListProcessor().process(CDATA_PROPERTIES_FILE, new HashSet<String>());
    // Process classes with special hierarchy
    beanInfoAnnotatedClasses = new PropertyClassListProcessor().process(BEAN_INFO_PROPERTIES_FILE, new ArrayList<Class<?>>());
    logBeanInfoWarnings();
    // Process classes with implicit collection
    xstreamImplicits = new PropertyStringListProcessor().process(XSTREAM_IMPLICIT_PROPERTIES_FILE, new HashSet<String>());
  }

  private static void logBeanInfoWarnings() {
    if (beanInfoAnnotatedClasses.size() > 0) {
      StringBuffer sb = new StringBuffer();
      beanInfoAnnotatedClasses.forEach(c -> {
        sb.append(c.getName());
      });
      log.warn("Found use of deprecated @GenerateBeanInfo : [{}]", sb.toString());
    }
  }

  /**
   * Creates the XStream object instance
   *
   * @param outputMode - The type of output to generate ie JSON/XML
   * @return - newly created instance
   */
  protected XStream createXStreamInstance(MarshallingOutput outputMode) {
    return XStreamTypes.valueOf(outputMode.name()).createXStreamInstance();
  }

  /**
   * Public method that returns a configure XStream object instance read for use
   *
   * @return Xstream configured for XML output
   */
  public XStream createXStream() {
    return createXStream(MarshallingOutput.XML);
  }

  /**
   * Public method that returns a configure XStream object instance read for use configured for the given form of output.
   *
   * @param outputType - Type of output to configure XStream for ie JSON or XML
   * @return Xstream configured for the specified output type
   */
  public XStream createXStream(MarshallingOutput outputType) {
    XStream xStreamInstance = createXStreamInstance(outputType);
    return configureXStream(xStreamInstance, outputType);
  }

  /**
   * Configures the given XStream object.<br />
   * It is expected that the Factory has been initialised and that all of the configuration settings have been processed and stored
   * already.
   *
   * @param xstream - XStream instance to configure
   * @param outputMode - The output type to configure XStream for
   */
  @SuppressWarnings("rawtypes")
  protected XStream configureXStream(XStream xstream, MarshallingOutput outputMode) {
    XStream.setupDefaultSecurity(xstream);
    // CVE-2017-7957
    // CVE-2020-26217 - we should be on xstream 1.14 but in case people aren't...
    xstream.denyTypes(new String[] {"javax.imageio.ImageIO$ContainsFilter"});
    xstream.denyTypes(new Class[] {java.lang.ProcessBuilder.class, java.beans.EventHandler.class,
        void.class, Void.class});
    Class[] annotationClassesArray = xstreamAnnotatedClasses.toArray(new Class[0]);
    xstream.allowTypes(annotationClassesArray);
    xstream.allowTypesByWildcard(new String[]
    {
        "com.adaptris.**"
    });
    // Configure Annotations
    xstream.processAnnotations(annotationClassesArray);

    // Configure Bean info
    for (Iterator<?> iterator = beanInfoAnnotatedClasses.iterator(); iterator.hasNext();) {
      Class<?> beanClass = (Class<?>) iterator.next();
      JavaBeanConverter converter = null;
      converter = new JavaBeanConverter(xstream.getMapper(), beanClass);
      xstream.registerConverter(converter);
    }

    // Now set any additional items
    if (outputMode == MarshallingOutput.JSON) {
      xstream.setMode(XStream.NO_REFERENCES);
    }
    else {
      xstream.setMode(XStream.XPATH_ABSOLUTE_REFERENCES);
    }

    xstream.addDefaultImplementation(ArrayList.class, Collection.class);
    xstream.addDefaultImplementation(LinkedHashSet.class, Set.class);

    return xstream;
  }

  // ------------------------------------------------------------------------
  // Inner Classes
  // ------------------------------------------------------------------------

  /**
   * Helper class that processes a given list of resources. Each resource is read and processed. This allows subclasses to focus on
   * processes the resource file contents in a specific way without the boilerplate.
   *
   * @author bklair
   *
   * @param <T> - Type of data contained by resource file - just text data or classes
   */
  static abstract class PropertyListProcessor<T> {

    public Collection<T> process(String resourceName, Collection<T> propertyCollection) {
      try {
        Enumeration<URL> mappings = AdapterXStreamMarshallerFactory.class.getClassLoader().getResources(resourceName);
        while (mappings.hasMoreElements()) {
          processQuietly(propertyCollection, mappings.nextElement());
        }
      }
      catch (IOException ignore) {
        log.warn("Error encountered reading resource: {}, continuing processing", resourceName);
      }
      return propertyCollection;
    }

    protected void processQuietly(Collection<T> propertyCollection, URL url) {
      try {
        processEachUrl(propertyCollection, url.openStream());
      }
      catch (IOException ioe) {
      }
    }
    /**
     * Processes the current resource file and adds the processed contents to the given Collection.
     *
     * @param propertyCollection - Target collection to populate with the resource contents
     * @param in - Opened Input Stream to the resource file
     * @throws IOException - If problem encountered
     */
    protected abstract void processEachUrl(Collection<T> propertyCollection, InputStream in) throws IOException;

  }

  /**
   * Processes the resources as a List of text data
   *
   * @author bklair
   */
  static class PropertyStringListProcessor extends PropertyListProcessor<String> {

    @Override
    protected void processEachUrl(Collection<String> propertyCollection, InputStream in) throws IOException {
      propertyCollection.addAll(readResource(in));
    }
  }

  /**
   * Processes the resources as a List of defined Classes
   *
   * @author bklair
   */
  static class PropertyClassListProcessor extends PropertyListProcessor<Class<?>> {

    @Override
    protected void processEachUrl(Collection<Class<?>> propertyCollection, InputStream in) throws IOException {
      propertyCollection.addAll(getClasses(in));
    }
  }

  // XStream subclass that adds a custom mapper to handle ImplicitCollection names
  static class MyExtremeMarshaller extends XStream {

    public MyExtremeMarshaller(ReflectionProvider rd, HierarchicalStreamDriver hsd) {
      super(rd, hsd);
    }

    @Override
    protected MapperWrapper wrapMapper(MapperWrapper next) {
      return new LowerCaseHyphenatedMapper(next, xstreamImplicits);
    }

  }
}


text:
package com.jaamsim.input;

import java.util.ArrayList;
import java.util.Collections;

import com.jaamsim.basicsim.Entity;
import com.jaamsim.basicsim.JaamSimModel;

public class EntityListInput<T extends Entity> extends ListInput<ArrayList<T>> {
	private Class<T> entClass;
	private boolean unique; // flag to determine if list must be unique or not
	private boolean even;  // flag to determine if there must be an even number of entries
	private boolean includeSubclasses;  // flag to determine if subclasses are valid
	private boolean includeSelf; // flag to determine whether to include the calling entity in the entityList
	private ArrayList<Class<? extends Entity>> validClasses; // list of valid classes (including subclasses).  if empty, then all classes are valid
	private ArrayList<Class<? extends Entity>> invalidClasses; // list of invalid classes (including subclasses).

	public EntityListInput(Class<T> aClass, String key, String cat, ArrayList<T> def) {
		super(key, cat, def);
		entClass = aClass;
		unique = true;
		even = false;
		includeSubclasses = true;
		includeSelf = true;
		validClasses = new ArrayList<>();
		invalidClasses = new ArrayList<>();
	}

	@Override
	public void parse(Entity thisEnt, KeywordIndex kw)
	throws InputErrorException {

		// If adding to the list
		if( kw.getArg( 0 ).equals( "++" ) ) {
			KeywordIndex subKw = new KeywordIndex(kw, 1);

			ArrayList<T> newValue;
			if( value == null )
				newValue = new ArrayList<>();
			else
				newValue = new ArrayList<>( value );

			Input.assertCountRange(subKw, 0, maxCount - newValue.size());
			if( even ) {
				if ((kw.numArgs() % 2) == 0)
					throw new InputErrorException(INP_ERR_EVENCOUNT, kw.argString());
			}

			ArrayList<T> addedValues = Input.parseEntityList(thisEnt.getJaamSimModel(), subKw, entClass, unique);
			for( T val : addedValues ) {
				if( unique && newValue.contains( val ) )
					throw new InputErrorException(INP_ERR_NOTUNIQUE, val.getName());
				newValue.add( val );
			}
			value = newValue;
			return;
		}

		// If removing from the list
		if( kw.getArg( 0 ).equals( "--" ) ) {
			KeywordIndex subKw = new KeywordIndex(kw, 1);

			Input.assertCountRange(subKw, 0, value.size() - minCount );
			if( even ) {
				if ((kw.numArgs() % 2) == 0)
					throw new InputErrorException(INP_ERR_EVENCOUNT, kw.argString());
			}

			ArrayList<T> newValue = new ArrayList<>( value );
			ArrayList<T> removedValues = Input.parseEntityList(thisEnt.getJaamSimModel(), subKw, entClass, unique);
			for( T val : removedValues ) {
				if( ! newValue.contains( val ) )
					InputAgent.logWarning(thisEnt.getJaamSimModel(),
							"Could not remove " + val + " from " + this.getKeyword() );
				newValue.remove( val );
			}
			value = newValue;
			return;
		}

		// Otherwise, just set the list normally
		Input.assertCountRange(kw, minCount, maxCount);
		if( even )
			Input.assertCountEven(kw);

		value = Input.parseEntityList(thisEnt.getJaamSimModel(), kw, entClass, unique);
	}

	@Override
	public String getValidInputDesc() {
		return Input.VALID_ENTITY_LIST;
	}

	@Override
	public int getListSize() {
		if (value == null)
			return 0;
		else
			return value.size();
	}

	public void setUnique(boolean unique) {
		this.unique = unique;
	}
	public void setEven(boolean bool) {
		this.even = bool;
	}
	public void setIncludeSubclasses(boolean bool) {
		this.includeSubclasses = bool;
	}

	public void setIncludeSelf(boolean bool) {
		this.includeSelf = bool;
	}

	@Override
	public ArrayList<String> getValidOptions(Entity ent) {
		ArrayList<String> list = new ArrayList<>();
		for(T each: ent.getJaamSimModel().getClonesOfIterator(entClass) ) {
			if(!each.isRegistered())
				continue;

			if( ! isValidClass( each ))
				continue;

			if(! includeSubclasses) {
				if( each.getClass() != entClass ) {
					continue;
				}
			}

			if(each.getEditableInputs().contains( this ) && ! includeSelf ) {
				continue;
			}

			list.add(each.getName());
		}

		// Include the default values
		if (getDefaultValue() != null) {
			for (Entity def : getDefaultValue()) {
				String name = def.getName();
				if (list.contains(name))
					continue;
				list.add(name);
			}
		}

		Collections.sort(list, Input.uiSortOrder);
		return list;
	}

	@Override
	public void getValueTokens(ArrayList<String> toks) {
		if (value == null || isDefault())
			return;

		for (int i = 0; i < value.size(); i++)
			toks.add(value.get(i).getName());
	}

	@Override
	public void setTokens(KeywordIndex kw) {
		isDef = false;

		String[] args = kw.getArgArray();
		if (args.length > 0) {

			// Consider the following input case:
			// Object1 Keyword1 { ++ Entity1 ...
			if (args[0].equals( "++" )) {
				this.addTokens(args);
				return;
			}

			// Consider the following input case:
			// Object1 Keyword1 { -- Entity1 ...
			if (args[0].equals( "--" )) {
				if (this.removeTokens(args))
					return;
			}
		}

		valueTokens = args;
	}

	@Override
	public String getDefaultString(JaamSimModel simModel) {
		if (defValue == null || defValue.isEmpty())
			return "";

		StringBuilder tmp = new StringBuilder();
		tmp.append(defValue.get(0).getName());
		for (int i = 1; i < defValue.size(); i++) {
			tmp.append(SEPARATOR);
			tmp.append(defValue.get(i).getName());
		}
		return tmp.toString();
	}

	public boolean isValidClass( Entity ent ) {

		for (Class<? extends Entity> c : invalidClasses) {
			if (c.isAssignableFrom( ent.getClass() )) {
				return false;
			}
		}

		if( validClasses.size() == 0 )
			return true;

		for( Class<? extends Entity> c : validClasses ) {
			if( c.isAssignableFrom( ent.getClass() ) ) {
				return true;
			}
		}

		return false;
	}

	public void addValidClass(Class<? extends Entity> aClass ) {
		invalidClasses.remove(aClass);
		validClasses.add(aClass);
	}

	public void addInvalidClass(Class<? extends Entity> aClass ) {
		validClasses.remove(aClass);
		invalidClasses.add(aClass);
	}

	public void clearValidClasses() {
		validClasses.clear();
		invalidClasses.clear();
	}

	@Override
	public boolean removeReferences(Entity ent) {
		if (value == null)
			return false;
		boolean ret = value.removeAll(Collections.singleton(ent));
		return ret;
	}

	@Override
	public void appendEntityReferences(ArrayList<Entity> list) {
		if (value == null)
			return;
		for (Entity ent : value) {
			if (ent == null || list.contains(ent))
				continue;
			list.add(ent);
		}
	}

}