< Summary

Class:GDX.IO.CoalesceStream
Assembly:GDX
File(s):./Packages/com.dotbunny.gdx/GDX/IO/CoalesceStream.cs
Covered lines:47
Uncovered lines:79
Coverable lines:126
Total lines:317
Line coverage:37.3% (47 of 126)
Covered branches:0
Total branches:0
Covered methods:11
Total methods:19
Method coverage:57.8% (11 of 19)

Coverage History

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
CoalesceStream(...)0%110100%
CoalesceStream(...)0%110100%
CoalesceStream(...)0%6200%
CoalesceStream(...)0%6200%
Read(...)0%42600%
Seek(...)0%20400%
SetLength(...)0%2100%
Write(...)0%3.053081.82%
ReadByte()0%2.062075%
WriteByte(...)0%6200%

File(s)

./Packages/com.dotbunny.gdx/GDX/IO/CoalesceStream.cs

#LineLine coverage
 1// Copyright (c) 2020-2024 dotBunny Inc.
 2// dotBunny licenses this file to you under the BSL-1.0 license.
 3// See the LICENSE file in the project root for more information.
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Diagnostics.CodeAnalysis;
 8using System.IO;
 9
 10namespace GDX.IO
 11{
 12    /// <summary>
 13    ///     A byte backed stream which combines multiple arrays acting as one uniform stream.
 14    /// </summary>
 15    /// <remarks>
 16    ///     Max size being limited to <see cref="m_Length" /> type limitations.
 17    /// </remarks>
 18    public class CoalesceStream : Stream
 19    {
 20        /// <summary>
 21        ///     The default size of each allocated bucket array.
 22        /// </summary>
 23        const int k_DefaultBucketSize = 65536;
 24
 25        /// <summary>
 26        ///     The internal arrays storage of buckets.
 27        /// </summary>
 428        readonly List<byte[]> m_Buckets = new List<byte[]>();
 29
 30        /// <summary>
 31        ///     The bucket size used to allocate new arrays.
 32        /// </summary>
 33        readonly int m_BucketSize;
 34
 35        /// <summary>
 36        ///     The perceived length of the data contained within.
 37        /// </summary>
 38        long m_Length;
 39
 40        /// <summary>
 41        ///     Create a <see cref="CoalesceStream" />.
 42        /// </summary>
 43        /// <param name="bucketSize">The bucket allocation size.</param>
 244        public CoalesceStream(int bucketSize = k_DefaultBucketSize)
 245        {
 246            Position = 0;
 247            m_BucketSize = bucketSize;
 248        }
 49
 50        /// <summary>
 51        ///     Create a <see cref="CoalesceStream" /> and fill it with the data found in <paramref name="source" />.
 52        /// </summary>
 53        /// <param name="source">An array used to prefill the <see cref="CoalesceStream" /> with.</param>
 54        /// <param name="bucketSize">The bucket allocation size.</param>
 255        public CoalesceStream(byte[] source, int bucketSize = k_DefaultBucketSize)
 256        {
 257            m_BucketSize = bucketSize;
 258            Write(source, 0, source.Length);
 259            Position = 0;
 260        }
 61
 62        /// <summary>
 63        ///     Preallocate a <see cref="CoalesceStream" /> at the desired <paramref name="length" />.
 64        /// </summary>
 65        /// <param name="length">The desired pre-allocated size.</param>
 66        /// <param name="bucketSize">The bucket allocation size.</param>
 067        public CoalesceStream(int length, int bucketSize = k_DefaultBucketSize)
 068        {
 069            m_BucketSize = bucketSize;
 070            SetLength(length);
 071            Position = length;
 072            while (m_Buckets.Count <= BucketIndex)
 073            {
 074                m_Buckets.Add(new byte[m_BucketSize]);
 075            }
 76
 077            Position = 0;
 078        }
 79
 80        /// <summary>
 81        ///     Preallocate a <see cref="CoalesceStream" /> at the desired <paramref name="length" />.
 82        /// </summary>
 83        /// <param name="length">The desired pre-allocated size.</param>
 84        /// <param name="bucketSize">The bucket allocation size.</param>
 085        public CoalesceStream(long length, int bucketSize = k_DefaultBucketSize)
 086        {
 087            m_BucketSize = bucketSize;
 088            SetLength(length);
 089            Position = length;
 090            while (m_Buckets.Count <= BucketIndex)
 091            {
 092                m_Buckets.Add(new byte[m_BucketSize]);
 093            }
 94
 095            Position = 0;
 096        }
 97
 98        /// <summary>
 99        ///     Is this <see cref="Stream" /> capable of reading?
 100        /// </summary>
 0101        public override bool CanRead => true;
 102
 103        /// <summary>
 104        ///     Is this <see cref="Stream" /> capable of seeking?
 105        /// </summary>
 0106        public override bool CanSeek => true;
 107
 108        /// <summary>
 109        ///     Is this <see cref="Stream" /> capable of writing?
 110        /// </summary>
 0111        public override bool CanWrite => true;
 112
 113        /// <summary>
 114        ///     Get the combined length of the internal arrays.
 115        /// </summary>
 0116        public override long Length => m_Length;
 117
 118        /// <summary>
 119        ///     Get the current position in the <see cref="Stream" />.
 120        /// </summary>
 30121        public sealed override long Position { get; set; }
 122
 123        /// <summary>
 124        ///     Get the current bucket.
 125        /// </summary>
 126        byte[] Bucket
 127        {
 128            get
 3129            {
 5130                while (m_Buckets.Count <= BucketIndex)
 2131                {
 2132                    m_Buckets.Add(new byte[m_BucketSize]);
 2133                }
 134
 3135                return m_Buckets[(int)BucketIndex];
 3136            }
 137        }
 138
 139        /// <summary>
 140        ///     Determine the current bucket index based on the position and bucket size.
 141        /// </summary>
 8142        long BucketIndex => Position / m_BucketSize;
 143
 144        /// <summary>
 145        ///     Determine the current bucket offset based on the position and bucket size.
 146        /// </summary>
 5147        long BucketOffset => Position % m_BucketSize;
 148
 149
 150        /// <summary>
 151        ///     Flush reading and writing buffers.
 152        /// </summary>
 153        /// <remarks>
 154        ///     Does nothing for the <see cref="CoalesceStream" />.
 155        /// </remarks>
 156        [ExcludeFromCodeCoverage]
 157        public override void Flush()
 158        {
 159        }
 160
 161        /// <summary>
 162        ///     Read from the <see cref="CoalesceStream" /> into a buffer.
 163        /// </summary>
 164        /// <param name="buffer">The target buffer to write the read data into.</param>
 165        /// <param name="offset">The offset position to start writing into the <paramref name="buffer" />.</param>
 166        /// <param name="count">The number of <see cref="byte" />s to read.</param>
 167        /// <returns>The number of bytes read.</returns>
 168        /// <exception cref="ArgumentOutOfRangeException">
 169        ///     Thrown when a negative amounts of bytes are requested, or a negative offset is provided.
 170        /// </exception>
 171        /// <exception cref="ArgumentNullException">
 172        ///     Thrown when the provided <paramref name="buffer" /> is null.
 173        /// </exception>
 174        public override int Read(byte[] buffer, int offset, int count)
 0175        {
 0176            long readCount = count;
 177
 0178            if (readCount < 0)
 0179            {
 0180                throw new ArgumentOutOfRangeException(nameof(count), readCount,
 181                    "Number of bytes to copy cannot be negative.");
 182            }
 183
 0184            long remaining = m_Length - Position;
 0185            if (readCount > remaining)
 0186            {
 0187                readCount = remaining;
 0188            }
 189
 0190            if (buffer == null)
 0191            {
 0192                throw new ArgumentNullException(nameof(buffer), "Buffer cannot be null.");
 193            }
 194
 0195            if (offset < 0)
 0196            {
 0197                throw new ArgumentOutOfRangeException(nameof(offset), offset, "Destination offset cannot be negative.");
 198            }
 199
 0200            int read = 0;
 201            do
 0202            {
 0203                long copySize = Math.Min(readCount, m_BucketSize - BucketOffset);
 0204                Buffer.BlockCopy(Bucket, (int)BucketOffset, buffer, offset, (int)copySize);
 0205                readCount -= copySize;
 0206                offset += (int)copySize;
 207
 0208                read += (int)copySize;
 0209                Position += copySize;
 0210            } while (readCount > 0);
 211
 0212            return read;
 0213        }
 214
 215        /// <summary>
 216        ///     Seek the internal position to a new location.
 217        /// </summary>
 218        /// <param name="offset">The value to offset the internal position by.</param>
 219        /// <param name="origin">The origin of the offset.</param>
 220        /// <returns></returns>
 221        public override long Seek(long offset, SeekOrigin origin)
 0222        {
 0223            switch (origin)
 224            {
 225                case SeekOrigin.Begin:
 0226                    Position = offset;
 0227                    break;
 228
 229                case SeekOrigin.End:
 0230                    Position = Length - offset;
 0231                    break;
 232                case SeekOrigin.Current:
 233                default:
 0234                    Position += offset;
 0235                    break;
 236            }
 237
 0238            return Position;
 0239        }
 240
 241        /// <summary>
 242        ///     Arbitrarily set the internal length of the <see cref="CoalesceStream" />
 243        /// </summary>
 244        /// <param name="value">The new length value.</param>
 245        public sealed override void SetLength(long value)
 0246        {
 0247            m_Length = value;
 0248        }
 249
 250        /// <summary>
 251        ///     Write into the <see cref="CoalesceStream" /> at the current position.
 252        /// </summary>
 253        /// <param name="buffer">The source array to read data from</param>
 254        /// <param name="offset">An offset of where to start in the <paramref name="buffer" />.</param>
 255        /// <param name="count">The number of bytes to read from the <paramref name="buffer" />.</param>
 256        public sealed override void Write(byte[] buffer, int offset, int count)
 2257        {
 2258            long initialPosition = Position;
 259            try
 2260            {
 261                do
 2262                {
 2263                    int copySize = Math.Min(count, (int)(m_BucketSize - BucketOffset));
 2264                    long intendedLength = Position + copySize;
 2265                    if (intendedLength > m_Length)
 2266                    {
 2267                        m_Length = intendedLength;
 2268                    }
 269
 2270                    Buffer.BlockCopy(buffer, offset, Bucket, (int)BucketOffset, copySize);
 2271                    count -= copySize;
 2272                    offset += copySize;
 273
 2274                    Position += copySize;
 4275                } while (count > 0);
 2276            }
 0277            catch (Exception)
 0278            {
 0279                Position = initialPosition;
 0280                throw;
 281            }
 2282        }
 283
 284        /// <summary>
 285        ///     Read a singular <see cref="byte" /> from the current position, incrementing the position.
 286        /// </summary>
 287        /// <returns>A valid byte as an int, or -1.</returns>
 288        public override int ReadByte()
 2289        {
 2290            if (Position >= m_Length)
 1291            {
 1292                return -1;
 293            }
 294
 1295            byte b = Bucket[BucketOffset];
 1296            Position++;
 297
 1298            return b;
 2299        }
 300
 301        /// <summary>
 302        ///     Write a singular <see cref="byte" /> to the <see cref="CoalesceStream" />, incrementing the position.
 303        /// </summary>
 304        /// <param name="value">The <see cref="byte" /> to write to the <see cref="CoalesceStream" />.</param>
 305        public override void WriteByte(byte value)
 0306        {
 0307            long intendedLength = Position + 1;
 0308            if (intendedLength > m_Length)
 0309            {
 0310                m_Length = intendedLength;
 0311            }
 312
 0313            Bucket[BucketOffset] = value;
 0314            Position++;
 0315        }
 316    }
 317}