Package com.esotericsoftware.kryo.serialize

Source Code of com.esotericsoftware.kryo.serialize.ArraySerializer

package com.esotericsoftware.kryo.serialize;

import static com.esotericsoftware.minlog.Log.*;

import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;

/**
* Serializes arrays.
* <p>
* With the default constructor, an array requires a header of 2-4 bytes plus 2 bytes for each dimension beyond the first. If the
* array type is not final then an extra byte is written for each element.
* @see Kryo#register(Class, Serializer)
* @author Nathan Sweet <misc@n4te.com>
*/
public class ArraySerializer extends Serializer {
  private final Kryo kryo;
  private Integer fixedDimensionCount;
  private boolean elementsAreSameType;
  private boolean elementsCanBeNull = true;
  private int[] dimensions;

  public ArraySerializer (Kryo kryo) {
    this.kryo = kryo;
  }

  /**
   * @param dimensions The number of dimensions. Saves 1 byte. Set to null to determine the number of dimensions (default).
   */
  public void setDimensionCount (Integer dimensions) {
    this.fixedDimensionCount = dimensions;
  }

  /**
   * Identical to calling {@link #setLengths(int[])} with: new int[] {length}
   */
  public void setLength (int length) {
    dimensions = new int[] {length};
  }

  /**
   * Sets both the number of dimensions and the lengths of each. Saves 2-6 bytes plus 1-5 bytes for each dimension beyond the
   * first.
   */
  public void setLengths (int[] lengths) {
    dimensions = lengths;
  }

  /**
   * @param elementsCanBeNull False if all elements are not null. This saves 1 byte per element if the array type is final or
   *           elementsAreSameClassAsType is true. True if it is not known (default).
   */
  public void setElementsCanBeNull (boolean elementsCanBeNull) {
    this.elementsCanBeNull = elementsCanBeNull;
  }

  /**
   * @param elementsAreSameType True if all elements are the same type as the array (ie they don't extend the array type). This
   *           saves 1 byte per element if the array type is not final. Set to false if the array type is final or elements
   *           extend the array type (default).
   */
  public void setElementsAreSameType (boolean elementsAreSameType) {
    this.elementsAreSameType = elementsAreSameType;
    // BOZO - Change to set type?
  }

  public void writeObjectData (ByteBuffer buffer, Object array) {
    // Write dimensions.
    int[] dimensions = this.dimensions;
    if (dimensions == null) {
      dimensions = getDimensions(array);
      if (fixedDimensionCount == null) ByteSerializer.putUnsigned(buffer, dimensions.length);
      for (int i = 0, n = dimensions.length; i < n; i++)
        IntSerializer.put(buffer, dimensions[i], true);
    }
    // If element class is final (this includes primitives) then all elements are the same type.
    Serializer elementSerializer = null;
    Class elementClass = getElementClass(array.getClass());
    boolean elementsCanBeNull = this.elementsCanBeNull && !elementClass.isPrimitive();
    if (elementsAreSameType || Modifier.isFinal(elementClass.getModifiers()))
      elementSerializer = kryo.getRegisteredClass(elementClass).getSerializer();
    // Write array data.
    writeArray(buffer, array, elementSerializer, 0, dimensions.length, elementsCanBeNull);
    if (TRACE) {
      StringBuilder stringBuffer = new StringBuilder(16);
      for (int i = 0, n = dimensions.length; i < n; i++) {
        stringBuffer.append('[');
        stringBuffer.append(dimensions[i]);
        stringBuffer.append(']');
      }
      trace("kryo", "Wrote array: " + elementClass.getName() + stringBuffer);
    }
  }

  private void writeArray (ByteBuffer buffer, Object array, Serializer elementSerializer, int dimension, int dimensionCount,
    boolean elementsCanBeNull) {
    int length = Array.getLength(array);
    if (dimension > 0) {
      // Write array length. With Java's "jagged arrays" this could be less than the dimension size.
      IntSerializer.put(buffer, length, true);
    }
    // Write array data.
    boolean elementsAreArrays = dimension < dimensionCount - 1;
    for (int i = 0; i < length; i++) {
      Object element = Array.get(array, i);
      if (elementsAreArrays) {
        // Nested array.
        if (element != null)
          writeArray(buffer, element, elementSerializer, dimension + 1, dimensionCount, elementsCanBeNull);
      } else if (elementSerializer != null) {
        // Use same serializer for all elements.
        if (elementsCanBeNull)
          elementSerializer.writeObject(buffer, element);
        else
          elementSerializer.writeObjectData(buffer, element);
      } else {
        // Each element could be a different type. Store the class with the object.
        kryo.writeClassAndObject(buffer, element);
      }
    }
  }

