| /* |
| * |
| * 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 Qpid.Buffer; |
| |
| namespace Qpid.Codec |
| { |
| public abstract class CumulativeProtocolDecoder : IProtocolDecoder |
| { |
| ByteBuffer _remaining; |
| |
| /// <summary> |
| /// Creates a new instance with the 4096 bytes initial capacity of |
| /// cumulative buffer. |
| /// </summary> |
| protected CumulativeProtocolDecoder() |
| { |
| _remaining = ByteBuffer.allocate(4096); |
| _remaining.setAutoExpand(true); |
| } |
| |
| /// <summary> |
| /// Cumulates content of <tt>in</tt> into internal buffer and forwards |
| /// decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}. |
| /// <tt>doDecode()</tt> is invoked repeatedly until it returns <tt>false</tt> |
| /// and the cumulative buffer is compacted after decoding ends. |
| /// </summary> |
| /// <exception cref="Exception"> |
| /// if your <tt>doDecode()</tt> returned <tt>true</tt> not consuming the cumulative buffer. |
| /// </exception> |
| public void Decode(ByteBuffer input, IProtocolDecoderOutput output) |
| { |
| if (_remaining.position() != 0) // If there were remaining undecoded bytes |
| { |
| DecodeRemainingAndInput(input, output); |
| } |
| else |
| { |
| DecodeInput(input, output); |
| } |
| } |
| |
| private void DecodeInput(ByteBuffer input, IProtocolDecoderOutput output) |
| { |
| // Just decode the input buffer and remember any remaining undecoded bytes. |
| try |
| { |
| DecodeAll(input, output); |
| } |
| finally |
| { |
| if (input.hasRemaining()) |
| { |
| _remaining.put(input); |
| } |
| } |
| } |
| |
| private void DecodeRemainingAndInput(ByteBuffer input, IProtocolDecoderOutput output) |
| { |
| // Concatenate input buffer with left-over bytes. |
| _remaining.put(input); |
| _remaining.flip(); |
| |
| try |
| { |
| DecodeAll(_remaining, output); |
| } |
| finally |
| { |
| _remaining.compact(); |
| } |
| } |
| |
| private void DecodeAll(ByteBuffer buf, IProtocolDecoderOutput output) |
| { |
| for (;;) |
| { |
| int oldPos = buf.position(); |
| bool decoded = DoDecode(buf, output); |
| if (decoded) |
| { |
| if (buf.position() == oldPos) |
| { |
| throw new Exception( |
| "doDecode() can't return true when buffer is not consumed."); |
| } |
| |
| if (!buf.hasRemaining()) |
| { |
| break; |
| } |
| } |
| else |
| { |
| break; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Implement this method to consume the specified cumulative buffer and |
| /// decode its content into message(s). |
| /// </summary> |
| /// <param name="input">the cumulative buffer</param> |
| /// <param name="output">decoder output</param> |
| /// <returns> |
| /// <tt>true</tt> if and only if there's more to decode in the buffer |
| /// and you want to have <tt>doDecode</tt> method invoked again. |
| /// Return <tt>false</tt> if remaining data is not enough to decode, |
| /// then this method will be invoked again when more data is cumulated. |
| /// </returns> |
| /// <exception cref="Exception">If cannot decode</exception> |
| protected abstract bool DoDecode(ByteBuffer input, IProtocolDecoderOutput output); |
| |
| public void Dispose() |
| { |
| _remaining = null; |
| } |
| } |
| } |