blob: 08f8d067712a6b0ff8050cb98532a65ebe690d3e [file] [log] [blame]
slowr60d4d102017-08-16 18:33:58 -07001/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4package org.onosproject.xran.codecs.ber.types;
5
6import com.fasterxml.jackson.annotation.JsonIgnore;
7import com.fasterxml.jackson.annotation.JsonValue;
8import org.onosproject.xran.codecs.ber.BerByteArrayOutputStream;
9import org.onosproject.xran.codecs.ber.BerLength;
10import org.onosproject.xran.codecs.ber.BerTag;
11import org.onosproject.xran.codecs.ber.internal.Util;
12
13import java.io.EOFException;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.Serializable;
17import java.math.BigInteger;
18
19public class BerReal implements Serializable {
20
21 public final static BerTag tag = new BerTag(BerTag.UNIVERSAL_CLASS, BerTag.PRIMITIVE, BerTag.REAL_TAG);
22 private static final long serialVersionUID = 1L;
23 @JsonIgnore
24 public byte[] code = null;
25
26 public double value;
27
28 public BerReal() {
29 }
30
31 public BerReal(byte[] code) {
32 this.code = code;
33 }
34
35 public BerReal(double value) {
36 this.value = value;
37 }
38
39 public int encode(BerByteArrayOutputStream os) throws IOException {
40 return encode(os, true);
41 }
42
43 public int encode(BerByteArrayOutputStream os, boolean withTag) throws IOException {
44
45 if (code != null) {
46 for (int i = code.length - 1; i >= 0; i--) {
47 os.write(code[i]);
48 }
49 if (withTag) {
50 return tag.encode(os) + code.length;
51 }
52 return code.length;
53 }
54
55 int codeLength = encodeValue(os);
56
57 codeLength += BerLength.encodeLength(os, codeLength);
58
59 if (withTag) {
60 codeLength += tag.encode(os);
61 }
62
63 return codeLength;
64 }
65
66 private int encodeValue(BerByteArrayOutputStream os) throws IOException {
67
68 // explained in Annex C and Ch. 8.5 of X.690
69
70 // we use binary encoding, with base 2 and F==0
71 // F is only needed when encoding with base 8 or 16
72
73 long longBits = Double.doubleToLongBits(value);
74
75 boolean isNegative = (longBits & 0x8000000000000000L) == 0x8000000000000000L;
76
77 int exponent = ((int) (longBits >> 52)) & 0x7ff;
78
79 long mantissa = (longBits & 0x000fffffffffffffL) | 0x0010000000000000L;
80
81 if (exponent == 0x7ff) {
82 if (mantissa == 0x0010000000000000L) {
83 if (isNegative) {
84 // - infinity
85 os.write(0x41);
86 } else {
87 // + infinity
88 os.write(0x40);
89 }
90 return 1;
91 } else {
92 throw new IOException("NAN not supported");
93 }
94 }
95
96 if ((exponent == 0 && mantissa == 0x0010000000000000L)) {
97 // zero
98 return 0;
99 }
100
101 // because IEEE double-precision format is (-1)^sign * 1.b51b50..b0 * 2^(e-1023) we need to subtract 1023 and 52
102 // from the exponent to get an exponent corresponding to an integer matissa as need here.
103 exponent -= 1075; // 1023 + 52 = 1075
104
105 // trailing zeros of the mantissa should be removed. Therefor find out how much the mantissa can be shifted and
106 // the exponent can be increased
107 int exponentIncr = 0;
108 while (((mantissa >> exponentIncr) & 0xff) == 0x00) {
109 exponentIncr += 8;
110 }
111 while (((mantissa >> exponentIncr) & 0x01) == 0x00) {
112 exponentIncr++;
113 }
114
115 exponent += exponentIncr;
116 mantissa >>= exponentIncr;
117
118 int mantissaLength = (Long.SIZE - Long.numberOfLeadingZeros(mantissa) + 7) / 8;
119
120 for (int i = 0; i < mantissaLength; i++) {
121 os.write((int) (mantissa >> (8 * i)));
122 }
123 int codeLength = mantissaLength;
124
125 byte[] exponentBytes = BigInteger.valueOf(exponent).toByteArray();
126 os.write(exponentBytes);
127 codeLength += exponentBytes.length;
128
129 byte exponentFormat = 0;
130 if (exponentBytes.length < 4) {
131 exponentFormat = (byte) (exponentBytes.length - 1);
132 } else {
133 os.write(exponentBytes.length);
134 codeLength++;
135 exponentFormat = 0x03;
136 }
137
138 if (isNegative) {
139 os.write(0x80 | 0x40 | exponentFormat);
140 } else {
141 os.write(0x80 | exponentFormat);
142 }
143
144 codeLength++;
145
146 return codeLength;
147 }
148
149 public int decode(InputStream is) throws IOException {
150 return decode(is, true);
151 }
152
153 public int decode(InputStream is, boolean withTag) throws IOException {
154
155 int codeLength = 0;
156
157 if (withTag) {
158 codeLength += tag.decodeAndCheck(is);
159 }
160
161 BerLength length = new BerLength();
162 codeLength += length.decode(is);
163
164 if (length.val == 0) {
165 value = 0;
166 return codeLength;
167 }
168
169 if (length.val == 1) {
170 int nextByte = is.read();
171 if (nextByte == -1) {
172 throw new EOFException("Unexpected end of input stream.");
173 }
174 if (nextByte == 0x40) {
175 value = Double.POSITIVE_INFINITY;
176 } else if (nextByte == 0x41) {
177 value = Double.NEGATIVE_INFINITY;
178 } else {
179 throw new IOException("invalid real encoding");
180 }
181 return codeLength + 1;
182 }
183
184 byte[] byteCode = new byte[length.val];
185 Util.readFully(is, byteCode);
186
187 if ((byteCode[0] & 0x80) != 0x80) {
188 throw new IOException("Only binary REAL encoding is supported");
189 }
190
191 codeLength += length.val;
192 int tempLength = 1;
193
194 int sign = 1;
195 if ((byteCode[0] & 0x40) == 0x40) {
196 sign = -1;
197 }
198
199 int exponentLength = (byteCode[0] & 0x03) + 1;
200 if (exponentLength == 4) {
201 exponentLength = byteCode[1];
202 tempLength++;
203 }
204
205 tempLength += exponentLength;
206
207 int exponent = 0;
208 for (int i = 0; i < exponentLength; i++) {
209 exponent |= byteCode[1 + i] << (8 * (exponentLength - i - 1));
210 }
211
212 long mantissa = 0;
213 for (int i = 0; i < length.val - tempLength; i++) {
214 mantissa |= (byteCode[i + tempLength] & 0xffL) << (8 * (length.val - tempLength - i - 1));
215 }
216
217 value = sign * mantissa * Math.pow(2, exponent);
218
219 return codeLength;
220 }
221
222 public void encodeAndSave(int encodingSizeGuess) throws IOException {
223 BerByteArrayOutputStream os = new BerByteArrayOutputStream(encodingSizeGuess);
224 encode(os, false);
225 code = os.getArray();
226 }
227
228 @JsonValue
229 @Override
230 public String toString() {
231 return "" + value;
232 }
233}