capnproto-java-rpc/runtime/src/main/java/org/capnproto/Serialize.java

137 lines
5 KiB
Java
Raw Normal View History

2014-10-08 20:20:15 +00:00
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
2014-05-24 18:39:47 +00:00
package org.capnproto;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
2014-09-03 21:21:44 +00:00
import java.nio.channels.WritableByteChannel;
2014-05-24 18:39:47 +00:00
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
2014-05-24 18:39:47 +00:00
public final class Serialize {
2014-06-30 14:30:04 +00:00
static ByteBuffer makeByteBuffer(int bytes) {
ByteBuffer result = ByteBuffer.allocate(bytes);
result.order(ByteOrder.LITTLE_ENDIAN);
result.mark();
return result;
}
public static void fillBuffer(ByteBuffer buffer, ReadableByteChannel bc) throws IOException {
while(buffer.hasRemaining()) {
int r = bc.read(buffer);
if (r < 0) {
throw new IOException("premature EOF");
}
// TODO check for r == 0 ?.
}
}
public static MessageReader read(ReadableByteChannel bc) throws IOException {
2014-10-08 19:16:17 +00:00
return read(bc, MessageReader.DEFAULT_TRAVERSAL_LIMIT_IN_WORDS, MessageReader.DEFAULT_NESTING_LIMIT);
}
public static MessageReader read(ReadableByteChannel bc, long traversalLimitInWords, int nestingLimit) throws IOException {
ByteBuffer firstWord = makeByteBuffer(Constants.BYTES_PER_WORD);
fillBuffer(firstWord, bc);
int segmentCount = 1 + firstWord.getInt(0);
int segment0Size = 0;
if (segmentCount > 0) {
segment0Size = firstWord.getInt(4);
}
int totalWords = segment0Size;
if (segmentCount > 512) {
throw new IOException("too many segments");
}
// in words
ArrayList<Integer> moreSizes = new ArrayList<Integer>();
if (segmentCount > 1) {
ByteBuffer moreSizesRaw = makeByteBuffer(4 * (segmentCount & ~1));
fillBuffer(moreSizesRaw, bc);
for (int ii = 0; ii < segmentCount - 1; ++ii) {
int size = moreSizesRaw.getInt(ii * 4);
moreSizes.add(size);
totalWords += size;
}
}
// TODO check that totalWords is reasonable
2014-10-08 19:16:17 +00:00
if (totalWords > traversalLimitInWords) {
throw new DecodeException("Message size exceeds traversal limit.");
}
ByteBuffer allSegments = makeByteBuffer(totalWords * Constants.BYTES_PER_WORD);
fillBuffer(allSegments, bc);
ByteBuffer[] segmentSlices = new ByteBuffer[segmentCount];
allSegments.rewind();
segmentSlices[0] = allSegments.slice();
segmentSlices[0].limit(segment0Size * Constants.BYTES_PER_WORD);
segmentSlices[0].order(ByteOrder.LITTLE_ENDIAN);
int offset = segment0Size;
for (int ii = 1; ii < segmentCount; ++ii) {
allSegments.position(offset * Constants.BYTES_PER_WORD);
segmentSlices[ii] = allSegments.slice();
segmentSlices[ii].limit(moreSizes.get(ii - 1) * Constants.BYTES_PER_WORD);
segmentSlices[ii].order(ByteOrder.LITTLE_ENDIAN);
offset += moreSizes.get(ii - 1);
}
2014-10-08 19:16:17 +00:00
return new MessageReader(segmentSlices, traversalLimitInWords, nestingLimit);
}
public static void write(WritableByteChannel outputChannel,
MessageBuilder message) throws IOException {
2014-05-24 18:39:47 +00:00
ByteBuffer[] segments = message.getSegmentsForOutput();
int tableSize = (segments.length + 2) & (~1);
ByteBuffer table = ByteBuffer.allocate(4 * tableSize);
table.order(ByteOrder.LITTLE_ENDIAN);
table.putInt(0, segments.length - 1);
for (int i = 0; i < segments.length; ++i) {
table.putInt(4 * (i + 1), segments[i].limit() / 8);
}
// Any padding is already zeroed.
2014-06-30 14:30:04 +00:00
while (table.hasRemaining()) {
outputChannel.write(table);
}
2014-05-24 18:39:47 +00:00
2014-09-03 21:21:44 +00:00
for (ByteBuffer buffer : segments) {
while(buffer.hasRemaining()) {
outputChannel.write(buffer);
}
2014-06-30 14:30:04 +00:00
}
2014-05-24 18:39:47 +00:00
}
}