All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.protostuff.runtime.NumericIdStrategy Maven / Gradle / Ivy

//================================================================================
//Copyright (c) 2012, David Yu
//All rights reserved.
//--------------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
// 3. Neither the name of protostuff nor the names of its contributors may be used
//    to endorse or promote products derived from this software without
//    specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//================================================================================

package io.protostuff.runtime;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;

import io.protostuff.ByteString;
import io.protostuff.CollectionSchema;
import io.protostuff.Input;
import io.protostuff.MapSchema;
import io.protostuff.Output;
import io.protostuff.Pipe;
import io.protostuff.Schema;

/**
 * Base class for numeric id strategies.
 * 
 * @author David Yu
 * @created Mar 28, 2012
 */
public abstract class NumericIdStrategy extends IdStrategy
{

    // class ids will be limited to 5 bits (written as the value
    // of the key: RuntimeFieldFactory.ID_ARRAY)

    // Note that ID_ARRAY is used to write the int ids because the value
    // is 15 (ID_ARRAY_MAPPED is 17), which means it only takes 1 byte to
    // write.
    // This optimization will benefit workloads that have primitive arrays
    // (including their boxed types) and arrays that have concrete component
    // types (e.g not an interface or abstract class)

    // The same rules apply for ID_CLASS_ARRAY and ID_CLASS_ARRAY_MAPPED

    // primitive values are 0-7 (first 3 bits)
    // the 4th bit is the primitive flag

    // limited to 24 ids max
    protected static final int CID_BOOL = 0, CID_BYTE = 1, CID_CHAR = 2, CID_SHORT = 3,
            CID_INT32 = 4, CID_INT64 = 5, CID_FLOAT = 6, CID_DOUBLE = 7,
            CID_STRING = 16, CID_BYTES = 17, CID_BYTE_ARRAY = 18,
            CID_BIGDECIMAL = 19, CID_BIGINTEGER = 20,
            CID_DATE = 21, CID_OBJECT = 22,
            CID_ENUM_SET = 23, CID_ENUM_MAP = 24, CID_ENUM = 25,
            CID_COLLECTION = 26, CID_MAP = 27,
            CID_POJO = 28, CID_CLASS = 29, CID_DELEGATE = 30;

    protected NumericIdStrategy(int flags, IdStrategy primaryGroup, int groupId)
    {
        super(flags, primaryGroup, groupId);
    }

    @Override
    protected void writeArrayIdTo(Output output, Class componentType)
            throws IOException
    {
        // shouldn't happen
        assert !componentType.isArray();

        final RegisteredDelegate rd = getRegisteredDelegate(componentType);
        if (rd != null)
        {
            output.writeUInt32(RuntimeFieldFactory.ID_ARRAY,
                    (rd.id << 5) | CID_DELEGATE, false);
            return;
        }

        final RuntimeFieldFactory inline =
                RuntimeFieldFactory.getInline(componentType);

        if (inline != null)
        {
            output.writeUInt32(RuntimeFieldFactory.ID_ARRAY,
                    getPrimitiveOrScalarId(componentType, inline.id), false);
        }
        else if (componentType.isEnum())
        {
            output.writeUInt32(RuntimeFieldFactory.ID_ARRAY,
                    getEnumId(componentType), false);
        }
        else if (Object.class == componentType)
        {
            output.writeUInt32(RuntimeFieldFactory.ID_ARRAY,
                    CID_OBJECT, false);
        }
        else if (Class.class == componentType)
        {
            output.writeUInt32(RuntimeFieldFactory.ID_ARRAY,
                    CID_CLASS, false);
        }
        else if (!componentType.isInterface() &&
                !Modifier.isAbstract(componentType.getModifiers()))
        {
            output.writeUInt32(RuntimeFieldFactory.ID_ARRAY,
                    getId(componentType), false);
        }
        else
        {
            // too many possible interfaces and abstract types that it would be costly
            // to index it at runtime (Not all subclasses allow dynamic indexing)
            output.writeString(RuntimeFieldFactory.ID_ARRAY_MAPPED,
                    componentType.getName(), false);
        }
    }

    @Override
    protected void transferArrayId(Input input, Output output, int fieldNumber,
            boolean mapped) throws IOException
    {
        if (mapped)
            input.transferByteRangeTo(output, true, fieldNumber, false);
        else
            output.writeUInt32(fieldNumber, input.readUInt32(), false);
    }

    @Override
    protected Class resolveArrayComponentTypeFrom(Input input, boolean mapped)
            throws IOException
    {
        return mapped ? RuntimeEnv.loadClass(input.readString()) :
                resolveClass(input.readUInt32());
    }

    @Override
    protected void writeClassIdTo(Output output, Class componentType, boolean array)
            throws IOException
    {
        // shouldn't happen
        assert !componentType.isArray();

        final int id = array ?
                RuntimeFieldFactory.ID_CLASS_ARRAY : RuntimeFieldFactory.ID_CLASS;

        final RegisteredDelegate rd = getRegisteredDelegate(componentType);
        if (rd != null)
        {
            output.writeUInt32(id,
                    (rd.id << 5) | CID_DELEGATE, false);
            return;
        }

        final RuntimeFieldFactory inline =
                RuntimeFieldFactory.getInline(componentType);

        if (inline != null)
        {
            output.writeUInt32(id,
                    getPrimitiveOrScalarId(componentType, inline.id), false);
        }
        else if (componentType.isEnum())
        {
            output.writeUInt32(id,
                    getEnumId(componentType), false);
        }
        else if (Object.class == componentType)
        {
            output.writeUInt32(id,
                    CID_OBJECT, false);
        }
        else if (Class.class == componentType)
        {
            output.writeUInt32(id,
                    CID_CLASS, false);
        }
        else if (!componentType.isInterface() &&
                !Modifier.isAbstract(componentType.getModifiers()))
        {
            output.writeUInt32(id,
                    getId(componentType), false);
        }
        else
        {
            // too many possible interfaces and abstract types that it would be costly
            // to index it at runtime (Not all subclasses allow dynamic indexing)
            // mapped class ids are +1 from regular the regular ones
            output.writeString(id + 1,
                    componentType.getName(), false);
        }
    }

