corrade-vassal – Rev 1

Subversion Repositories:
Rev:
/*
 * Copyright (c) 2006-2014, openmetaverse.org
 * All rights reserved.
 *
 * - Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * - Neither the name of the openmetaverse.org nor the names
 *   of its contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/* 
 * 
 * This implementation is based upon the description at
 * 
 * http://wiki.secondlife.com/wiki/LLSD
 * 
 * and (partially) tested against the (supposed) reference implementation at
 * 
 * http://svn.secondlife.com/svn/linden/release/indra/lib/python/indra/base/osd.py
 * 
 */

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace OpenMetaverse.StructuredData
{
    /// <summary>
    /// 
    /// </summary>
    public static partial class OSDParser
    {
        private const int initialBufferSize = 128;
        private const int int32Length = 4;
        private const int doubleLength = 8;

        private const string llsdBinaryHead = "<? llsd/binary ?>";
        private const string llsdBinaryHead2 = "<?llsd/binary?>";
        private const byte undefBinaryValue = (byte)'!';
        private const byte trueBinaryValue = (byte)'1';
        private const byte falseBinaryValue = (byte)'0';
        private const byte integerBinaryMarker = (byte)'i';
        private const byte realBinaryMarker = (byte)'r';
        private const byte uuidBinaryMarker = (byte)'u';
        private const byte binaryBinaryMarker = (byte)'b';
        private const byte stringBinaryMarker = (byte)'s';
        private const byte uriBinaryMarker = (byte)'l';
        private const byte dateBinaryMarker = (byte)'d';
        private const byte arrayBeginBinaryMarker = (byte)'[';
        private const byte arrayEndBinaryMarker = (byte)']';
        private const byte mapBeginBinaryMarker = (byte)'{';
        private const byte mapEndBinaryMarker = (byte)'}';
        private const byte keyBinaryMarker = (byte)'k';

        private static readonly byte[] llsdBinaryHeadBytes = Encoding.ASCII.GetBytes(llsdBinaryHead2);

        /// <summary>
        /// Deserializes binary LLSD
        /// </summary>
        /// <param name="binaryData">Serialized data</param>
        /// <returns>OSD containting deserialized data</returns>
        public static OSD DeserializeLLSDBinary(byte[] binaryData)
        {

            MemoryStream stream = new MemoryStream(binaryData);
            OSD osd = DeserializeLLSDBinary(stream);
            stream.Close();
            return osd;
        }

        /// <summary>
        /// Deserializes binary LLSD
        /// </summary>
        /// <param name="stream">Stream to read the data from</param>
        /// <returns>OSD containting deserialized data</returns>
        public static OSD DeserializeLLSDBinary(Stream stream)
        {
            if (!stream.CanSeek)
                throw new OSDException("Cannot deserialize binary LLSD from unseekable streams");

            SkipWhiteSpace(stream);

            if (!FindString(stream, llsdBinaryHead) && !FindString(stream, llsdBinaryHead2))
            {
                //throw new OSDException("Failed to decode binary LLSD");
            }

            SkipWhiteSpace(stream);

            return ParseLLSDBinaryElement(stream);
        }

        /// <summary>
        /// Serializes OSD to binary format. It does no prepend header
        /// </summary>
        /// <param name="osd">OSD to serialize</param>
        /// <returns>Serialized data</returns>
        public static byte[] SerializeLLSDBinary(OSD osd)
        {
            return SerializeLLSDBinary(osd, true);
        }

        /// <summary>
        /// Serializes OSD to binary format
        /// </summary>
        /// <param name="osd">OSD to serialize</param>
        /// <param name="prependHeader"></param>
        /// <returns>Serialized data</returns>
        public static byte[] SerializeLLSDBinary(OSD osd, bool prependHeader)
        {
            MemoryStream stream = SerializeLLSDBinaryStream(osd, prependHeader);
            byte[] binaryData = stream.ToArray();

            stream.Close();

            return binaryData;
        }

        /// <summary>
        /// Serializes OSD to binary format. It does no prepend header
        /// </summary>
        /// <param name="data">OSD to serialize</param>
        /// <returns>Serialized data</returns>
        public static MemoryStream SerializeLLSDBinaryStream(OSD data)
        {
            return SerializeLLSDBinaryStream(data, true);
        }
        /// <summary>
        /// Serializes OSD to binary format
        /// </summary>
        /// <param name="data">OSD to serialize</param>
        /// <param name="prependHeader"></param>
        /// <returns>Serialized data</returns>
        public static MemoryStream SerializeLLSDBinaryStream(OSD data, bool prependHeader)
        {
            MemoryStream stream = new MemoryStream(initialBufferSize);

            if (prependHeader)
            {
                stream.Write(llsdBinaryHeadBytes, 0, llsdBinaryHeadBytes.Length);
                stream.WriteByte((byte)'\n');
            }
            SerializeLLSDBinaryElement(stream, data);
            return stream;
        }

        private static void SerializeLLSDBinaryElement(MemoryStream stream, OSD osd)
        {
            switch (osd.Type)
            {
                case OSDType.Unknown:
                    stream.WriteByte(undefBinaryValue);
                    break;
                case OSDType.Boolean:
                    stream.Write(osd.AsBinary(), 0, 1);
                    break;
                case OSDType.Integer:
                    stream.WriteByte(integerBinaryMarker);
                    stream.Write(osd.AsBinary(), 0, int32Length);
                    break;
                case OSDType.Real:
                    stream.WriteByte(realBinaryMarker);
                    stream.Write(osd.AsBinary(), 0, doubleLength);
                    break;
                case OSDType.UUID:
                    stream.WriteByte(uuidBinaryMarker);
                    stream.Write(osd.AsBinary(), 0, 16);
                    break;
                case OSDType.String:
                    stream.WriteByte(stringBinaryMarker);
                    byte[] rawString = osd.AsBinary();
                    byte[] stringLengthNetEnd = HostToNetworkIntBytes(rawString.Length);
                    stream.Write(stringLengthNetEnd, 0, int32Length);
                    stream.Write(rawString, 0, rawString.Length);
                    break;
                case OSDType.Binary:
                    stream.WriteByte(binaryBinaryMarker);
                    byte[] rawBinary = osd.AsBinary();
                    byte[] binaryLengthNetEnd = HostToNetworkIntBytes(rawBinary.Length);
                    stream.Write(binaryLengthNetEnd, 0, int32Length);
                    stream.Write(rawBinary, 0, rawBinary.Length);
                    break;
                case OSDType.Date:
                    stream.WriteByte(dateBinaryMarker);
                    stream.Write(osd.AsBinary(), 0, doubleLength);
                    break;
                case OSDType.URI:
                    stream.WriteByte(uriBinaryMarker);
                    byte[] rawURI = osd.AsBinary();
                    byte[] uriLengthNetEnd = HostToNetworkIntBytes(rawURI.Length);
                    stream.Write(uriLengthNetEnd, 0, int32Length);
                    stream.Write(rawURI, 0, rawURI.Length);
                    break;
                case OSDType.Array:
                    SerializeLLSDBinaryArray(stream, (OSDArray)osd);
                    break;
                case OSDType.Map:
                    SerializeLLSDBinaryMap(stream, (OSDMap)osd);
                    break;
                default:
                    throw new OSDException("Binary serialization: Not existing element discovered.");

            }
        }

        private static void SerializeLLSDBinaryArray(MemoryStream stream, OSDArray osdArray)
        {
            stream.WriteByte(arrayBeginBinaryMarker);
            byte[] binaryNumElementsHostEnd = HostToNetworkIntBytes(osdArray.Count);
            stream.Write(binaryNumElementsHostEnd, 0, int32Length);

            foreach (OSD osd in osdArray)
            {
                SerializeLLSDBinaryElement(stream, osd);
            }
            stream.WriteByte(arrayEndBinaryMarker);
        }

        private static void SerializeLLSDBinaryMap(MemoryStream stream, OSDMap osdMap)
        {
            stream.WriteByte(mapBeginBinaryMarker);
            byte[] binaryNumElementsNetEnd = HostToNetworkIntBytes(osdMap.Count);
            stream.Write(binaryNumElementsNetEnd, 0, int32Length);

            foreach (KeyValuePair<string, OSD> kvp in osdMap)
            {
                stream.WriteByte(keyBinaryMarker);
                byte[] binaryKey = Encoding.UTF8.GetBytes(kvp.Key);
                byte[] binaryKeyLength = HostToNetworkIntBytes(binaryKey.Length);
                stream.Write(binaryKeyLength, 0, int32Length);
                stream.Write(binaryKey, 0, binaryKey.Length);
                SerializeLLSDBinaryElement(stream, kvp.Value);
            }
            stream.WriteByte(mapEndBinaryMarker);
        }

        private static OSD ParseLLSDBinaryElement(Stream stream)
        {
            SkipWhiteSpace(stream);
            OSD osd;

            int marker = stream.ReadByte();
            if (marker < 0)
                throw new OSDException("Binary LLSD parsing: Unexpected end of stream.");

            switch ((byte)marker)
            {
                case undefBinaryValue:
                    osd = new OSD();
                    break;
                case trueBinaryValue:
                    osd = OSD.FromBoolean(true);
                    break;
                case falseBinaryValue:
                    osd = OSD.FromBoolean(false);
                    break;
                case integerBinaryMarker:
                    int integer = NetworkToHostInt(ConsumeBytes(stream, int32Length));
                    osd = OSD.FromInteger(integer);
                    break;
                case realBinaryMarker:
                    double dbl = NetworkToHostDouble(ConsumeBytes(stream, doubleLength));
                    osd = OSD.FromReal(dbl);
                    break;
                case uuidBinaryMarker:
                    osd = OSD.FromUUID(new UUID(ConsumeBytes(stream, 16), 0));
                    break;
                case binaryBinaryMarker:
                    int binaryLength = NetworkToHostInt(ConsumeBytes(stream, int32Length));
                    osd = OSD.FromBinary(ConsumeBytes(stream, binaryLength));
                    break;
                case stringBinaryMarker:
                    int stringLength = NetworkToHostInt(ConsumeBytes(stream, int32Length));
                    string ss = Encoding.UTF8.GetString(ConsumeBytes(stream, stringLength));
                    osd = OSD.FromString(ss);
                    break;
                case uriBinaryMarker:
                    int uriLength = NetworkToHostInt(ConsumeBytes(stream, int32Length));
                    string sUri = Encoding.UTF8.GetString(ConsumeBytes(stream, uriLength));
                    Uri uri;
                    try
                    {
                        uri = new Uri(sUri, UriKind.RelativeOrAbsolute);
                    }
                    catch
                    {
                        throw new OSDException("Binary LLSD parsing: Invalid Uri format detected.");
                    }
                    osd = OSD.FromUri(uri);
                    break;
                case dateBinaryMarker:
                    double timestamp = Utils.BytesToDouble(ConsumeBytes(stream, doubleLength), 0);
                    DateTime dateTime = DateTime.SpecifyKind(Utils.Epoch, DateTimeKind.Utc);
                    dateTime = dateTime.AddSeconds(timestamp);
                    osd = OSD.FromDate(dateTime.ToLocalTime());
                    break;
                case arrayBeginBinaryMarker:
                    osd = ParseLLSDBinaryArray(stream);
                    break;
                case mapBeginBinaryMarker:
                    osd = ParseLLSDBinaryMap(stream);
                    break;
                default:
                    throw new OSDException("Binary LLSD parsing: Unknown type marker.");

            }
            return osd;
        }

        private static OSD ParseLLSDBinaryArray(Stream stream)
        {
            int numElements = NetworkToHostInt(ConsumeBytes(stream, int32Length));
            int crrElement = 0;
            OSDArray osdArray = new OSDArray();
            while (crrElement < numElements)
            {
                osdArray.Add(ParseLLSDBinaryElement(stream));
                crrElement++;
            }

            if (!FindByte(stream, arrayEndBinaryMarker))
                throw new OSDException("Binary LLSD parsing: Missing end marker in array.");

            return (OSD)osdArray;
        }

        private static OSD ParseLLSDBinaryMap(Stream stream)
        {
            int numElements = NetworkToHostInt(ConsumeBytes(stream, int32Length));
            int crrElement = 0;
            OSDMap osdMap = new OSDMap();
            while (crrElement < numElements)
            {
                if (!FindByte(stream, keyBinaryMarker))
                    throw new OSDException("Binary LLSD parsing: Missing key marker in map.");
                int keyLength = NetworkToHostInt(ConsumeBytes(stream, int32Length));
                string key = Encoding.UTF8.GetString(ConsumeBytes(stream, keyLength));
                osdMap[key] = ParseLLSDBinaryElement(stream);
                crrElement++;
            }

            if (!FindByte(stream, mapEndBinaryMarker))
                throw new OSDException("Binary LLSD parsing: Missing end marker in map.");

            return (OSD)osdMap;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stream"></param>
        public static void SkipWhiteSpace(Stream stream)
        {
            int bt;

            while (((bt = stream.ReadByte()) > 0) &&
                ((byte)bt == ' ' || (byte)bt == '\t' ||
                  (byte)bt == '\n' || (byte)bt == '\r')
                 )
            {
            }
            stream.Seek(-1, SeekOrigin.Current);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="toFind"></param>
        /// <returns></returns>
        public static bool FindByte(Stream stream, byte toFind)
        {
            int bt = stream.ReadByte();
            if (bt < 0)
                return false;
            if ((byte)bt == toFind)
                return true;
            else
            {
                stream.Seek(-1L, SeekOrigin.Current);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="toFind"></param>
        /// <returns></returns>
        public static bool FindString(Stream stream, string toFind)
        {
            int lastIndexToFind = toFind.Length - 1;
            int crrIndex = 0;
            bool found = true;
            int bt;
            long lastPosition = stream.Position;

            while (found &&
                  ((bt = stream.ReadByte()) > 0) &&
                    (crrIndex <= lastIndexToFind)
                  )
            {
                if (toFind[crrIndex].ToString().Equals(((char)bt).ToString(), StringComparison.InvariantCultureIgnoreCase))
                {
                    found = true;
                    crrIndex++;
                }
                else
                    found = false;
            }

            if (found && crrIndex > lastIndexToFind)
            {
                stream.Seek(-1L, SeekOrigin.Current);
                return true;
            }
            else
            {
                stream.Position = lastPosition;
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="consumeBytes"></param>
        /// <returns></returns>
        public static byte[] ConsumeBytes(Stream stream, int consumeBytes)
        {
            byte[] bytes = new byte[consumeBytes];
            if (stream.Read(bytes, 0, consumeBytes) < consumeBytes)
                throw new OSDException("Binary LLSD parsing: Unexpected end of stream.");
            return bytes;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="binaryNetEnd"></param>
        /// <returns></returns>
        public static int NetworkToHostInt(byte[] binaryNetEnd)
        {
            if (binaryNetEnd == null)
                return -1;

            int intNetEnd = BitConverter.ToInt32(binaryNetEnd, 0);
            int intHostEnd = System.Net.IPAddress.NetworkToHostOrder(intNetEnd);
            return intHostEnd;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="binaryNetEnd"></param>
        /// <returns></returns>
        public static double NetworkToHostDouble(byte[] binaryNetEnd)
        {
            if (binaryNetEnd == null)
                return -1d;
            long longNetEnd = BitConverter.ToInt64(binaryNetEnd, 0);
            long longHostEnd = System.Net.IPAddress.NetworkToHostOrder(longNetEnd);
            byte[] binaryHostEnd = BitConverter.GetBytes(longHostEnd);
            double doubleHostEnd = BitConverter.ToDouble(binaryHostEnd, 0);
            return doubleHostEnd;

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="intHostEnd"></param>
        /// <returns></returns>   
        public static byte[] HostToNetworkIntBytes(int intHostEnd)
        {
            int intNetEnd = System.Net.IPAddress.HostToNetworkOrder(intHostEnd);
            byte[] bytesNetEnd = BitConverter.GetBytes(intNetEnd);
            return bytesNetEnd;

        }
    }
}

Generated by GNU Enscript 1.6.5.90.