From d5839d2b4d45a6af02fe5852ace768ab3d40b5ff Mon Sep 17 00:00:00 2001 From: Jisi Liu Date: Tue, 3 Feb 2015 18:15:12 -0800 Subject: parsing and serialzation for maps in JavaNano. --- .../protobuf/nano/CodedOutputByteBufferNano.java | 124 +++++++++++++ .../java/com/google/protobuf/nano/MapUtil.java | 193 ++++++++++++++++++--- 2 files changed, 289 insertions(+), 28 deletions(-) (limited to 'javanano/src/main') diff --git a/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java b/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java index 37982b57..2777f34c 100644 --- a/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java +++ b/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java @@ -876,4 +876,128 @@ public final class CodedOutputByteBufferNano { // Note: the right-shift must be arithmetic return (n << 1) ^ (n >> 63); } + + static int computeFieldSize(int number, int type, Object object) { + switch (type) { + case InternalNano.TYPE_BOOL: + return computeBoolSize(number, (Boolean) object); + case InternalNano.TYPE_BYTES: + return computeBytesSize(number, (byte[]) object); + case InternalNano.TYPE_STRING: + return computeStringSize(number, (String) object); + case InternalNano.TYPE_FLOAT: + return computeFloatSize(number, (Float) object); + case InternalNano.TYPE_DOUBLE: + return computeDoubleSize(number, (Double) object); + case InternalNano.TYPE_ENUM: + return computeEnumSize(number, (Integer) object); + case InternalNano.TYPE_FIXED32: + return computeFixed32Size(number, (Integer) object); + case InternalNano.TYPE_INT32: + return computeInt32Size(number, (Integer) object); + case InternalNano.TYPE_UINT32: + return computeUInt32Size(number, (Integer) object); + case InternalNano.TYPE_SINT32: + return computeSInt32Size(number, (Integer) object); + case InternalNano.TYPE_SFIXED32: + return computeSFixed32Size(number, (Integer) object); + case InternalNano.TYPE_INT64: + return computeInt64Size(number, (Long) object); + case InternalNano.TYPE_UINT64: + return computeUInt64Size(number, (Long) object); + case InternalNano.TYPE_SINT64: + return computeSInt64Size(number, (Long) object); + case InternalNano.TYPE_FIXED64: + return computeFixed64Size(number, (Long) object); + case InternalNano.TYPE_SFIXED64: + return computeSFixed64Size(number, (Long) object); + case InternalNano.TYPE_MESSAGE: + return computeMessageSize(number, (MessageNano) object); + case InternalNano.TYPE_GROUP: + return computeGroupSize(number, (MessageNano) object); + default: + throw new IllegalArgumentException("Unknown type: " + type); + } + } + + void writeField(int number, int type, Object value) + throws IOException { + switch (type) { + case InternalNano.TYPE_DOUBLE: + Double doubleValue = (Double) value; + writeDouble(number, doubleValue); + break; + case InternalNano.TYPE_FLOAT: + Float floatValue = (Float) value; + writeFloat(number, floatValue); + break; + case InternalNano.TYPE_INT64: + Long int64Value = (Long) value; + writeInt64(number, int64Value); + break; + case InternalNano.TYPE_UINT64: + Long uint64Value = (Long) value; + writeUInt64(number, uint64Value); + break; + case InternalNano.TYPE_INT32: + Integer int32Value = (Integer) value; + writeInt32(number, int32Value); + break; + case InternalNano.TYPE_FIXED64: + Long fixed64Value = (Long) value; + writeFixed64(number, fixed64Value); + break; + case InternalNano.TYPE_FIXED32: + Integer fixed32Value = (Integer) value; + writeFixed32(number, fixed32Value); + break; + case InternalNano.TYPE_BOOL: + Boolean boolValue = (Boolean) value; + writeBool(number, boolValue); + break; + case InternalNano.TYPE_STRING: + String stringValue = (String) value; + writeString(number, stringValue); + break; + case InternalNano.TYPE_BYTES: + byte[] bytesValue = (byte[]) value; + writeBytes(number, bytesValue); + break; + case InternalNano.TYPE_UINT32: + Integer uint32Value = (Integer) value; + writeUInt32(number, uint32Value); + break; + case InternalNano.TYPE_ENUM: + Integer enumValue = (Integer) value; + writeEnum(number, enumValue); + break; + case InternalNano.TYPE_SFIXED32: + Integer sfixed32Value = (Integer) value; + writeSFixed32(number, sfixed32Value); + break; + case InternalNano.TYPE_SFIXED64: + Long sfixed64Value = (Long) value; + writeSFixed64(number, sfixed64Value); + break; + case InternalNano.TYPE_SINT32: + Integer sint32Value = (Integer) value; + writeSInt32(number, sint32Value); + break; + case InternalNano.TYPE_SINT64: + Long sint64Value = (Long) value; + writeSInt64(number, sint64Value); + break; + case InternalNano.TYPE_MESSAGE: + MessageNano messageValue = (MessageNano) value; + writeMessage(number, messageValue); + break; + case InternalNano.TYPE_GROUP: + MessageNano groupValue = (MessageNano) value; + writeGroup(number, groupValue); + break; + default: + throw new IOException("Unknown type: " + type); + } + } + } diff --git a/javanano/src/main/java/com/google/protobuf/nano/MapUtil.java b/javanano/src/main/java/com/google/protobuf/nano/MapUtil.java index 8e7647dd..bc544081 100644 --- a/javanano/src/main/java/com/google/protobuf/nano/MapUtil.java +++ b/javanano/src/main/java/com/google/protobuf/nano/MapUtil.java @@ -33,6 +33,7 @@ package com.google.protobuf.nano; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; /** * Utility class for maps support. @@ -55,42 +56,178 @@ public final class MapUtil { } private static volatile MapFactory mapFactory = new DefaultMapFactory(); - @SuppressWarnings("unchecked") - public static final Map mergeEntry( - Map target, CodedInputByteBufferNano input, - int keyType, int valueType, V value, - int keyTag, int valueTag) - throws IOException { - target = mapFactory.forMap(target); - final int length = input.readRawVarint32(); - final int oldLimit = input.pushLimit(length); - K key = null; - while (true) { - int tag = input.readTag(); - if (tag == 0) { - break; + /** + * Internal utilities to implement maps for generated messages. + * Do NOT use it explicitly. + */ + public static class Internal { + private static final byte[] emptyBytes = new byte[0]; + private static Object primitiveDefaultValue(int type) { + switch (type) { + case InternalNano.TYPE_BOOL: + return Boolean.FALSE; + case InternalNano.TYPE_BYTES: + return emptyBytes; + case InternalNano.TYPE_STRING: + return ""; + case InternalNano.TYPE_FLOAT: + return Float.valueOf(0); + case InternalNano.TYPE_DOUBLE: + return Double.valueOf(0); + case InternalNano.TYPE_ENUM: + case InternalNano.TYPE_FIXED32: + case InternalNano.TYPE_INT32: + case InternalNano.TYPE_UINT32: + case InternalNano.TYPE_SINT32: + case InternalNano.TYPE_SFIXED32: + return Integer.valueOf(0); + case InternalNano.TYPE_INT64: + case InternalNano.TYPE_UINT64: + case InternalNano.TYPE_SINT64: + case InternalNano.TYPE_FIXED64: + case InternalNano.TYPE_SFIXED64: + return Long.valueOf(0L); + case InternalNano.TYPE_MESSAGE: + case InternalNano.TYPE_GROUP: + default: + throw new IllegalArgumentException( + "Type: " + type + " is not a primitive type."); } - if (tag == keyTag) { - key = (K) input.readData(keyType); - } else if (tag == valueTag) { - if (valueType == InternalNano.TYPE_MESSAGE) { - input.readMessage((MessageNano) value); + } + + /** + * Merges the map entry into the map field. Note this is only supposed to + * be called by generated messages. + * + * @param map the map field; may be null, in which case a map will be + * instantiated using the {@link MapUtil.MapFactory} + * @param input the input byte buffer + * @param keyType key type, as defined in InternalNano.TYPE_* + * @param valueType value type, as defined in InternalNano.TYPE_* + * @param valueClazz class of the value field if the valueType is + * TYPE_MESSAGE; otherwise the parameter is ignored and can be null. + * @param keyTag wire tag for the key + * @param valueTag wire tag for the value + * @return the map field + * @throws IOException + */ + @SuppressWarnings("unchecked") + public static final Map mergeEntry( + CodedInputByteBufferNano input, + Map map, + int keyType, + int valueType, + Class valueClazz, + int keyTag, + int valueTag) throws IOException { + map = mapFactory.forMap(map); + final int length = input.readRawVarint32(); + final int oldLimit = input.pushLimit(length); + byte[] payload = null; + K key = null; + V value = null; + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + if (tag == keyTag) { + key = (K) input.readData(keyType); + } else if (tag == valueTag) { + if (valueType == InternalNano.TYPE_MESSAGE) { + payload = input.readBytes(); + } else { + value = (V) input.readData(valueType); + } } else { - value = (V) input.readData(valueType); + if (!input.skipField(tag)) { + break; + } } - } else { - if (!input.skipField(tag)) { - break; + } + input.checkLastTagWas(0); + input.popLimit(oldLimit); + + if (key == null) { + key = (K) primitiveDefaultValue(keyType); + } + + // Special case: merge the value when the value is a message. + if (valueType == InternalNano.TYPE_MESSAGE) { + MessageNano oldMessageValue = (MessageNano) map.get(key); + if (oldMessageValue != null) { + if (payload != null) { + MessageNano.mergeFrom(oldMessageValue, payload); + } + return map; + } + // Otherwise, create a new value message. + try { + value = valueClazz.newInstance(); + } catch (InstantiationException e) { + throw new IOException( + "Unable to create value message " + valueClazz.getName() + + " in maps."); + } catch (IllegalAccessException e) { + throw new IOException( + "Unable to create value message " + valueClazz.getName() + + " in maps."); + } + if (payload != null) { + MessageNano.mergeFrom((MessageNano) value, payload); } } + + if (value == null) { + value = (V) primitiveDefaultValue(valueType); + } + + map.put(key, value); + return map; + } + + public static void serializeMapField( + CodedOutputByteBufferNano output, + Map map, int number, int keyType, int valueType) + throws IOException { + for (Entry entry: map.entrySet()) { + K key = entry.getKey(); + V value = entry.getValue(); + if (key == null || value == null) { + throw new IllegalStateException( + "keys and values in maps cannot be null"); + } + int entrySize = + CodedOutputByteBufferNano.computeFieldSize(1, keyType, key) + + CodedOutputByteBufferNano.computeFieldSize(2, valueType, value); + output.writeTag(number, WireFormatNano.WIRETYPE_LENGTH_DELIMITED); + output.writeRawVarint32(entrySize); + output.writeField(1, keyType, key); + output.writeField(2, valueType, value); + } } - input.checkLastTagWas(0); - input.popLimit(oldLimit); - if (key != null) { - target.put(key, value); + public static int computeMapFieldSize( + Map map, int number, int keyType, int valueType) { + int size = 0; + int tagSize = CodedOutputByteBufferNano.computeTagSize(number); + for (Entry entry: map.entrySet()) { + K key = entry.getKey(); + V value = entry.getValue(); + if (key == null || value == null) { + throw new IllegalStateException( + "keys and values in maps cannot be null"); + } + int entrySize = + CodedOutputByteBufferNano.computeFieldSize(1, keyType, key) + + CodedOutputByteBufferNano.computeFieldSize(2, valueType, value); + size += tagSize + entrySize + + CodedOutputByteBufferNano.computeRawVarint32Size(entrySize); + } + return size; } - return target; + + private Internal() {} } private MapUtil() {} -- cgit v1.2.3