public static void AddBubbles(BubbleGroup group, VisualBubble[] bubbles)
        {
            lock (OperationLock)
            {
                var file = GetLocation(@group);

                using (var stream = File.Open(file, FileMode.Append, FileAccess.Write))
                {
                    BubbleGroupIndex.UpdateLastBubbleOrAndLastModifiedIndex(group.ID, bubbles.Last(), stream.Position);

                    foreach (var b in bubbles)
                    {
                        using (var ms = new MemoryStream())
                        {
                            Serializer.Serialize(ms, b);

                            var bubbleData   = ms.ToArray();
                            var bubbleHeader = b.GetType().Name + ":" + b.ID + ":" + b.Time;

                            BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleData);
                            BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleHeader);
                        }
                    }
                }
            }
        }
Exemple #2
0
        public static void AddBubble(BubbleGroup group, VisualBubble b)
        {
            lock (OperationLock)
            {
                using (var ms = new MemoryStream())
                {
                    Serializer.Serialize(ms, b);

                    var bubbleData   = ms.ToArray();
                    var bubbleHeader = b.GetType().Name + ":" + b.ID + ":" + b.Time;

                    var file = GetLocation(@group);

                    using (var stream = File.Open(file, FileMode.Append, FileAccess.Write))
                    {
                        BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleData);
                        BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleHeader);
                    }
                }
            }
        }
