001/*
002  GRANITE DATA SERVICES
003  Copyright (C) 2013 GRANITE DATA SERVICES S.A.S.
004
005  This file is part of Granite Data Services.
006
007  Granite Data Services is free software; you can redistribute it and/or modify
008  it under the terms of the GNU Library General Public License as published by
009  the Free Software Foundation; either version 2 of the License, or (at your
010  option) any later version.
011
012  Granite Data Services is distributed in the hope that it will be useful, but
013  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015  for more details.
016
017  You should have received a copy of the GNU Library General Public License
018  along with this library; if not, see <http://www.gnu.org/licenses/>.
019*/
020
021package org.granite.messaging.jmf.codec.std.impl;
022
023import java.io.IOException;
024import java.io.OutputStream;
025
026import org.granite.messaging.jmf.DumpContext;
027import org.granite.messaging.jmf.InputContext;
028import org.granite.messaging.jmf.OutputContext;
029import org.granite.messaging.jmf.codec.std.LongCodec;
030
031/**
032 * @author Franck WOLFF
033 */
034public class LongCodecImpl extends AbstractStandardCodec<Long> implements LongCodec {
035
036        public int getObjectType() {
037                return JMF_LONG_OBJECT;
038        }
039
040        public Class<?> getObjectClass() {
041                return Long.class;
042        }
043
044        public int getPrimitiveType() {
045                return JMF_LONG;
046        }
047
048        public Class<?> getPrimitiveClass() {
049                return Long.TYPE;
050        }
051
052        public void encode(OutputContext ctx, Long v) throws IOException {
053                writeLongData(ctx, JMF_LONG_OBJECT, v.longValue());
054        }
055        
056        public Long decode(InputContext ctx, int parameterizedJmfType) throws IOException {
057                int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
058
059                if (jmfType != JMF_LONG_OBJECT)
060                        throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
061
062                return Long.valueOf(readLongData(ctx, parameterizedJmfType));
063        }
064
065        public void encodePrimitive(OutputContext ctx, long v) throws IOException {
066                writeLongData(ctx, JMF_LONG, v);
067        }
068        
069        public long decodePrimitive(InputContext ctx) throws IOException {
070                int parameterizedJmfType = ctx.safeRead();
071                int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
072
073                if (jmfType != JMF_LONG)
074                        throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
075                
076                return readLongData(ctx, parameterizedJmfType);
077        }
078        
079        public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException {
080                int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
081                
082                switch (jmfType) {
083                case JMF_LONG:
084                        ctx.indentPrintLn("long: " + readLongData(ctx, parameterizedJmfType));
085                        break;
086                case JMF_LONG_OBJECT:
087                        ctx.indentPrintLn(Long.class.getName() + ": " + Long.valueOf(readLongData(ctx, parameterizedJmfType)));
088                        break;
089                default:
090                        throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
091                }
092        }
093
094        protected void writeLongData(OutputContext ctx, int jmfType, long v) throws IOException {
095                int l = 7; // --> Long.MIN_VALUE
096                int s = 0x00;
097                if (v != Long.MIN_VALUE) {
098                        if (v < 0) {
099                                s = 0x80;
100                                v = -v;
101                        }
102                        l = lenghtOfAbsoluteLong(v);
103                }
104                
105                final OutputStream os = ctx.getOutputStream();
106                
107                os.write(s | (l << 4) | jmfType);
108                switch (l) {
109                case 7:
110                        os.write((int)(v >> 56));
111                case 6:
112                        os.write((int)(v >> 48));
113                case 5:
114                        os.write((int)(v >> 40));
115                case 4:
116                        os.write((int)(v >> 32));
117                case 3:
118                        os.write((int)(v >> 24));
119                case 2:
120                        os.write((int)(v >> 16));
121                case 1:
122                        os.write((int)(v >> 8));
123                case 0:
124                        os.write((int)v);
125                        break;
126                }
127        }
128        
129        protected int lenghtOfAbsoluteLong(long v) {
130                if (v <= 0x00000000FFFFFFFFL) {
131                        if (v <= 0x000000000000FFFFL)
132                                return (v <= 0x00000000000000FFL ? 0 : 1);
133                        return (v <= 0x0000000000FFFFFFL ? 2 : 3);
134                }
135                
136                if (v <= 0x0000FFFFFFFFFFFFL)
137                        return (v <= 0x000000FFFFFFFFFFL ? 4 : 5);
138                return (v <= 0x00FFFFFFFFFFFFFFL ? 6 : 7);
139        }
140        
141        protected long readLongData(InputContext ctx, int parameterizedJmfType) throws IOException {
142                long v = 0;
143                
144                switch ((parameterizedJmfType >> 4) & 0x07) {
145                case 7:
146                        v |= ((long)ctx.safeRead()) << 56;
147                case 6:
148                        v |= ((long)ctx.safeRead()) << 48;
149                case 5:
150                        v |= ((long)ctx.safeRead()) << 40;
151                case 4:
152                        v |= ((long)ctx.safeRead()) << 32;
153                case 3:
154                        v |= ((long)ctx.safeRead()) << 24;
155                case 2:
156                        v |= ((long)ctx.safeRead()) << 16;
157                case 1:
158                        v |= ((long)ctx.safeRead()) << 8;
159                case 0:
160                        v |= ctx.safeRead();
161                }
162
163                if ((parameterizedJmfType & 0x80) != 0)
164                        v = -v;
165                
166                return v;
167        }
168        
169        public void writeVariableLong(OutputContext ctx, long v) throws IOException {
170
171                final OutputStream os = ctx.getOutputStream();
172                
173                if (v == Long.MIN_VALUE)
174                        os.write(0x80);
175                else {
176                        int sign = 0x00;
177                        if (v < 0) {
178                                sign = 0x80;
179                                v = -v;
180                        }
181                        
182                        int bytesCount = lengthOfVariableAbsoluteLong(v);
183                        v -= deltaForVariableAbsoluteLongLength(bytesCount);
184                        
185                        switch (bytesCount) {
186                        case 0:
187                                os.write(sign | (int)v);
188                                break;
189                        case 1: case 2: case 3: case 4: case 5: case 6: case 7:
190                                os.write(sign | 0x40 | (int)(v >> (bytesCount * 7)));
191                                for (int i = bytesCount - 1; i > 0; i--)
192                                        os.write(0x80 | (int)(v >> (i * 7)));
193                                os.write(0x7F & (int)v);
194                                break;
195                        case 8:
196                                os.write(sign | 0x40 | (int)(v >> 57));
197                                os.write(0x80 | (int)(v >> 50));
198                                os.write(0x80 | (int)(v >> 43));
199                                os.write(0x80 | (int)(v >> 36));
200                                os.write(0x80 | (int)(v >> 29));
201                                os.write(0x80 | (int)(v >> 22));
202                                os.write(0x80 | (int)(v >> 15));
203                                os.write(0x80 | (int)(v >> 8));
204                                os.write((int)v);
205                                break;
206                        }
207                }
208        }
209        
210        public long readVariableLong(InputContext ctx) throws IOException {
211                long v = ctx.safeRead();
212                
213                if (v == 0x80L)
214                        v = Long.MIN_VALUE;
215                else {
216                        boolean opposite = ((v & 0x80L) != 0);
217                        boolean readNext = (v & 0x40L) != 0;
218                        
219                        v &= 0x3FL;
220                        
221                        if (readNext) {
222                                int l = 1;
223                                for (int i = 1; i <= 7; i++) {
224                                        long u = ctx.safeRead();
225                                        v = (v << 7) | (u & 0x7FL);
226                                        if ((u & 0x80L) == 0) {
227                                                readNext = false;
228                                                break;
229                                        }
230                                        l++;
231                                }
232                                if (readNext)
233                                        v = (v << 8) | ctx.safeRead();
234                                v += deltaForVariableAbsoluteLongLength(l);
235                        }
236                        
237                        if (opposite)
238                                v = -v;
239                }
240                
241                return v;
242        }
243
244        protected static final long[] VARIABLE_LONG_DELTAS = new long[9];
245        static {
246                VARIABLE_LONG_DELTAS[0] = 0L;
247                for (int i = 1; i < VARIABLE_LONG_DELTAS.length; i++)
248                        VARIABLE_LONG_DELTAS[i] = (VARIABLE_LONG_DELTAS[i-1] << 7) | 0x40L;
249        }
250        
251        protected static int lengthOfVariableAbsoluteLong(long abs) {
252                for (int i = 1; i < VARIABLE_LONG_DELTAS.length; i++) {
253                        if (abs < VARIABLE_LONG_DELTAS[i])
254                                return (i - 1);
255                }
256                return 8;
257        }
258        
259        protected static long deltaForVariableAbsoluteLongLength(int len) {
260                return VARIABLE_LONG_DELTAS[len];
261        }
262}