1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * 19 */ 20 package org.apache.mina.codec.delimited.ints; 21 22 import java.nio.BufferUnderflowException; 23 import java.nio.ByteBuffer; 24 25 import org.apache.mina.codec.IoBuffer; 26 import org.apache.mina.codec.ProtocolDecoderException; 27 import org.apache.mina.codec.delimited.ByteBufferEncoder; 28 import org.apache.mina.codec.delimited.IoBufferDecoder; 29 30 /** 31 * Class providing a variable length representation of integers. 32 * 33 * <style type="text/css"> pre-fw { color: rgb(0, 0, 0); display: block; 34 * font-family:courier, "courier new", monospace; font-size: 13px; white-space: 35 * pre; } </style> 36 * 37 * <h2>Base 128 Varints serializer</h2> 38 * <p> 39 * This serializer is efficient in terms of computing costs as well as 40 * bandwith/memory usage. 41 * </p> 42 * <p> 43 * The average memory usage overall the range 0 to 44 * {@link java.lang.Integer#MAX_VALUE} is 4.87 bytes per number which is not far 45 * from the canonical form ({@link RawInt32}), however varints are an 46 * interesting solution since the small values (which are supposed to be more 47 * frequent) are using less bytes. 48 * </p> 49 * <p> 50 * All bytes forming a varint except the last one have the most significant bit 51 * (MSB) set. The lower 7 bits of each byte contains the actual representation 52 * of the two's complement representation of the number (least significant group 53 * first). 54 * </p> 55 * <p> 56 * n.b. This serializer is fully compatible with the 128 Varint mechanism 57 * shipped with the <a 58 * href="https://developers.google.com/protocol-buffers/docs/encoding#varints" > 59 * Google Protocol Buffer stack</a> as default representation of messages sizes. 60 * </p> 61 * <h2>On-wire representation</h2> 62 * <p> 63 * Encoding of the value 812 64 * 65 * <pre-fw> 66 * 67 * 1001 1100 0000 0110 68 * ↑ ↑ 69 * 1 0 // the most significant bit being unset designs the last byte 70 * ___↑____ ___↑____ 71 * 001 1100 000 0110 // the remaining bits defines the value itself 72 * → 44 6 // 44 + 128 * 6 = 812 73 * </pre-fw> 74 * 75 * </p> 76 * <p> 77 * n.b. This class doesn't have any dependency against Google Protocol Buffer or 78 * any other library in order to provide this convenient integer serialization 79 * module to any software using FramedMINA. 80 * </p> 81 * 82 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 83 */ 84 /* 85 * About the suppression of warnings: 86 * This class contains a lot of bit-shifting, logical and/or operations order to handle 87 * VarInt conversions. The code contains a lot of hard-coded integer that tools like 88 * Sonar classify as "magic numbers". Using final static variables for all of them 89 * would have resulted in a code less readable. 90 * The "all" scope is too generic, but Sonar doesn't not handle properly others scopes 91 * like "MagicNumber" (Sonar 3.6 - 03July2013) 92 */ 93 @SuppressWarnings("all") 94 public final class VarInt implements IntTranscoder { 95 96 @Override 97 public IoBufferDecoder<Integer> getDecoder() { 98 return new Decoder(); 99 } 100 101 @Override 102 public ByteBufferEncoder<Integer> getEncoder() { 103 return new Encoder(); 104 } 105 106 /** 107 * Documentation available in the {@link VarInt} enclosing class. 108 * 109 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 110 * 111 */ 112 private class Decoder extends IoBufferDecoder<Integer> { 113 114 @Override 115 public Integer decode(IoBuffer input) { 116 int origpos = input.position(); 117 118 try { 119 byte tmp = input.get(); 120 if (tmp >= 0) { 121 return (int) tmp; 122 } 123 int result = tmp & 0x7f; 124 if ((tmp = input.get()) >= 0) { 125 result |= tmp << 7; 126 } else { 127 result |= (tmp & 0x7f) << 7; 128 if ((tmp = input.get()) >= 0) { 129 result |= tmp << 14; 130 } else { 131 result |= (tmp & 0x7f) << 14; 132 if ((tmp = input.get()) >= 0) { 133 result |= tmp << 21; 134 } else { 135 result |= (tmp & 0x7f) << 21; 136 137 // check that there are at most 3 significant bits available 138 if (((tmp = input.get()) & ~0x7) == 0) { 139 result |= tmp << 28; 140 } else { 141 throw new ProtocolDecoderException("Not the varint representation of a signed int32"); 142 } 143 } 144 } 145 } 146 return result; 147 148 } catch (BufferUnderflowException bue) { 149 input.position(origpos); 150 } 151 return null; 152 } 153 } 154 155 /** 156 * Documentation available in the {@link VarInt} enclosing class. 157 * 158 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 159 * 160 */ 161 private class Encoder extends ByteBufferEncoder<Integer> { 162 163 @Override 164 public void writeTo(Integer message, ByteBuffer buffer) { 165 // VarInts don't support negative values 166 int value = Math.max(0,message); 167 168 while (true) { 169 if ((value & ~0x7F) == 0) { 170 buffer.put((byte) value); 171 return; 172 } else { 173 buffer.put((byte) ((value & 0x7F) | 0x80)); 174 value >>>= 7; 175 } 176 } 177 178 } 179 180 @Override 181 public int getEncodedSize(Integer value) { 182 if ((value & (0xffffffff << 7)) == 0) { 183 return 1; 184 } 185 if ((value & (0xffffffff << 14)) == 0) { 186 return 2; 187 } 188 if ((value & (0xffffffff << 21)) == 0) { 189 return 3; 190 } 191 if ((value & (0xffffffff << 28)) == 0) { 192 return 4; 193 } 194 return 5; 195 } 196 197 } 198 }