    @Override
    protected void transferClassId(Input input, Output output, int fieldNumber,
            boolean mapped, boolean array) throws IOException
    {
        if (mapped)
            input.transferByteRangeTo(output, true, fieldNumber, false);
        else
            output.writeUInt32(fieldNumber, input.readUInt32(), false);
    }

    @Override
    protected Class resolveClassFrom(Input input, boolean mapped,
            boolean array) throws IOException
    {
        return mapped ? RuntimeEnv.loadClass(input.readString()) :
                resolveClass(input.readUInt32());
    }

    private static int getPrimitiveOrScalarId(Class clazz, int id)
    {
        if (clazz.isPrimitive())
            return id - 1;

        // if id < 9, its the primitive's boxed type.
        return id < 9 ? ((id - 1) | 0x08) : (id + 7);
    }

    private Class resolveClass(int id)
    {
        final int type = id & 0x1F;
        if (type < 16)
        {
            final boolean primitive = type < 8;
            // first 3 bits
            switch (type & 0x07)
            {
                case CID_BOOL:
                    return primitive ? boolean.class : Boolean.class;
                case CID_BYTE:
                    return primitive ? byte.class : Byte.class;
                case CID_CHAR:
                    return primitive ? char.class : Character.class;
                case CID_SHORT:
                    return primitive ? short.class : Short.class;
                case CID_INT32:
                    return primitive ? int.class : Integer.class;
                case CID_INT64:
                    return primitive ? long.class : Long.class;
                case CID_FLOAT:
                    return primitive ? float.class : Float.class;
                case CID_DOUBLE:
                    return primitive ? double.class : Double.class;
                default:
                    throw new RuntimeException("Should not happen.");
            }
        }

        switch (type)
        {
            case CID_STRING:
                return String.class;
            case CID_BYTES:
                return ByteString.class;
            case CID_BYTE_ARRAY:
                return byte[].class;
            case CID_BIGDECIMAL:
                return BigDecimal.class;
            case CID_BIGINTEGER:
                return BigInteger.class;
            case CID_DATE:
                return Date.class;
            case CID_OBJECT:
                return Object.class;
            case CID_ENUM_SET:
                return EnumSet.class;
            case CID_ENUM_MAP:
                return EnumMap.class;
            case CID_ENUM:
                return enumClass(id >>> 5);

            case CID_COLLECTION:
                return collectionClass(id >>> 5);

            case CID_MAP:
                return mapClass(id >>> 5);

            case CID_POJO:
                return pojoClass(id >>> 5);

            case CID_CLASS:
                return Class.class;

            case CID_DELEGATE:
                return delegateClass(id >>> 5);
        }

        throw new RuntimeException("Should not happen.");
    }

    protected abstract RegisteredDelegate getRegisteredDelegate(Class clazz);

    protected abstract Class enumClass(int id);

    protected abstract Class delegateClass(int id);

    protected abstract Class collectionClass(int id);

    protected abstract Class mapClass(int id);

    protected abstract Class pojoClass(int id);

    protected abstract int getEnumId(Class clazz);

    protected abstract int getId(Class clazz);

    protected static  ArrayList newList(int size)
    {
        List l = Collections.nCopies(size, null);
        return new ArrayList<>(l);
    }

    protected static  void grow(ArrayList list, int size)
    {
        int previousSize = list.size();

        list.ensureCapacity(size);
        List l = Collections.nCopies(size - previousSize, null);
        list.addAll(l);
    }

    protected static final class RegisteredDelegate extends HasDelegate
    {
        public final int id;

        RegisteredDelegate(int id, Delegate delegate, IdStrategy strategy)
        {
            super(delegate, strategy);

            this.id = id;
        }
    }

    /**
     * Register your pojos/enums/collections/maps/delegates here.
     */
    public interface Registry
    {
        /**
         * Collection ids start at 1.
         */
        public > Registry registerCollection(
                CollectionSchema.MessageFactory factory, int id);

        /**
         * Map ids start at 1.
         */
        public > Registry registerMap(
                MapSchema.MessageFactory factory, int id);

        /**
         * Enum ids start at 1.
         */
        public > Registry registerEnum(Class clazz, int id);

        /**
         * Enum ids start at 1.
         */
        public Registry registerEnum(EnumIO eio, int id);

        /**
         * Pojo ids start at 1.
         */
        public  Registry registerPojo(Class clazz, int id);

        /**
         * Pojo ids start at 1.
         */
        public  Registry registerPojo(Schema schema, Pipe.Schema pipeSchema,
                int id);

        /**
         * If you are sure that you are only using a single implementation of your interface/abstract class, then it
         * makes sense to map it directly to its impl class to avoid writing the type.
         * 

* Note that the type is always written when your field is {@link java.lang.Object}. *

* Pojo ids start at 1. */ public Registry mapPojo(Class baseClass, Class implClass); /** * Register a {@link Delegate} and assign an id. *

* Delegate ids start at 1. */ public Registry registerDelegate(Delegate delegate, int id); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy