aboutsummaryrefslogtreecommitdiff
path: root/cs-dotnet/src/gatsstream.cs
blob: 742f0b429ec44b7dcebec6733f3fd47d2845d22b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * Copyright (C) 2007-2012 Xagasoft, All rights reserved.
 *
 * This file is part of the libgats library and is released under the
 * terms of the license contained in the file LICENSE.
 */

using System.IO;
using System.Net;
using System;

namespace Com.Xagasoft.Gats
{
    /// <summary>
    /// Main I/O interface.  Use this to read and write GATS.
    /// </summary>
    /// <remarks>
    /// GatsStream is not a traditional Stream class, it only can read and
    /// write complete GATS packets.  A GATS packet consists of a version,
    /// size, and a single encoded GATS object (which can be a container).
    ///
    /// This one class handles both reading and writing.  Writing is fairly
    /// straight forward, but for volatile streams like network sockets you'll
    /// probably want to wrap your stream with a buffer before handing it to
    /// GatsStream.
    ///
    /// Reading handles it's own buffering, and will remember what has been
    /// read between calls if it cannot get enough data at once to complete
    /// a single packet.  See the ReadObject docs for more details.
    /// </remarks>
    public class GatsStream
    {
        private Stream s;
        private MemoryStream ReadBuf;
        private int size = -1;
        private int version;
        private BinaryWriter bw = null;
        private BinaryReader br = null;

        /// <summary>
        /// Construct a new GatsStream around a given Stream class.
        /// </summary>
        /// <remarks>
        /// The provided Stream does not need to be opened for both reading and
        /// writing, you can construct a GatsStream around a read only or
        /// write only stream.
        /// </remarks>
        /// <param name="s">Stream to operate on.</param>
        public GatsStream( Stream s )
        {
            this.s = s;
            this.ReadBuf = new MemoryStream();
        }

        /// <summary>
        /// Read a GATS packet and return the contained GatsObject.
        /// </summary>
        /// <remarks>
        /// This reads a complete packet into an internal buffer, parses the
        /// gats object within it, and returns it.  This method will read as
        /// much data as it needs from the stream, but no more.  Not a single
        /// extra byte will be read.
        ///
        /// In the event that an end of stream is encountered, or a partial
        /// packet has been read but no more data can be read at the moment,
        /// this method will maintain it's buffer but return null to the caller.
        /// </remarks>
        /// <returns>
        /// The read object, or null if no object could be read yet.
        /// </returns>
        public GatsObject ReadObject()
        {
            if( size == -1 )
            {
                for(;;)
                {
                    version = s.ReadByte();
                    if( version == -1 )
                        return null;
                    if( version > 0 )
                        break;
                }
            }

            if( br == null )
                this.br = new BinaryReader( s );

            switch( version )
            {
                case 1:
                    // Verion 1 of gats
                    if( size == -1 )
                        size = IPAddress.NetworkToHostOrder( br.ReadInt32() )-5;
                    byte[] buf = new byte[4096];
                    while( ReadBuf.Length < size )
                    {
                        int goal = (int)(size-ReadBuf.Length);
                        if( goal > 4096 )
                            goal = 4096;
                        int amnt = s.Read( buf, 0, goal );
                        if( amnt <= 0 )
                            return null;
                        ReadBuf.Write( buf, 0, amnt );
                    }
                    ReadBuf.Seek( 0, SeekOrigin.Begin );
                    GatsObject ret = GatsObject.Read( ReadBuf );
                    ReadBuf.SetLength( 0 );
                    size = version = -1;
                    return ret;
            }

            return null;
        }

        /// <summary>
        /// Write a GatsObject to the stream in a proper GATS packet.
        /// </summary>
        /// <remarks>
        /// The object passed in is first written to an internal buffer, and
        /// then to the output in several write operations.  This means that
        /// for many applications you will want to buffer the output.  In the
        /// case of writing to files, it could be faster to buffer the output.
        /// In the case of writing to a socket buffering could prevent sending
        /// many small packets.
        ///
        /// No flushing is done by this method, it is left to the caller to
        /// determine the proper time to flush their streams.
        /// </remarks>
        /// <param name="obj">The object to be written.</param>
        public void WriteObject( GatsObject obj )
        {
            MemoryStream ms = new MemoryStream();
            obj.Write( ms );

            if( bw == null )
                this.bw = new BinaryWriter( s );
            bw.Write( (byte)1 );
            bw.Write( IPAddress.HostToNetworkOrder( (int)ms.Length+5 ) );
            bw.Write( ms.GetBuffer(), 0, (int)ms.Length );
        }
    }
}