Exemple #3
0
        public static bool UpdateBubble(BubbleGroup group, VisualBubble[] bubbles, int searchDepth = 100)
        {
            //we can't operate if a thread/worker is concurrently processing a new bubble
            lock (OperationLock)
            {
                var groupDatabaseLocation = GetLocation(@group);
                var bubbleTuples          = bubbles.Select(x => new Tuple <string, VisualBubble>(x.ID, x)).ToList();

                using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite))
                {
                    stream.Seek(stream.Length, SeekOrigin.Begin);

                    for (var i = 0; i < (searchDepth != -1 ? searchDepth : Int32.MaxValue); i++)
                    {
                        if (stream.Position == 0)
                        {
                            break;
                        }

                        byte[] headerBytes;
                        int    headerBytesLength;
                        int    endPosition;

                        BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength);
                        BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition);
                        var bubbleDataLength = BubbleGroupDatabasePrimitives.JumpBubbleData(stream); //we need to seek over the data.

                        var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength,
                                                                                        endPosition + 1, out endPosition);

                        Tuple <string, VisualBubble> bubbleTuple;
                        if ((bubbleTuple = bubbleTuples.FirstOrDefault(x => x.Item1 == guid)) == null)
                        {
                            continue;
                        }

                        var b = bubbleTuple.Item2;

                        var bubbleSize = headerBytesLength + bubbleDataLength + 8;
                        var cutStart   = stream.Position + bubbleSize;
                        var cutLength  = stream.Length - cutStart;

                        using (var ms = new MemoryStream())
                        {
                            Serializer.Serialize(ms, b);

                            var updatedBubbleData   = ms.ToArray();
                            var updatedBubbleHeader = b.GetType().Name + ":" + b.ID + ":" + b.Time;
                            var updatedBubbleSize   = updatedBubbleHeader.Length + updatedBubbleData.Length + 8;

                            var bubbleInjectDelta = bubbleSize - updatedBubbleSize;
                            //enough room
                            if (bubbleInjectDelta != 0)
                            {
                                var injectPosition = stream.Position;

                                stream.Position = cutStart;          //higher
                                var cut = new byte[cutLength];
                                stream.Read(cut, 0, (int)cutLength); //should always work as long as the count ain't crazy high

                                //var bw = new BinaryWriter(stream, Encoding.ASCII);
                                stream.Position = injectPosition;
                                BubbleGroupDatabasePrimitives.WriteBubbleData(stream, updatedBubbleData);
                                BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, updatedBubbleHeader);

                                stream.Write(cut, 0, cut.Length);
                                stream.SetLength(stream.Position);
                            }
                            //they're the same!
                            else if (bubbleInjectDelta == 0)
                            {
                                //var bw = new BinaryWriter(stream, Encoding.ASCII);
                                BubbleGroupDatabasePrimitives.WriteBubbleData(stream, updatedBubbleData);
                                BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, updatedBubbleHeader);
                            }
                        }

                        bubbleTuples.Remove(bubbleTuple);
                        if (!bubbleTuples.Any())
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Exemple #4
0
        public static bool InsertBubblesByTime(BubbleGroup group, VisualBubble[] bubbles, int maxDepth = 1000, bool guidCheck = false, bool insertAtTop = false)
        {
            //we can't operate if a thread/worker is concurrently processing a new bubble
            lock (OperationLock)
            {
                var groupDatabaseLocation = GetLocation(@group);
                var bubbleTuples          = bubbles.Select(x => new Tuple <long, VisualBubble>(x.Time, x)).ToList();

                using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite))
                {
                    stream.Seek(stream.Length, SeekOrigin.Begin);

                    for (var i = 0; i < (maxDepth != -1 ? maxDepth : Int32.MaxValue); i++)
                    {
                        if (stream.Position == 0)
                        {
                            if (insertAtTop)
                            {
                                var cut = new byte[(int)stream.Length];
                                stream.Read(cut, 0, (int)stream.Length);
                                stream.Position = 0;

                                var bubblesToInsert = bubbleTuples.Select(x => x.Item2).ToList();
                                bubblesToInsert.TimSort((x, y) => x.Time.CompareTo(y.Time));
                                foreach (var bubbleToInsert in bubblesToInsert)
                                {
                                    using (var ms = new MemoryStream())
                                    {
                                        Serializer.Serialize(ms, bubbleToInsert);

                                        var bubbleToInsertData   = ms.ToArray();
                                        var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID
                                                                   + ":" + bubbleToInsert.Time;

                                        BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData);
                                        BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader);
                                    }
                                }

                                stream.Write(cut, 0, cut.Length);
                                stream.SetLength(stream.Position);

                                return(true);
                            }
                            else
                            {
                                break;
                            }
                        }

                        byte[] headerBytes;
                        int    headerBytesLength;
                        int    endPosition;

                        BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength);
                        BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition);
                        var bubbleDataLength = BubbleGroupDatabasePrimitives.JumpBubbleData(stream); //we need to seek over the data.

                        var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength,
                                                                                        endPosition + 1, out endPosition);
                        var time = BubbleGroupDatabasePrimitives.AsciiBytesToString(headerBytes, endPosition + 1,
                                                                                    headerBytesLength);
                        long longTime;
                        Int64.TryParse(time, out longTime);

                        {
                            var bubblesToInsert = bubbleTuples.Where(x =>
                            {
                                if (guidCheck && guid == x.Item2.ID)
                                {
                                    return(false);
                                }

                                if (longTime <= x.Item1)
                                {
                                    return(true);
                                }

                                return(false);
                            }).ToList();
                            if (!bubblesToInsert.Any())
                            {
                                continue;
                            }

                            var bubbleSize     = headerBytesLength + bubbleDataLength + 8;
                            var insertLocation = stream.Position + bubbleSize;
                            stream.Seek(insertLocation, SeekOrigin.Begin);

                            var cutLength = stream.Length - insertLocation;
                            var cut       = new byte[cutLength];
                            stream.Read(cut, 0, (int)cutLength); //should always work as long as the count ain't crazy high

                            stream.Seek(insertLocation, SeekOrigin.Begin);

                            foreach (var bubbleToInsert in bubblesToInsert.Select(x => x.Item2))
                            {
                                using (var ms = new MemoryStream())
                                {
                                    Serializer.Serialize(ms, bubbleToInsert);

                                    var bubbleToInsertData   = ms.ToArray();
                                    var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID
                                                               + ":" + bubbleToInsert.Time;

                                    BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData);
                                    BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader);
                                }
                            }

                            stream.Write(cut, 0, cut.Length);
                            stream.SetLength(stream.Position);

                            foreach (var bubbleToInsert in bubblesToInsert)
                            {
                                bubbleTuples.Remove(bubbleToInsert);
                            }

                            if (!bubbleTuples.Any())
                            {
                                return(true);
                            }
                        }
                    }
                }

                return(false);
            }
        }
        public static bool InsertBubblesByTime(BubbleGroup group, VisualBubble[] bubbles, int maxDepth = 1000, bool guidCheck = false, bool insertAtTop = false)
        {
            //we can't operate if a thread/worker is concurrently processing a new bubble
            lock (OperationLock)
            {
                var groupDatabaseLocation = GetLocation(@group);
                var bubbleTuples          = bubbles.Select(x => new Tuple <long, VisualBubble>(x.Time, x)).ToList();

                VisualBubble lastBubble     = null;
                long         insertPosition = -1;

                using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite))
                {
                    stream.Seek(stream.Length, SeekOrigin.Begin);

                    for (var i = 0; i < (maxDepth != -1 ? maxDepth : Int32.MaxValue); i++)
                    {
                        if (stream.Position == 0)
                        {
                            if (insertAtTop)
                            {
                                insertPosition = 0;

                                var cut = new byte[(int)stream.Length];
                                stream.Read(cut, 0, (int)stream.Length);
                                stream.Position = 0;

                                var bubblesToInsert = bubbleTuples.Select(x => x.Item2).ToList();
                                bubblesToInsert.TimSort((x, y) =>
                                {
                                    // IMPORTANT: Our Time field is specified in seconds, however scenarios are appearing
                                    //            (e.g., bots) where messages are sent in on the same second but still require
                                    //            proper ordering. In this case, Services may set a flag specifying a fallback
                                    //            to the ID assigned by the Service (e.g. Telegram).
                                    if ((x.Time == y.Time) &&
                                        (x.IsServiceIdSequence && y.IsServiceIdSequence))
                                    {
                                        return(string.Compare(
                                                   strA: x.IdService,
                                                   strB: y.IdService,
                                                   ignoreCase: false,
                                                   culture: CultureInfo.InvariantCulture));
                                    }
                                    // OK, simpler scenario, just compare the Time field for the bubbles
                                    else
                                    {
                                        return(x.Time.CompareTo(y.Time));
                                    }
                                });

                                foreach (var bubbleToInsert in bubblesToInsert)
                                {
                                    using (var ms = new MemoryStream())
                                    {
                                        Serializer.Serialize(ms, bubbleToInsert);

                                        var bubbleToInsertData   = ms.ToArray();
                                        var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID
                                                                   + ":" + bubbleToInsert.Time;

                                        BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData);
                                        BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader);
                                    }
                                }

                                stream.Write(cut, 0, cut.Length);
                                stream.SetLength(stream.Position);

                                break;
                            }
                            else
                            {
                                break;
                            }
                        }

                        byte[] headerBytes;
                        int    headerBytesLength;
                        int    endPosition;
                        int    bubbleDataLength;

                        BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength);
                        BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition);

                        //we need to seek over the data.
                        var bubbleData = BubbleGroupDatabasePrimitives.ReadBubbleData(stream, out bubbleDataLength);
                        var nBubble    = Deserialize(bubbleData);

                        var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength,
                                                                                        endPosition + 1, out endPosition);
                        var time = BubbleGroupDatabasePrimitives.AsciiBytesToString(headerBytes, endPosition + 1,
                                                                                    headerBytesLength);
                        long longTime;
                        Int64.TryParse(time, out longTime);

                        {
                            var bubblesToInsert = bubbleTuples.Where(x =>
                            {
                                if (guidCheck && guid == x.Item2.ID)
                                {
                                    return(false);
                                }

                                // IMPORTANT: Our Time field is specified in seconds, however scenarios are appearing
                                //            (e.g., bots) where messages are sent in on the same second but still require
                                //            proper ordering. In this case, Services may set a flag specifying a fallback
                                //            to the ID assigned by the Service (e.g. Telegram).
                                if ((longTime == x.Item1) &&
                                    (nBubble.IsServiceIdSequence && x.Item2.IsServiceIdSequence))
                                {
                                    if (string.Compare(
                                            strA: nBubble.IdService,
                                            strB: x.Item2.IdService,
                                            ignoreCase: false,
                                            culture: CultureInfo.InvariantCulture) < 0)
                                    {
                                        //
                                        // Incoming bubble qualifies to be placed AFTER current bubble we are evaluating
                                        //

                                        return(true);
                                    }
                                    else
                                    {
                                        //
                                        // Incoming bubble DOES NOT qualify to be placed AFTER current bubble we are evaluating
                                        //

                                        return(false);
                                    }
                                }
                                // OK, simpler scenario, DOES incoming bubble qualify to be placed AFTER current bubble we are evaluating
                                if (longTime <= x.Item1)
                                {
                                    return(true);
                                }
                                else
                                {
                                    return(false);
                                }
                            }).ToList();
                            if (!bubblesToInsert.Any())
                            {
                                continue;
                            }

                            var bubbleSize     = headerBytesLength + bubbleDataLength + 8;
                            var insertLocation = stream.Position + bubbleSize;
                            stream.Seek(insertLocation, SeekOrigin.Begin);

                            var cutLength = stream.Length - insertLocation;
                            var cut       = new byte[cutLength];
                            stream.Read(cut, 0, (int)cutLength); //should always work as long as the count ain't crazy high

                            stream.Seek(insertLocation, SeekOrigin.Begin);

                            insertPosition = stream.Position;

                            foreach (var bubbleToInsert in bubblesToInsert.Select(x => x.Item2))
                            {
                                using (var ms = new MemoryStream())
                                {
                                    Serializer.Serialize(ms, bubbleToInsert);

                                    var bubbleToInsertData   = ms.ToArray();
                                    var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID
                                                               + ":" + bubbleToInsert.Time;

                                    BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData);
                                    BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader);
                                }
                            }

                            stream.Write(cut, 0, cut.Length);
                            stream.SetLength(stream.Position);

                            // adding to end
                            if (cut.Length == 0)
                            {
                                lastBubble = bubblesToInsert.Last().Item2;
                            }

                            foreach (var bubbleToInsert in bubblesToInsert)
                            {
                                bubbleTuples.Remove(bubbleToInsert);
                            }

                            if (!bubbleTuples.Any())
                            {
                                break;
                            }
                        }
                    }

                    BubbleGroupIndex.UpdateLastBubbleOrAndLastModifiedIndex(group.ID, lastBubble, insertPosition);
                }

                if (!bubbleTuples.Any())
                {
                    return(true);
                }

                return(false);
            }
        }