Fast searching in a ByteArray

Adobe AIRAdobe Flex

For some reason there is no method to search for a specific text in a ByteArray. At least i could not find one. I tried a very straightforward search by simply scanning from every position. Although it works, it is very slow with bigger ByteArrays.

A quick look on the internet resulted in an algorithm that suited my needs: the Boyer-Moore-Horspool algorithm. Unfortunately there was no ActionScript alternative available for it yet. So i decided to port it myself and with great result, because it greatly improved the performance of the searches for my use case.

package nl.vanhulzenonline.utils
{
	import flash.utils.ByteArray;

	public class ByteArrayUtils
	{
		public static function getIndexOf(text:String, data:ByteArray, start:int = 0, end:int = -1):int
		{
			var pattern:ByteArray = new ByteArray();
			pattern.writeUTFBytes(text);
			pattern.position = 0;

			if (end == -1)
				end = data.length - 1;

			var i:int;
			var badCharSkip:Array = new Array();

			// initialize the table to default value
			// when a character is encountered that does not occur
			// in the pattern, we can safely skip ahead for the whole
			// length of the pattern.
			for (i = 0; i <= 255; i++)
				badCharSkip[i] = pattern.length;

			// then populate it with the analysis of the pattern
			var endOfPattern:int = pattern.length - 1;
			for (i = 0; i < endOfPattern; i = i + 1)
				badCharSkip[pattern.readUnsignedByte()] = endOfPattern - i;

			// do the matching

			// search the data, while the pattern can still be within it.
			var dataPart:int;
			var endOfData:int = end;
			var dataPosition:int = start;
			while (endOfData >= endOfPattern)
			{
				// scan from the end of the pattern
				i = endOfPattern;
				while(true)
				{
					data.position = dataPosition + i;
					pattern.position = i;
					if (data.readUnsignedByte() == pattern.readUnsignedByte())
					{
						// if the first byte matches, we've found it.
						if (i == 0)
							return dataPosition;
						i--;
					}
					else
						break;
				}

				// otherwise, we need to skip some bytes and start again.
				// note that here we are getting the skip value based on
				// the last byte of pattern, no matter where we didn't
				// match. so if pattern is: "abcd" then we are skipping
				// based on 'd' and that value will be 4, and for "abcdd"
				// we again skip on 'd' but the value will be only 1.
				// the alternative of pretending that the mismatched
				// character was the last character is slower in the normal
 				// case (eg. finding "abcd" in "...azcd..." gives 4 by
				// using 'd' but only 4-2==2 using 'z'.
				data.position = dataPosition + endOfPattern;
				dataPart = data.readUnsignedByte();
				endOfData -= badCharSkip[dataPart];
				dataPosition += badCharSkip[dataPart];
			}
			return -1;
		}
	}
}

XML configuration of Log4j for the ServletHelper

Java

The original ServletHelper was introduced in Separate a J2EE application from data/configuration. In short it is a generic way to make a J2EE application configurable. Just use the ServletHelper class to have easy log and application configuration and have the application data outside your deployed J2EE application. So redeploying your .war file won’t destroy your data or configuration.

The previous ServletHelper only supported a Log4j .properties file. This new version of the ServletHelper supports XML configuration of Log4j too. If the data/configuration directory contains a file name [servletname]-log4j.xml, it is used to configure Log4j for the J2EE application using the new version of the ServletHelper.

Check the original article for more information on how to use the ServletHelper.

package nl.vanhulzenonline.servlethelper;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

import javax.servlet.ServletContext;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.xml.DOMConfigurator;

public class ServletHelper
{
	public static Logger logger = null;
	public static Properties properties = null;

	private static ServletContext _context = null;

	private static String getServletName()
	{
		return _context.getContextPath().substring(1);
	}

	private static String getServletHomePathVariableName()
	{
		return getServletName().toUpperCase() + "_HOME";
	}

	private static String getServletHomePath()
	{
		String path = System.getenv(getServletHomePathVariableName());
		return (path == null) ? "" : path;
	}

	private static String getPropertiesFilePrefix()
	{
		return getServletHomePath() + File.separatorChar + getServletName().toLowerCase();
	}

	private static String getLog4jPropertiesFileName()
	{
		return getPropertiesFilePrefix() + "-log4j.properties";
	}

