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

com.dyuproject.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 com.dyuproject.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 com.dyuproject.protostuff.ByteString;
import com.dyuproject.protostuff.CollectionSchema;
import com.dyuproject.protostuff.Input;
import com.dyuproject.protostuff.MapSchema;
import com.dyuproject.protostuff.Output;
import com.dyuproject.protostuff.Pipe;
import com.dyuproject.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(IdStrategy primaryGroup, int groupId)
    {
        super(primaryGroup, groupId);
    }
    
    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);
        }
    }
    
    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);
    }
    
    protected Class resolveArrayComponentTypeFrom(Input input, boolean mapped) 
            throws IOException 
    {
        return mapped ? RuntimeEnv.loadClass(input.readString()) : 
            resolveClass(input.readUInt32());
    }
    
    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);
        }
    }
    
    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);
    }
    
    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