Skip to content Skip to sidebar Skip to footer

Is There A Simple Method To Match An Array Of Bytes In An Byte Array?

I'm trying to check for a sequence of bytes in a byte array. Is there a method in the ByteArray class that works like indexOf()? For example, I'm trying to find out if a file is

Solution 1:

Both PNG and JPG have a specified header, and they're both a specific size. You need to read just the bytes for the header (being careful to check against the byte array size so you don't go out of bounds). In the case of the PNG you first check if you have 4 bytes to read, then get those first 4 bytes, comparing against that 0x89, 0x50, 0x4E, 0x47 sequence. For JPG the first 3 bytes will be 0xFF 0xD8 0xFF.

Also keep in mind that the AS3 Loader class can just take the bytes for either one and figure it out for you.

Solution 2:

See if this example helps you. The idea is to find a candidate byte and then check if following bytes makes up rest-of-search item. So for bytes CA FE BA BE we search only for byte 0xCA and whenever one (out of many) is found we then check if it's followed by bytes 0xFE+0xBA+0xBE...

Here's a complete program to study...

The main function you want is :

indexOfBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):int

Which returns an int of the offset of search sequence's beginning. I've thrown in a checkImageFormat( bytes ); function where bytes are either .jpg or .png bytes. This will return a String saying whether if "jpg" or "png".

package  
{

import flash.display.MovieClip;
import flash.display.Sprite;
import flash.utils.ByteArray;
import flash.utils.*;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.net.URLLoaderDataFormat;
import flash.events.*;
import flash.net.URLStream;


publicclassCheck_Bytes_Image_v1extendsMovieClip{
    publicvar bytes    :ByteArray = new ByteArray();
    publicvar stream   :URLStream = new URLStream();
    publicvar loader   :Loader = new Loader();

    publicvar formatFound : Boolean = false;
    publicvar str_Format : String = "";
    publicvar str_Search : String = "";
    publicvar tempStr : String = "";
    publicvar tempUInt : uint = 0;
    publicvar bytePos : int = 0;

    publicvar searchString:String = "";;
    publicvar searchArray:Array = newArray();

    publicfunctionCheck_Bytes_Image_v1() 
    {
        //# add canvas
        addChild(loader);

        //# load image bytes via URLStream
        stream.addEventListener(ProgressEvent.PROGRESS, onStreamProgress);
        stream.addEventListener(Event.COMPLETE, completeHandler);
        stream.load(new URLRequest("image1.png")); //image1.png
    }

    publicfunctiononStreamProgress(evt:ProgressEvent):void{ evt.target.readBytes(bytes, bytes.length, evt.target.bytesAvailable); }

    publicfunctioncompleteHandler(evt:Event):void{

        //bytes_toString(bytes); //if you want to check hex output//# update a string with format type
        str_Format = checkImageFormat( bytes );
        trace("Image format is  : " + str_Format );

        searchString = "89 50 4E 47"; //use any known sequential bytes in your Array
        bytePos = indexOfBytes( bytes, searchString );
        trace("bytes begin at : " + bytePos);

        bytes.position = 0; //reset to starting pos
        loader.loadBytes(bytes); //decode & display image
        trace("loaded image now...");
        //bytes.clear(); //empty the bytes
    }

    //# findBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):intpublicfunctionindexOfBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):int{
        str_Search = value;
        trace("str_Search v1 is " + str_Search);

        //# Clean up possible hex notation
        str_Search = str_Search.split("0x").join("");
        str_Search = str_Search.split(".").join("");
        str_Search = str_Search.split("/").join("");

        //# Clean up hex (remove non Alpha-Numeric, white spaces, new line, etc)
        str_Search = str_Search.replace(/\W+/g, ""); //# clear all non Alpha-Numeric chars
        str_Search.toUpperCase(); //# convert to uppercase
        trace("str_Search v2 is " + str_Search);

        tempUInt = 0; searchArray = []; //# resets//# convert input string into two-digits-per-slot arraywhile ( tempUInt < str_Search.length) 
        {
            searchArray.push(str_Search.substring(tempUInt, Math.min(tempUInt+2, str_Search.length) ) );
            tempUInt += 2;
        }

        trace("searchArray length is : " + searchArray.length);

        //# search bytes for same hex vlues//# Resets for WHILE loopif ( startPosition != 0) { bytePos = startPosition; }
        else { bytePos = 0; }

        tempUInt = 0;

        while (byteArray.bytesAvailable)
        {
            //# safety check :  Stop at limit of bytes rangeif ( endPosition != 0 ) //# Don't go over end pos if not null
            {   
                if ( byteArray.position >= endPosition )
                { 
                    trace("reached end position of search : stopping at: " + byteArray.position );
                    break;
                }
            }
            else//# Don't go over total bytes length
            {
                if ( byteArray.position > (byteArray.length - searchArray.length) )
                { 
                    trace("reached ENDing bytes of search : stopping at: " + byteArray.position );
                    break;
                }
            } //# end safety check...//# Find search values
            byteArray.position = bytePos;
            tempStr = byteToHex( byteArray.readUnsignedByte() );
            tempUInt = 0; //reset for TRUE countif ( tempStr == searchArray[0] )
            {
                //# tempUInt to count how many times it was true//# if total true's == search array length then we found match//trace("-------------------------------------------");for (var i:int = 0; i <= searchArray.length-1; i++) 
                {
                    bytePos = byteArray.position-1;

                    if ( byteToHex( byteArray[bytePos+i] ) == searchArray[i] )
                    {
                        //trace("bytes [" +bytePos+ " + " +i+ "] is : " +  byteToHex( byteArray[bytePos+i] ) );//trace("search array [" +i+ "] is : " + searchArray[i] );
                        tempUInt++; //# add +1 for each matching entry for both arrays
                    }

                }

                trace("tempUInt match is : " + tempUInt + " || Expected TRUE count : " + searchArray.length );

                if ( tempUInt == searchArray.length ) 
                { 
                    tempUInt = byteArray.position - 1;
                    trace("match FOUND for search at pos : " + tempUInt); 
                    break; 
                }
                else 
                { 
                    tempUInt = int(-1); 
                    trace("match NOT found for search... current pos : " + ( byteArray.position-1) );
                }

            }

            bytePos++;

        } //# end WHILE loopreturn tempUInt;
    }

    publicfunctionbyteToHex ( input:uint ) : String{
        tempStr = input.toString(16); 
        if(tempStr.length < 2) { tempStr = "0" + tempStr; } //# zero padding if 1 char onlyreturn tempStr.toUpperCase();
    }

    publicfunctioncheckImageFormat ( inputBA : ByteArray ) : String{
        //# If you need to READ as specific Endianness of format//# default in AS3 is BIG ENDIAN type//inputBA.endian = Endian.LITTLE_ENDIAN;//inputBA.endian = Endian.BIG_ENDIAN;

        formatFound = false; bytePos = 0;

        while( formatFound == false) //* keep going until BREAK action
        {
            if ( formatFound == true ){ trace("## Found format..."); break; }

            inputBA.position = bytePos; //* reset positions 
            tempUInt = inputBA.readUnsignedByte(); //* READ byte = check byte value as Int
            bytePos = inputBA.position; //* update Pos to new offset move caused by READ action//# Check for JPG (begins xFF xD8 xFF xE0)if (tempUInt == 0xFF) 
            { 
                inputBA.position = bytePos-1; //* go back one byte to include FF in next check
                tempUInt = inputBA.readUnsignedInt(); //* READ 4 bytes at once (expect FF-D8-FF-E0)//trace ("tempUInt check : " + tempUInt);if (tempUInt == 0xFFD8FFE0 ) //* check IF EQUAL to expected value
                { 
                    inputBA.position += 2; //skip 2 bytes ahead to expected pos of JFIF
                    tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytesif (tempUInt == 0x4A464946) //* if expected JFIF (as hex) value
                    {
                        str_Format = "jpg";
                        trace("found JPG start + JFIF header..."); formatFound = true;
                    }
                }
            } //# end Check for JPG//# Check for PNG (begins x89 x50 x4E x47)if (tempUInt == 0x89) 
            { 
                inputBA.position = bytePos-1; //* go back one byte to include 89 in next check
                tempUInt = inputBA.readUnsignedInt(); //* READ 4 bytes at once (expect 89-50-4E-47)//trace ("tempUInt check : " + tempUInt);if (tempUInt == 0x89504E47 ) //* check IF EQUAL to expected value
                { 
                    tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytesif (tempUInt == 0x0D0A1A0A) //* if also expected value
                    {
                        inputBA.position += 4; //skip 4 bytes ahead to expected pos of IHDR
                        tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytesif (tempUInt == 0x49484452) //* if expected IHDR (as hex) value
                        {
                            str_Format = "png";
                            trace("found PNG start + IHDR header..."); formatFound = true;
                        }
                    }
                }
            } //# end Check for PNG//# Increment Pos for next byte value checking at top of While loop
            bytePos++;

        } //# end WHILE loopreturn str_Format;
    }

    publicfunctionbytes_toString ( ba:ByteArray ) : String{
        //trace("checking args : " + args);
        tempStr = "";   var str_Hex:String = ""; var len:uint = ba.length;
        ba.position = 0;

        for (var i:uint = 0; i < len; i++) 
        {
            tempStr=ba.readUnsignedByte().toString(16); 

            if(tempStr.length<2) { tempStr="0"+tempStr; } //# zero-padding if 1 char only
            str_Hex += tempStr ;
        }

        return str_Hex.toUpperCase();
    }

    publicfunctionbytes_toInt( ...args ) : int{
        var temp_conv_ba : ByteArray = new ByteArray();

        for (var i:uint = 0; i < args.length; i++) 
        { temp_conv_ba[i] = args[i]; }

        var int_fromBytes:int = int("0x" + bytes_toString(temp_conv_ba) );
        return int_fromBytes;
    }

} //# end class

} //# end package 

