// $Id$ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // using System; using System.Diagnostics; using System.IO; namespace Org.Apache.Etch.Bindings.Csharp.Util { /// /// A FlexBuffer wraps a byte array and manages the active region of /// it (0..length). It also supports dynamically extending the buffer /// as needed. /// /// A FlexBuffer also has an index (read or write cursor). The various /// Get and put operations always occur at the current index, with the /// index adjusted appropriately afterward. Get will not move index past /// length. If put needs to move index past length, length is also /// adjusted. This may cause the byte array to be re-allocated to a /// larger size. /// public sealed class FlexBuffer { /// /// Constructs a FlexBuffer with initial length and index of 0. /// public FlexBuffer() : this(new byte[INIT_BUFFER_LEN], 0, 0) { } /// /// Constructs the FlexBuffer out of an existing buffer, ready to /// read, with the index set to 0 and length set to buffer.length. /// The buffer is adopted, not copied. If you want to copy, use one /// of the put methods instead. /// /// Note: this is the same as FlexBuffer( buffer, 0, buffer.length ). /// /// the buffer to adopt. /// public FlexBuffer(byte[] buffer) : this(buffer, 0, buffer.Length) { } /// /// Constructs the FlexBuffer out of an existing buffer, ready to /// read, with the index set to 0 and length set to buffer.length. /// The buffer is adopted, not copied. If you want to copy, use one /// of the put methods instead. /// /// Note: this is the same as FlexBuffer( buffer, 0, length ). /// /// the buffer to adopt. /// the length of the data in the buffer (data presumed /// to start at 0). /// public FlexBuffer(byte[] buffer, int length) : this(buffer, 0, length) { } /// /// Constructs the FlexBuffer out of an existing buffer, ready to /// read, with the index set to 0 and length set to buffer.length. /// The buffer is adopted, not copied. If you want to copy, use one /// of the put methods instead. /// /// Note: if you want to start off writing to the end of the buffer /// and not reading it, specify index as the place to start writing, /// and length as the amount of data that is valid after index ( which /// might reasonably be 0 ) /// /// the buffer to adopt. /// /// the index to start reading or writing. /// the length of the valid data in the buffer, /// starting at the index. /// public FlexBuffer(byte[] buffer, int index, int length ) { if ( buffer == null ) throw new NullReferenceException( "buffer == null" ); if ( index < 0 ) throw new ArgumentException( " index < 0 " ); if ( length < 0 ) throw new ArgumentException( "length < 0 " ); if ( index + length > buffer.Length ) throw new ArgumentException( "index + length > buffer.Length" ); this.buffer = buffer; this.index = index; this.length = index + length; } /// /// /// /// the current byte array. Might change if any operation /// needs to extend length past the end of the array. public byte[] GetBuf() { return buffer; } /// /// /// /// /// Exception: /// throws IOException private void EnsureLength( int len ) { int n = buffer.Length; if (len <= n) return; // the buffer is not big enough, expand it int k = n; if ( k < INIT_BUFFER_LEN ) k = INIT_BUFFER_LEN; while ( len > k && k < MAX_BUFFER_LEN ) k = k << 1; if (len > k) throw new IOException( "buffer overflow" ); byte[] b = new byte[k]; Array.Copy( buffer, 0, b, 0, n ); buffer = b; } private byte[] buffer; private const int INIT_BUFFER_LEN = 32; private const int TRIM_BUFFER_LEN = 16*1024; private const int MAX_BUFFER_LEN = 4*1024*1024; /// /// /// /// the current value of length. This is the last /// index of valid data in the buffer. public int Length() { return length; } /// /// /// /// length the new value of length. Length must be >= 0. /// If length is less than index, index is also set to length. If length /// is larger than the current buffer, the buffer is expanded. /// this flex buffer object. /// Exception: /// throws ArgumentOutOfRangeException, IOException public FlexBuffer SetLength( int length ) { if (length < 0) throw new ArgumentOutOfRangeException( "length < 0" ); EnsureLength( length ); this.length = length; if (index > length) index = length; return this; } private int length; /// /// /// /// the current value of index. public int Index() { return index; } /// /// /// /// index the new value of index. Index must be >= 0. /// this flex buffer object. /// Exception: /// throws ArgumentOutOfRangeException public FlexBuffer SetIndex( int index ) { if (index < 0 || index > length) throw new ArgumentOutOfRangeException( "index < 0 || index > length" ); this.index = index; return this; } private int index; /// /// /// /// length() - index(). Result is always >= 0. This is the amount /// of data that could be read using the various forms of Get. It doesn't /// really mean anything in relation to put. public int Avail() { return length - index; } /// /// Sets both length and index to 0. /// /// Shorthand for SetLength( 0 ). /// /// this flex buffer object. public FlexBuffer Reset() { index = 0; length = 0; if (buffer.Length > TRIM_BUFFER_LEN) buffer = new byte[TRIM_BUFFER_LEN]; return this; } /// /// Compacts the buffer by moving remaining data (from index to length) /// to the front of the buffer. Sets index to 0, and sets length to /// Avail (before index was changed). /// /// this flex buffer object. public FlexBuffer Compact() { if(index == 0) return this; int n = Avail(); if(n == 0) { Reset(); return this; } Array.Copy(buffer, index, buffer, 0, n); index = 0; length = n; return this; } /// /// /// /// the byte value at index, and adjusts index /// by adding one. /// Exception: /// End of File exception / NullReferenceException public int Get() { CheckAvail( 1 ); return buffer[index++] & 255; } /// /// Copies data from the byte array to buf as if by repeated /// calls to Get(). /// /// a buffer to receive the data. At most /// min( buf.length, Avail() ) bytes are transferred. /// the amount of data transferred. /// Exception: /// End of File exception / NullReferenceException public int Get( byte[] buf ) { return Get( buf, 0, buf.Length ); } /// /// Copies data from the byte array to buf as if by repeated /// calls to Get(). /// /// a buffer to receive the data. At most /// min( len, Avail() ) bytes are transferred, starting /// at off. /// the index in buf to receive the data. /// off must be >= 0 && less than or equal to buf.length. /// the max amount of data to transfer. Len /// must be >= 0 and less than or equal to buf.length - off. /// the amount of data transferred. /// Exception: /// End of File exception / NullReferenceException public int Get( byte[] buf, int off, int len ) { CheckBuf( buf, off, len ); if (len == 0) return 0; CheckAvail( 1 ); int n = Math.Min( len, Avail() ); // Array.Copy( buffer, index, buf, off, n ); Buffer.BlockCopy(buffer, index, buf, off, n); index += n; return n; } public sbyte GetByte() { CheckAvail( 1 ); return (sbyte)buffer[ index++ ]; } public readonly static bool littleEndian = false; /// /// /// /// a short composed from the next 2 bytes. Little-endian. /// Exception: /// throws Exception if avail() < 2 public short GetShort() { CheckAvail( 2 ); if ( littleEndian ) { // little-endian int value = buffer[ index++ ] & 255; return ( short ) ( value + ( ( buffer[ index++ ] & 255 ) << 8 ) ); } else { // big-endian int value = buffer[ index++ ]; return ( short ) ( ( value << 8 ) + ( buffer[ index++ ] & 255 ) ); } } /// /// /// /// an integer composed of 4 bytes from /// the buffer, with the first byte being the least /// significant and the last being the most significant. /// Exception: /// End of File exception / NullReferenceException public int GetInt() { CheckAvail( 4 ); if ( littleEndian ) { // little-endian int value = buffer[ index++ ] & 255; value += ( ( buffer[ index++ ] & 255 ) << 8 ); value += ( ( buffer[ index++ ] & 255 ) << 16 ); return value + ( ( buffer[ index++ ] & 255 ) << 24 ); } else { // big-endian int value = buffer[ index++ ]; value = ( value << 8 ) + ( buffer[ index++ ] & 255 ); value = ( value << 8 ) + ( buffer[ index++ ] & 255 ); return ( value << 8 ) + ( buffer[ index++ ] & 255 ); } } /// /// /// /// a long comprised of the next 8 bytes. Little-endian public long GetLong() { CheckAvail( 8 ); if ( littleEndian ) { // little-endian long value = buffer[ index++ ] & 255; value += ( ( ( long ) ( buffer[ index++ ] & 255 ) ) << 8 ); value += ( ( ( long ) ( buffer[ index++ ] & 255 ) ) << 16 ); value += ( ( ( long ) ( buffer[ index++ ] & 255 ) ) << 24 ); value += ( ( ( long ) ( buffer[ index++ ] & 255 ) ) << 32 ); value += ( ( ( long ) ( buffer[ index++ ] & 255 ) ) << 40 ); value += ( ( ( long ) ( buffer[ index++ ] & 255 ) ) << 48 ); return value + ( ( ( long ) ( buffer[ index++ ] & 255 ) ) << 56 ); } else { // big-endian long value = buffer[ index++ ]; value = ( value << 8 ) + ( buffer[ index++ ] & 255 ); value = ( value << 8 ) + ( buffer[ index++ ] & 255 ); value = ( value << 8 ) + ( buffer[ index++ ] & 255 ); value = ( value << 8 ) + ( buffer[ index++ ] & 255 ); value = ( value << 8 ) + ( buffer[ index++ ] & 255 ); value = ( value << 8 ) + ( buffer[ index++ ] & 255 ); return ( value << 8 ) + ( buffer[ index++ ] & 255 ); } } /// /// /// /// a float from the next available bytes public float GetFloat() { return BitConverter.ToSingle( BitConverter.GetBytes( GetInt() ), 0 ); } /// /// /// /// a double from the next available bytes public double GetDouble() { return BitConverter.Int64BitsToDouble( GetLong() ); } public void GetFully( byte[] b ) { CheckAvail( b.Length ); int n = Get( b, 0, b.Length ); Debug.Assert( n == b.Length ); } /// /// Puts a byte into the buffer at the current index, /// then adjusts the index by one. Adjusts the length /// as necessary. The buffer is expanded if needed. /// /// byte to be put /// this flex buffer object. /// Exception: /// IOException if the buffer overflows its max length. public FlexBuffer Put( int b ) { EnsureLength( index+1 ); buffer[index++] = (byte) b; FixLength(); return this; } /// /// Puts some bytes into the buffer as if by repeated /// calls to put(). /// /// the source of the bytes to put. The entire /// array of bytes is put. /// flex buffer object. /// Exception: /// IOException if the buffer overflows its max length. public FlexBuffer Put( byte[] buf ) { return Put( buf, 0, buf.Length ); } /// /// Puts some bytes into the buffer as if by repeated /// calls to Put(). /// /// the source of the bytes to put. /// the index to the first byte to put. /// the number of bytes to put. /// flex buffer object. /// Exception: /// IOException if the buffer overflows its max length. public FlexBuffer Put( byte[] buf, int off, int len ) { CheckBuf( buf, off, len ); if (len == 0) return this; EnsureLength( index+len ); // Array.Copy( buf, off, buffer, index, len ); Buffer.BlockCopy(buf, off, buffer, index, len); index += len; FixLength(); return this; } /// /// Copies the Available bytes from buf into buffer as if by /// repeated execution of put( buf.Get() ). /// /// the source of the bytes to put. All Available /// bytes are copied. /// flex buffer object. /// Exception: /// IOException if the buffer overflows its max length. public FlexBuffer Put( FlexBuffer buf ) { int n = buf.Avail(); Put( buf.buffer, buf.index, n ); buf.Skip( n, false ); return this; } /// /// Copies the specified number of bytes from buf into buffer /// as if by repeated execution of Put( buf.Get() ). /// /// the source of the bytes to put. /// len the number of bytes to put. Len must be /// less than or equal to buf.Avail(). /// flex buffer object. /// Exception: /// IOException if the buffer overflows its max length. public FlexBuffer Put( FlexBuffer buf, int len ) { if (len > buf.Avail()) throw new ArgumentOutOfRangeException( "len > buf.Avail()" ); Put( buf.buffer, buf.index, len ); buf.Skip( len, false ); return this; } public void PutByte( byte value ) { EnsureLength( index + 1 ); buffer[ index++ ] = ( byte ) value; FixLength(); } public void PutByte( sbyte value ) { EnsureLength( index + 1 ); buffer[ index++ ] = ( byte ) value; FixLength(); } /// /// Put short as the next 2 bytes. Little-endian /// /// public void PutShort( short value ) { EnsureLength( index + 2 ); if ( littleEndian ) { buffer[ index++ ] = ( byte ) value; buffer[ index++ ] = ( byte ) ( value >> 8 ); } else { /// In C#, since we're using the byte (which is unsigned), /// it doesn't matter whether you do logical or arithmetic /// shift. Hence, an equivalent of the Java >>> operator is /// not required here. buffer[ index++ ] = ( byte ) ( value >> 8 ); buffer[ index++ ] = ( byte ) value; } FixLength(); } public void PutInt( int value ) { EnsureLength( length + 4 ); if ( littleEndian ) { buffer[ index++ ] = ( byte ) value; buffer[ index++ ] = ( byte ) ( value >> 8 ); buffer[ index++ ] = ( byte ) ( value >> 16 ); buffer[ index++ ] = ( byte ) ( value >> 24 ); } else { buffer[ index++ ] = ( byte ) ( value >> 24 ); buffer[ index++ ] = ( byte ) ( value >> 16 ); buffer[ index++ ] = ( byte ) ( value >> 8 ); buffer[ index++ ] = ( byte ) value; } FixLength(); } public void PutLong( long value ) { EnsureLength( index+8 ); if ( littleEndian ) { buffer[ index++ ] = ( byte ) value; buffer[ index++ ] = ( byte ) ( value >> 8 ); buffer[ index++ ] = ( byte ) ( value >> 16 ); buffer[ index++ ] = ( byte ) ( value >> 24 ); buffer[ index++ ] = ( byte ) ( value >> 32 ); buffer[ index++ ] = ( byte ) ( value >> 40 ); buffer[ index++ ] = ( byte ) ( value >> 48 ); buffer[ index++ ] = ( byte ) ( value >> 56 ); } else { buffer[ index++ ] = ( byte ) ( value >> 56 ); buffer[ index++ ] = ( byte ) ( value >> 48 ); buffer[ index++ ] = ( byte ) ( value >> 40 ); buffer[ index++ ] = ( byte ) ( value >> 32 ); buffer[ index++ ] = ( byte ) ( value >> 24 ); buffer[ index++ ] = ( byte ) ( value >> 16 ); buffer[ index++ ] = ( byte ) ( value >> 8 ); buffer[ index++ ] = ( byte ) value; } FixLength(); } public void PutFloat( float value ) { PutInt( BitConverter.ToInt32( BitConverter.GetBytes( value ), 0 ) ); } public void PutDouble( double value ) { PutLong( BitConverter.DoubleToInt64Bits( value ) ); } /// /// Adjusts index as if by a Get or put but without transferring /// any data. This could be used to skip over a data item in an /// input or output buffer. /// /// len the number of bytes to skip over. Len must be /// greater than or equal to 0. If put is false, it is an error if len >Avail(). /// If put is true, the buffer /// may be extended (and the buffer length adjusted). /// put if true it is ok to extend the length of the /// buffer. /// this Flexbuffer object. /// Exception: /// IOException public FlexBuffer Skip( int len, bool put ) { if (len < 0) throw new ArgumentException( "count < 0" ); if (len == 0) return this; if (put) { EnsureLength( index+len ); index += len; FixLength(); return this; } CheckAvail( len ); index += len; return this; } /// /// If index has moved past length during a put, then adjust length /// to track index. /// private void FixLength() { if(index > length) length = index; } private void CheckBuf(byte[] buf, int off, int len) { if(buf == null) throw new NullReferenceException("buf == null"); if(off < 0 || off > buf.Length) throw new ArgumentOutOfRangeException("off < 0 || off > buf.length"); if(len < 0) throw new ArgumentOutOfRangeException("len < 0"); if(off+len > buf.Length) throw new ArgumentOutOfRangeException("off+len > buf.length"); } /// /// Return the currently Available bytes. /// /// the currently Available bytes. /// Exception: /// throws an IO Exception public byte[] GetAvailBytes() { byte[] buf = new byte[Avail()]; Get( buf ); return buf; } /// /// Checks that there are enough bytes to for a read. /// /// the length required by a read operation. private void CheckAvail( int len ) { if ( len > Avail() ) throw new EndOfStreamException( " len > Avail() " ); } } }