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.util;
023
024import java.io.IOException;
025import java.io.OutputStream;
026
027import org.granite.messaging.jmf.InputContext;
028import org.granite.messaging.jmf.JMFEncodingException;
029import org.granite.messaging.jmf.OutputContext;
030
031/**
032 * @author Franck WOLFF
033 */
034public class DoubleUtil {
035        
036        // ------------------------------------------------------------------------
037
038        public static class DoubleAsLong {
039                
040                public final long longValue;
041                public final int pow10;
042                
043                public DoubleAsLong(long longValue, int pow10) {
044                        this.longValue = longValue;
045                        this.pow10 = pow10;
046                }
047        }
048        
049        public static DoubleAsLong doubleAsLong04(double v) {
050                long asLong = (long)v;
051                if (v == (double)asLong)
052                        return new DoubleAsLong(asLong, 0);
053                
054                asLong = (long)(v * 100.0);
055                if (v == (asLong / 100.0))
056                        return new DoubleAsLong(asLong, 2);
057                asLong += (asLong < 0 ? -1 : 1);
058                if (v == (asLong / 100.0))
059                        return new DoubleAsLong(asLong, 2);
060                
061                asLong = (long)(v * 10000.0);
062                if (v == (asLong / 10000.0))
063                        return new DoubleAsLong(asLong, 4);
064                asLong += (asLong < 0 ? -1 : 1);
065                if (v == (asLong / 10000.0))
066                        return new DoubleAsLong(asLong, 4);
067                
068                return null;
069        }
070
071        // ------------------------------------------------------------------------
072        
073        public static void encodeDouble(OutputContext ctx, double v) throws IOException {
074                LongUtil.encodeLong(ctx, Double.doubleToLongBits(v));
075        }
076        
077        public static double decodeDouble(InputContext ctx) throws IOException {
078                return Double.longBitsToDouble(LongUtil.decodeLong(ctx));
079        }
080
081        // ------------------------------------------------------------------------
082
083        /*
084         * Double special values:
085         * 
086         * 0x7FF0000000000000L                                                  -> +Infinity
087         * 0x7FF0000000000001L ... 0x7FF7FFFFFFFFFFFFL  -> 1st range of Quiet NaNs
088         * 0x7FF8000000000000L                                                  -> Canonical NaN
089         * 0x7FF8000000000001L ... 0x7FFFFFFFFFFFFFFFL  -> 1st range of Signaling NaNs
090         * 0xFFF0000000000000L                                                  -> -Infinity
091         * 0xFFF0000000000001L ... 0xFFF7FFFFFFFFFFFFL  -> 2nd range of Quiet NaNs
092         * 0xFFF8000000000000L ... 0xFFFFFFFFFFFFFFFFL  -> 2nd range of Signaling NaNs
093         * 
094         * Which gives the following 2-bytes prefixes:
095         * 
096         * 0x7FF0 -> +Infinity
097         * 0x7FF8 -> NaN
098         * 0xFFF0 -> -Infinity
099         * 
100         * 0x7FF1 ... 0x7FF7 \
101         * 0x7FF9 ... 0x7FFF  > Non canonical NaN prefixes.
102         * 0xFFF1 ... 0xFFFF /
103         */
104        
105        public static void encodeVariableDouble(OutputContext ctx, double v) throws IOException {
106                final OutputStream os = ctx.getOutputStream();
107                
108                long bits = Double.doubleToRawLongBits(v);
109                if ((bits & 0x7FF0000000000000L) == 0x7FF0000000000000L) {
110                        os.write(0x7F);
111                        if (bits == 0x7FF0000000000000L) // +Infinity
112                                os.write(0xF0);
113                        else if (bits == 0xFFF0000000000000L) // -Infinity
114                                os.write(0xF1);
115                        else // NaN (canonical or not)
116                                os.write(0xF2);
117                }
118                else if (bits == 0x8000000000000000L) { // -0.0
119                        os.write(0x7F);
120                        os.write(0xF3);
121                }
122                else {
123                        DoubleAsLong asLong = doubleAsLong04(v);
124                        if (asLong != null &&
125                                asLong.longValue >= LongUtil.MIN_5_BYTES_VARIABLE_LONG &&
126                                asLong.longValue <= LongUtil.MAX_5_BYTES_VARIABLE_LONG) {
127                                
128                                os.write(0xFF);
129                                os.write(0xF0 | asLong.pow10); // 0xF0, 0xF2 or 0xF4.
130                                LongUtil.encodeVariableLong(ctx, asLong.longValue);
131                        }
132                        else
133                                LongUtil.encodeLong(ctx, bits);
134                }
135        }
136        
137        public static double decodeVariableDouble(InputContext ctx) throws IOException {
138                
139                int prefix = ctx.safeRead() << 8 | ctx.safeRead();
140                switch (prefix) {
141                case 0x7FF0:
142                        return Double.POSITIVE_INFINITY;
143                case 0x7FF1:
144                        return Double.NEGATIVE_INFINITY;
145                case 0x7FF2:
146                        return Double.NaN;
147                case 0x7FF3:
148                        return -0.0;
149                }
150                
151                if ((prefix & 0xFFF0) == 0xFFF0) {
152                        long asLong = LongUtil.decodeVariableLong(ctx);
153                        int pow10 = (prefix & 0x0F);
154                        
155                        switch (pow10) {
156                        case 0:
157                                return asLong;
158                        case 2:
159                                return asLong / 100.0;
160                        case 4:
161                                return asLong / 10000.0;
162                        default:
163                                throw new JMFEncodingException("Unsupported power of 10: " + pow10);
164                        }
165                }
166                
167                return Double.longBitsToDouble(((long)prefix) << 48 | LongUtil.decodeLong(ctx, 5));
168        }
169}