	private static String getLog4jXMLFileName()
	{
		return getPropertiesFilePrefix() + "-log4j.xml";
	}

	private static String getServletPropertiesFileName()
	{
		return getPropertiesFilePrefix() + ".properties";
	}

	private static Properties loadProperties(File file)
	{
		try
		{
			Properties properties = new Properties();
			FileInputStream stream = new FileInputStream(file);
			properties.load(stream);
			stream.close();
			return properties;
		}
		catch (Exception e)
		{
			logger.error("Failure reading " + getServletPropertiesFileName() + ". Unable to execute " + getServletName() + ".");
			return null;
		}
    }

	private static void initializeLog4j()
	{
		logger = Logger.getLogger(getServletName());

		// we have to initialize log4j first

		File file = new File(getLog4jXMLFileName());
		if (file.exists())
		{
			DOMConfigurator.configure(getLog4jXMLFileName()); // configure log4j
			logger.info("Using " + getLog4jXMLFileName() + " for log4j configuration.");
		}
		else
		{
			file = new File(getLog4jPropertiesFileName());
			if (file.exists())
			{
				PropertyConfigurator.configure(getLog4jPropertiesFileName()); // configure log4j
				logger.info("Using " + getLog4jPropertiesFileName() + " for log4j configuration.");
			}
			else
			{
				BasicConfigurator.configure(); // configure log4j
				logger.info("Using default log4j configuration.");
			}
		}
	}

	private static Boolean initializeServlet()
	{
		// checking the home path
		String homePath = getServletHomePath();
		if (homePath == "")
		{
			logger.error(getServletHomePathVariableName() + " is not set. Unable to execute " + getServletName() + ".");
			return false;
		}

		File homeDir = new File(homePath);
		if (!homeDir.exists())
		{
			logger.error(getServletHomePath() + " does not exist. Unable to execute " + getServletName() + ".");
			return false;
		}
		if(!homeDir.isDirectory())
		{
			logger.error(getServletHomePath() + " is not a directory. Unable to execute " + getServletName() + ".");
			return false;
		}

		// checking the properties file of the webapp and load the properties
		File propertiesFile = new File(getServletPropertiesFileName());
		if (propertiesFile.exists())
		{
			properties = loadProperties(propertiesFile);
			if (properties == null)
				return false;
		}
		else
		{
			logger.error(getServletPropertiesFileName() + " not found. Unable to execute " + getServletName() + ".");
			return false;
		}

		logger.info(getServletName() + " initialized.");

		return true;
	}

	public static Boolean initialize(ServletContext context)
	{
		if (_context != null)
			return false;

		_context = context;

		initializeLog4j();

		return initializeServlet();
	}
}

Simple shell for Ant on Linux

Ant

As promised in Simple shell for Ant on Windows i would provide a Linux version of the simple Ant shell. So here is a version based on the Bash shell. I’ve written it using Cygwin, but using it on Linux should not be much of a problem. Of course this Linux version has the same functionality as the Windows version.

#!/bin/bash

project=build.xml

ANT_HOME="/cygdrive/c/programs/apache-ant-1.7.1"

if [ ! -d "$ANT_HOME" ]
then
	echo Error: ANT_HOME does not have a valid value
	read
	exit -1
fi

function help
{
	$ANT_HOME/bin/ant.bat -p -f $project
	echo ""
	echo "Other commands:"
	echo ""
	echo " ?      Show this help."
	echo " help   Show this help."
	echo " q      Stop this shell."
	echo " quit   Stop this shell."
	echo " exit   Stop this shell."
	echo ""
}

# clear the terminal
echo -e "\0033\0143"

# set the terminal title 
echo -en "\033]2;env - A sample Ant shell\007"

echo "For more information/help, type '?' or 'help'."

while true
do
	read -p " $ " command
	
	if [ "$command" == "?" ] || [ "$command" == "help" ]
	then
		help
	else if [ "$command" == "q" ] || [ "$command" == "quit" ] || [ "$command" == "exit" ]
	then
		exit 1
	else
		$ANT_HOME/bin/ant.bat "$command" -f $project -propertyfile env.properties 
	fi
	fi
done

← Previous PageNext Page →