Solution 3:

I've put together this method but it fails to find the first value in the PNG array but the other values it finds just fine.

var png:Array = [0x89,0x50,0x4E,0x47];
var pngIndex:int = getIndexOfValueInByteArray(byteArray, png, 0, 4);

/**
 * Gets the position where either a single character or an array of hexidecimal values are found in a byte array
 * */publicfunctiongetIndexOfValueInByteArray(byteArray:ByteArray, value:*, startPosition:int = 0, endPosition:int = 0, endian:String = null):int{
    var byte:uint;
    var byteString:String;
    var position:int;
    var matchIndex:int;
    var searchArray:Array;
    var searchByte:int;
    var searchByteString:String;
    var found:Boolean;
    var endOfFile:Boolean;
    var endIndex:uint;
    var debug:Boolean;
    var firstByte:uint;
    var firstByteString:String;
    var startIndex:uint;
    var searchArrayLength:int;
    var compareAsString:Boolean;

    debug = true;

    if (value is String) {
        searchArray = String(value).split("");
        compareAsString = true;
    }
    else {
        searchArray = ArrayUtil.toArray(value);
    }

    if (endian) {
        byteArray.endian = endian;
    }

    if (startPosition>-1) {
        byteArray.position = startPosition;
    }

    if (searchArray && searchArray.length) {
        firstByte = searchArray[0];
        firstByteString = compareAsString ? searchArray[0] : String.fromCharCode(firstByte);
        searchArrayLength = searchArray.length;
    }
    else {
        return -1;
    }

    while (byteArray.bytesAvailable) {
        byte = byteArray.readByte();

        if (!compareAsString && byte==firstByte) {
            debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;

            for (var j:int = 1; j < searchArrayLength; j++) {
                byte = byteArray.readByte();
                searchByte = searchArray[j];

                debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;

                if (byte==searchByte) {
                    if (j==searchArrayLength-1) {
                        found = true;
                        matchIndex = byteArray.position;
                        startIndex = matchIndex - searchArrayLength;
                        endIndex = matchIndex;

                        debug ? trace("Match found at " + startIndex):void;

                        break;
                    }
                }

                if (byteArray.bytesAvailable==0) {
                    endOfFile = true;
                    break;
                }
            }
        }

        elseif (compareAsString && String.fromCharCode(byte)==firstByteString) {
            debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;

            for (j = 1; j < searchArrayLength; j++) {
                byteString = String.fromCharCode(byteArray.readByte());
                searchByteString = searchArray[j];

                debug ? trace("Byte:0x" + byte.toString(16) + " " + searchByteString):void;

                if (byteString==searchByteString) {
                    if (j==searchArrayLength-1) {
                        found = true;
                        matchIndex = byteArray.position;
                        startIndex = matchIndex - searchArrayLength;
                        endIndex = matchIndex;

                        debug ? trace("Match found at " + startIndex):void;

                        break;
                    }
                }

                if (byteArray.bytesAvailable==0) {
                    endOfFile = true;
                    break;
                }
            }
        }
        else {
            debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;
        }

        if (found || endOfFile || (endPosition!=0 && byteArray.position>endPosition)) {
            break;
        }
    }

    if (found) {
        debug?trace("Found at position " + startIndex + ". It ends at " + endIndex):0;
    }
    else {
        debug?trace("Could not find what the value you're looking for in this here byte array"):0;
        matchIndex = -1;
    }

    return matchIndex;
}

var png:Array = [0x89, 0x50, 0x4E, 0x47];
var pngIndex:int = getIndexOfValueInByteArray(byteArray, png, 0, 5);

The values traced out are:

Byte:0xffffff89Byte:0x50Byte:0x4eByte:0x47

If I set byte as int instead of uint it prints out:

Byte:0x-77Byte:0x50Byte:0x4eByte:0x47

When I use a JPEG and set byte as uint it prints out this value:

var jpg:Array = [0xFF, 0xD8, 0xFF];
var jpgIndex:int = getIndexOfValueInByteArray(byteArray, jpg, 0, 5);

Byte:0xffffffffByte:0xffffffd8Byte:0xffffffffByte:0xffffffe0

It looks like it matches the last of values.

UPDATE: I'm going to just pass in the 0xFFFFFF89 instead of 0x89. That seems to work on Mac. Don't know how or why. I updated the function to print out the hexidecimal character and string character if it translates to a string (sometimes it's empty).

Post a Comment for "Is There A Simple Method To Match An Array Of Bytes In An Byte Array?"