Warning - this is all unsynchronized here - receivers must implement synchronization.
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final ByteCacheCache supplying the fragment buffers.private booleanTrue once all fragments are in.private final I2PAppContextThe app context.private final longWhen the reassembly started.private SimpleTimer2.TimedEventTimer dropping the incomplete reassembly.private final ByteArray[]The fragments received so far.private intHighest fragment number received.private booleanTrue once the final fragment arrived.private final LogLogger for the fragmented message.private final longId of the message being reassembled.private longWhen the reassembly is dropped if incomplete.private HashThe router the message goes to.private TunnelIdThe tunnel the message goes through.private static final intLargest fragment, in bytes.private static final intMost fragments in one message, even 32 is unlikely.Constructor Summary
ConstructorsConstructorDescriptionFragmentedMessage(I2PAppContext ctx, long messageId) Starts an empty reassembly buffer for one message id.Method Summary
Modifier and TypeMethodDescription(package private) static intcountFragments(ByteArray[] fragments, int highFragmentNum) Count the non-null fragment slots from 0 through the high-water mark.voidfailed()Releases the fragments, marking the message as failed.intThe size of the complete message, in bytes.Used in the fragment handler so we can cancel the expire event on success.intThe number of fragments received so far.longHow long has this fragmented message been alive?longThe unique ID of this fragmented message.booleanWhether this message has been released after completion.longThe lifetime at which the fragments were released, or 0 if not yet released.The router this message is being sent to.The tunnel this message is being sent to.booleanHave we received all of the fragments?booleanReceive the first fragment (#0) and related metadata.booleanreceive(int fragmentNum, byte[] payload, int offset, int length, boolean isLast) Receive a followup fragment, though one of these may arrive at the endpoint prior to the fragment # 0.private voidCalled as one of the endpoints for the tunnel cache pipeline (see TunnelDataMessage).voidThe expire event to cancel when this message completes.byte[]The complete reassembled message data, freeing the fragments.toString()Debug description of the message ID and the received fragments.private voidwriteComplete(byte[] target, int offset) Writes the reassembled message to the buffer.
Field Details
_context
The app context._log
Logger for the fragmented message._messageId
private final long _messageIdId of the message being reassembled._toRouter
The router the message goes to._toTunnel
The tunnel the message goes through._fragments
The fragments received so far._lastReceived
private volatile boolean _lastReceivedTrue once the final fragment arrived._highFragmentNum
private volatile int _highFragmentNumHighest fragment number received._createdOn
private final long _createdOnWhen the reassembly started._completed
private volatile boolean _completedTrue once all fragments are in._releasedAfter
private volatile long _releasedAfterWhen the reassembly is dropped if incomplete._expireEvent
Timer dropping the incomplete reassembly._cache
Cache supplying the fragment buffers.MAX_FRAGMENTS
private static final int MAX_FRAGMENTSMost fragments in one message, even 32 is unlikely.- See Also:
MAX_FRAGMENT_SIZE
Constructor Details
FragmentedMessage
Starts an empty reassembly buffer for one message id.- Parameters:
ctx- the context supplying the clock and log used by this messagemessageId- the id that ties the fragments of one message together
Method Details
receive
public boolean receive(int fragmentNum, byte[] payload, int offset, int length, boolean isLast) Receive a followup fragment, though one of these may arrive at the endpoint prior to the fragment # 0.- Parameters:
fragmentNum- sequence number within the message (1 - 63)payload- data for the fragment non-nulloffset- index into the payload where the fragment data starts (past headers/etc)length- how much past the offset should we snag?isLast- is this the last fragment in the message?- Returns:
- true if the fragment was stored, false if the number, the length or the offset range was out of bounds
receive
public boolean receive(byte[] payload, int offset, int length, boolean isLast, Hash toRouter, TunnelId toTunnel) Receive the first fragment (#0) and related metadata. This may not be the first one to arrive at the endpoint however.- Parameters:
payload- data for the fragment non-nulloffset- index into the payload where the fragment data starts (past headers/etc)length- how much past the offset should we snag?isLast- is this the last fragment in the message?toRouter- what router is this destined for (may be null)toTunnel- what tunnel is this destined for (may be null)- Returns:
- true if the fragment was stored, false if the length or the offset range was out of bounds
getMessageId
public long getMessageId()The unique ID of this fragmented message.- Returns:
- the message id
getTargetRouter
getTargetTunnel
getFragmentCount
public int getFragmentCount()The number of fragments received so far.- Returns:
- the fragment count
countFragments
Count the non-null fragment slots from 0 through the high-water mark.Slots above
highFragmentNumcannot be non-null: bothreceive(int, byte[], int, int, boolean)overloads raise the mark to at least the index they store into, and the mark is never lowered, so scanning past it cannot add to the count. Bounding the scan therefore costs nothing in accuracy while skipping the unused tail of the array, and it makes this accessor agree withisComplete(),getCompleteSize(),writeComplete(byte[], int)andreleaseFragments(), all of which already stop at the mark.Static and driven by its parameters so the scan can be exercised without an I2PAppContext. The high-water mark is passed in rather than read in the loop condition: it is only ever increased, and passing it as an argument reads the volatile field exactly once instead of on every iteration.
- Parameters:
fragments- fragment slots, indexed by fragment number; not nullhighFragmentNum- highest fragment number received, or -1 if none received- Returns:
- the number of non-null slots in 0..highFragmentNum, zero if none received
- Throws:
NullPointerException- iffragmentsis null and the range to scan is non-empty
getExpireEvent
Used in the fragment handler so we can cancel the expire event on success.- Returns:
- the scheduled expiry event, or null if none has been set
setExpireEvent
The expire event to cancel when this message completes.- Parameters:
evt- the timer event to cancel on completion
isComplete
public boolean isComplete()Have we received all of the fragments?- Returns:
- true if the last fragment has been seen and no slot up to the high-water mark is still empty
getCompleteSize
public int getCompleteSize()The size of the complete message, in bytes.- Returns:
- the complete size
getLifetime
public long getLifetime()How long has this fragmented message been alive?- Returns:
- the milliseconds elapsed since construction
getReleased
public boolean getReleased()Whether this message has been released after completion.- Returns:
- true once every fragment has been written out to the caller's buffer, after which this message must not be read again
writeComplete
private void writeComplete(byte[] target, int offset) Writes the reassembled message to the buffer.- Parameters:
target- the buffer to write intooffset- where to start writing
toByteArray
public byte[] toByteArray()The complete reassembled message data, freeing the fragments.- Returns:
- the concatenated fragment data, or null if the fragments have already been released
getReleasedAfter
public long getReleasedAfter()The lifetime at which the fragments were released, or 0 if not yet released.- Returns:
- the released after
failed
public void failed()Releases the fragments, marking the message as failed.releaseFragments
private void releaseFragments()Called as one of the endpoints for the tunnel cache pipeline (see TunnelDataMessage).toString