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