  public <T> T readObjectData (ByteBuffer buffer, Class<T> type) {
    // Get dimensions.
    int[] dimensions = this.dimensions;
    int dimensionCount;
    if (dimensions == null) {
      dimensionCount = fixedDimensionCount != null ? fixedDimensionCount : ByteSerializer.getUnsigned(buffer);
      dimensions = new int[dimensionCount];
      for (int i = 0; i < dimensionCount; i++)
        dimensions[i] = IntSerializer.get(buffer, true);
    } else
      dimensionCount = dimensions.length;
    // Get element serializer if all elements are the same type.
    Serializer elementSerializer = null;
    Class elementClass = getElementClass(type);
    boolean elementsCanBeNull = this.elementsCanBeNull && !elementClass.isPrimitive();
    if (elementsAreSameType || Modifier.isFinal(elementClass.getModifiers()))
      elementSerializer = kryo.getRegisteredClass(elementClass).getSerializer();
    // Create array and read in the data.
    T array = (T)Array.newInstance(elementClass, dimensions);
    readArray(buffer, array, elementSerializer, elementClass, 0, dimensions, elementsCanBeNull);
    if (TRACE) {
      StringBuilder stringBuffer = new StringBuilder(16);
      for (int i = 0; i < dimensionCount; i++) {
        stringBuffer.append('[');
        stringBuffer.append(dimensions[i]);
        stringBuffer.append(']');
      }
      trace("kryo", "Read array: " + elementClass.getName() + stringBuffer);
    }
    return array;
  }

  private void readArray (ByteBuffer buffer, Object array, Serializer elementSerializer, Class elementClass, int dimension,
    int[] dimensions, boolean elementsCanBeNull) {
    boolean elementsAreArrays = dimension < dimensions.length - 1;
    int length;
    if (dimension == 0)
      length = dimensions[0];
    else
      length = IntSerializer.get(buffer, true);
    for (int i = 0; i < length; i++) {
      if (elementsAreArrays) {
        // Nested array.
        Object element = Array.get(array, i);
        if (element != null)
          readArray(buffer, element, elementSerializer, elementClass, dimension + 1, dimensions, elementsCanBeNull);
      } else if (elementSerializer != null) {
        // Use same serializer (and class) for all elements.
        if (elementsCanBeNull)
          Array.set(array, i, elementSerializer.readObject(buffer, elementClass));
        else
          Array.set(array, i, elementSerializer.readObjectData(buffer, elementClass));
      } else {
        // Each element could be a different type. Look up the class with the object.
        Array.set(array, i, kryo.readClassAndObject(buffer));
      }
    }
  }

  static public int getDimensionCount (Class arrayClass) {
    int depth = 0;
    Class nextClass = arrayClass.getComponentType();
    while (nextClass != null) {
      depth++;
      nextClass = nextClass.getComponentType();
    }
    return depth;
  }

  static public int[] getDimensions (Object array) {
    int depth = 0;
    Class nextClass = array.getClass().getComponentType();
    while (nextClass != null) {
      depth++;
      nextClass = nextClass.getComponentType();
    }
    int[] dimensions = new int[depth];
    dimensions[0] = Array.getLength(array);
    if (depth > 1) collectDimensions(array, 1, dimensions);
    return dimensions;
  }

  static private void collectDimensions (Object array, int dimension, int[] dimensions) {
    boolean elementsAreArrays = dimension < dimensions.length - 1;
    for (int i = 0, s = Array.getLength(array); i < s; i++) {
      Object element = Array.get(array, i);
      if (element == null) continue;
      dimensions[dimension] = Math.max(dimensions[dimension], Array.getLength(element));
      if (elementsAreArrays) collectDimensions(element, dimension + 1, dimensions);
    }
  }

  static public Class getElementClass (Class arrayClass) {
    Class elementClass = arrayClass;
    while (elementClass.getComponentType() != null)
      elementClass = elementClass.getComponentType();
    return elementClass;
  }
}
TOP

Related Classes of com.esotericsoftware.kryo.serialize.ArraySerializer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.