001: /*
002:
003: Derby - Class org.apache.derby.client.am.Decimal
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021: package org.apache.derby.client.am;
022:
023: import org.apache.derby.shared.common.reference.SQLState;
024: import org.apache.derby.shared.common.i18n.MessageUtil;
025:
026: /**
027: * Converters from fixed point decimal bytes to <code>java.math.BigDecimal</code>, <code>double</code>, or
028: * <code>long</code>.
029: */
030: public class Decimal {
031: /**
032: * Packed Decimal representation
033: */
034: public final static int PACKED_DECIMAL = 0x30;
035:
036: private static MessageUtil msgutil = SqlException.getMessageUtil();
037:
038: //--------------------------private constants---------------------------------
039:
040: private static final int[][] tenRadixMagnitude = { { 0x3b9aca00 }, // 10^9
041: { 0x0de0b6b3, 0xa7640000 }, // 10^18
042: { 0x033b2e3c, 0x9fd0803c, 0xe8000000 }, // 10^27
043: };
044:
045: //--------------------------constructors--------------------------------------
046:
047: // Hide the default constructor, this is a static class.
048: private Decimal() {
049: }
050:
051: //--------------------------private helper methods----------------------------
052:
053: /**
054: * Convert a range of packed nybbles (up to 9 digits without overflow) to an int. Note that for performance purpose,
055: * it does not do array-out-of-bound checking.
056: */
057: private static final int packedNybblesToInt(byte[] buffer,
058: int offset, int startNybble, int numberOfNybbles) {
059: int value = 0;
060:
061: int i = startNybble / 2;
062: if ((startNybble % 2) != 0) {
063: // process low nybble of the first byte if necessary.
064: value += buffer[offset + i] & 0x0F;
065: i++;
066: }
067:
068: int endNybble = startNybble + numberOfNybbles - 1;
069: for (; i < (endNybble + 1) / 2; i++) {
070: value = value * 10 + ((buffer[offset + i] & 0xF0) >>> 4); // high nybble.
071: value = value * 10 + (buffer[offset + i] & 0x0F); // low nybble.
072: }
073:
074: if ((endNybble % 2) == 0) {
075: // process high nybble of the last byte if necessary.
076: value = value * 10 + ((buffer[offset + i] & 0xF0) >>> 4);
077: }
078:
079: return value;
080: }
081:
082: /**
083: * Convert a range of packed nybbles (up to 18 digits without overflow) to a long. Note that for performance
084: * purpose, it does not do array-out-of-bound checking.
085: */
086: private static final long packedNybblesToLong(byte[] buffer,
087: int offset, int startNybble, int numberOfNybbles) {
088: long value = 0;
089:
090: int i = startNybble / 2;
091: if ((startNybble % 2) != 0) {
092: // process low nybble of the first byte if necessary.
093: value += buffer[offset + i] & 0x0F;
094: i++;
095: }
096:
097: int endNybble = startNybble + numberOfNybbles - 1;
098: for (; i < (endNybble + 1) / 2; i++) {
099: value = value * 10 + ((buffer[offset + i] & 0xF0) >>> 4); // high nybble.
100: value = value * 10 + (buffer[offset + i] & 0x0F); // low nybble.
101: }
102:
103: if ((endNybble % 2) == 0) {
104: // process high nybble of the last byte if necessary.
105: value = value * 10 + ((buffer[offset + i] & 0xF0) >>> 4);
106: }
107:
108: return value;
109: }
110:
111: /**
112: * Compute the int array of magnitude from input value segments.
113: */
114: private static final int[] computeMagnitude(int[] input) {
115: int length = input.length;
116: int[] mag = new int[length];
117:
118: mag[length - 1] = input[length - 1];
119: for (int i = 0; i < length - 1; i++) {
120: int carry = 0;
121: int j = tenRadixMagnitude[i].length - 1;
122: int k = length - 1;
123: for (; j >= 0; j--, k--) {
124: long product = (input[length - 2 - i] & 0xFFFFFFFFL)
125: * (tenRadixMagnitude[i][j] & 0xFFFFFFFFL)
126: + (mag[k] & 0xFFFFFFFFL) // add previous value
127: + (carry & 0xFFFFFFFFL); // add carry
128: carry = (int) (product >>> 32);
129: mag[k] = (int) (product & 0xFFFFFFFFL);
130: }
131: mag[k] = (int) carry;
132: }
133: return mag;
134: }
135:
136: //--------------entry points for runtime representation-----------------------
137:
138: /**
139: * Build a <code>java.math.BigDecimal</code> from a fixed point decimal byte representation.
140: *
141: * @throws IllegalArgumentException if the specified representation is not recognized.
142: */
143: public static final java.math.BigDecimal getBigDecimal(
144: byte[] buffer, int offset, int precision, int scale)
145: throws java.io.UnsupportedEncodingException {
146: // The byte-length of a packed decimal with precision <code>p</code> is always <code>p/2 + 1</code>
147: int length = precision / 2 + 1;
148:
149: // check for sign.
150: int signum;
151: if ((buffer[offset + length - 1] & 0x0F) == 0x0D) {
152: signum = -1;
153: } else {
154: signum = 1;
155: }
156:
157: if (precision <= 9) {
158: // can be handled by int without overflow.
159: int value = packedNybblesToInt(buffer, offset, 0,
160: length * 2 - 1);
161:
162: // convert value to a byte array of magnitude.
163: byte[] magnitude = new byte[4];
164: magnitude[0] = (byte) (value >>> 24);
165: magnitude[1] = (byte) (value >>> 16);
166: magnitude[2] = (byte) (value >>> 8);
167: magnitude[3] = (byte) (value);
168:
169: return new java.math.BigDecimal(new java.math.BigInteger(
170: signum, magnitude), scale);
171: } else if (precision <= 18) {
172: // can be handled by long without overflow.
173: long value = packedNybblesToLong(buffer, offset, 0,
174: length * 2 - 1);
175:
176: // convert value to a byte array of magnitude.
177: byte[] magnitude = new byte[8];
178: magnitude[0] = (byte) (value >>> 56);
179: magnitude[1] = (byte) (value >>> 48);
180: magnitude[2] = (byte) (value >>> 40);
181: magnitude[3] = (byte) (value >>> 32);
182: magnitude[4] = (byte) (value >>> 24);
183: magnitude[5] = (byte) (value >>> 16);
184: magnitude[6] = (byte) (value >>> 8);
185: magnitude[7] = (byte) (value);
186:
187: return new java.math.BigDecimal(new java.math.BigInteger(
188: signum, magnitude), scale);
189: } else if (precision <= 27) {
190: // get the value of last 9 digits (5 bytes).
191: int lo = packedNybblesToInt(buffer, offset,
192: (length - 5) * 2, 9);
193: // get the value of another 9 digits (5 bytes).
194: int me = packedNybblesToInt(buffer, offset,
195: (length - 10) * 2 + 1, 9);
196: // get the value of the rest digits.
197: int hi = packedNybblesToInt(buffer, offset, 0,
198: (length - 10) * 2 + 1);
199:
200: // compute the int array of magnitude.
201: int[] value = computeMagnitude(new int[] { hi, me, lo });
202:
203: // convert value to a byte array of magnitude.
204: byte[] magnitude = new byte[12];
205: magnitude[0] = (byte) (value[0] >>> 24);
206: magnitude[1] = (byte) (value[0] >>> 16);
207: magnitude[2] = (byte) (value[0] >>> 8);
208: magnitude[3] = (byte) (value[0]);
209: magnitude[4] = (byte) (value[1] >>> 24);
210: magnitude[5] = (byte) (value[1] >>> 16);
211: magnitude[6] = (byte) (value[1] >>> 8);
212: magnitude[7] = (byte) (value[1]);
213: magnitude[8] = (byte) (value[2] >>> 24);
214: magnitude[9] = (byte) (value[2] >>> 16);
215: magnitude[10] = (byte) (value[2] >>> 8);
216: magnitude[11] = (byte) (value[2]);
217:
218: return new java.math.BigDecimal(new java.math.BigInteger(
219: signum, magnitude), scale);
220: } else if (precision <= 31) {
221: // get the value of last 9 digits (5 bytes).
222: int lo = packedNybblesToInt(buffer, offset,
223: (length - 5) * 2, 9);
224: // get the value of another 9 digits (5 bytes).
225: int meLo = packedNybblesToInt(buffer, offset,
226: (length - 10) * 2 + 1, 9);
227: // get the value of another 9 digits (5 bytes).
228: int meHi = packedNybblesToInt(buffer, offset,
229: (length - 14) * 2, 9);
230: // get the value of the rest digits.
231: int hi = packedNybblesToInt(buffer, offset, 0,
232: (length - 14) * 2);
233:
234: // compute the int array of magnitude.
235: int[] value = computeMagnitude(new int[] { hi, meHi, meLo,
236: lo });
237:
238: // convert value to a byte array of magnitude.
239: byte[] magnitude = new byte[16];
240: magnitude[0] = (byte) (value[0] >>> 24);
241: magnitude[1] = (byte) (value[0] >>> 16);
242: magnitude[2] = (byte) (value[0] >>> 8);
243: magnitude[3] = (byte) (value[0]);
244: magnitude[4] = (byte) (value[1] >>> 24);
245: magnitude[5] = (byte) (value[1] >>> 16);
246: magnitude[6] = (byte) (value[1] >>> 8);
247: magnitude[7] = (byte) (value[1]);
248: magnitude[8] = (byte) (value[2] >>> 24);
249: magnitude[9] = (byte) (value[2] >>> 16);
250: magnitude[10] = (byte) (value[2] >>> 8);
251: magnitude[11] = (byte) (value[2]);
252: magnitude[12] = (byte) (value[3] >>> 24);
253: magnitude[13] = (byte) (value[3] >>> 16);
254: magnitude[14] = (byte) (value[3] >>> 8);
255: magnitude[15] = (byte) (value[3]);
256:
257: return new java.math.BigDecimal(new java.math.BigInteger(
258: signum, magnitude), scale);
259: } else {
260: // throw an exception here if nibbles is greater than 31
261: throw new java.lang.IllegalArgumentException(msgutil
262: .getTextMessage(SQLState.DECIMAL_TOO_MANY_DIGITS));
263: }
264: }
265:
266: /**
267: * Build a Java <code>double</code> from a fixed point decimal byte representation.
268: *
269: * @throws IllegalArgumentException if the specified representation is not recognized.
270: */
271: public static final double getDouble(byte[] buffer, int offset,
272: int precision, int scale)
273: throws java.io.UnsupportedEncodingException {
274: // The byte-length of a packed decimal with precision <code>p</code> is always <code>p/2 + 1</code>
275: int length = precision / 2 + 1;
276:
277: // check for sign.
278: int signum;
279: if ((buffer[offset + length - 1] & 0x0F) == 0x0D) {
280: signum = -1;
281: } else {
282: signum = 1;
283: }
284:
285: if (precision <= 9) {
286: // can be handled by int without overflow.
287: int value = packedNybblesToInt(buffer, offset, 0,
288: length * 2 - 1);
289:
290: return signum * value / Math.pow(10, scale);
291: } else if (precision <= 18) {
292: // can be handled by long without overflow.
293: long value = packedNybblesToLong(buffer, offset, 0,
294: length * 2 - 1);
295:
296: return signum * value / Math.pow(10, scale);
297: } else if (precision <= 27) {
298: // get the value of last 9 digits (5 bytes).
299: int lo = packedNybblesToInt(buffer, offset,
300: (length - 5) * 2, 9);
301: // get the value of another 9 digits (5 bytes).
302: int me = packedNybblesToInt(buffer, offset,
303: (length - 10) * 2 + 1, 9);
304: // get the value of the rest digits.
305: int hi = packedNybblesToInt(buffer, offset, 0,
306: (length - 10) * 2 + 1);
307:
308: return signum
309: * (lo / Math.pow(10, scale) + me
310: * Math.pow(10, 9 - scale) + hi
311: * Math.pow(10, 18 - scale));
312: } else if (precision <= 31) {
313: // get the value of last 9 digits (5 bytes).
314: int lo = packedNybblesToInt(buffer, offset,
315: (length - 5) * 2, 9);
316: // get the value of another 9 digits (5 bytes).
317: int meLo = packedNybblesToInt(buffer, offset,
318: (length - 10) * 2 + 1, 9);
319: // get the value of another 9 digits (5 bytes).
320: int meHi = packedNybblesToInt(buffer, offset,
321: (length - 14) * 2, 9);
322: // get the value of the rest digits.
323: int hi = packedNybblesToInt(buffer, offset, 0,
324: (length - 14) * 2);
325:
326: return signum
327: * (lo / Math.pow(10, scale) + meLo
328: * Math.pow(10, 9 - scale) + meHi
329: * Math.pow(10, 18 - scale) + hi
330: * Math.pow(10, 27 - scale));
331: } else {
332: // throw an exception here if nibbles is greater than 31
333: throw new java.lang.IllegalArgumentException(msgutil
334: .getTextMessage(SQLState.DECIMAL_TOO_MANY_DIGITS));
335: }
336: }
337:
338: /**
339: * Build a Java <code>long</code> from a fixed point decimal byte representation.
340: *
341: * @throws IllegalArgumentException if the specified representation is not recognized.
342: */
343: public static final long getLong(byte[] buffer, int offset,
344: int precision, int scale)
345: throws java.io.UnsupportedEncodingException {
346: if (precision > 31) {
347: // throw an exception here if nibbles is greater than 31
348: throw new java.lang.IllegalArgumentException(msgutil
349: .getTextMessage(SQLState.DECIMAL_TOO_MANY_DIGITS));
350: }
351:
352: // The byte-length of a packed decimal with precision <code>p</code> is always <code>p/2 + 1</code>
353: int length = precision / 2 + 1;
354:
355: // check for sign.
356: int signum;
357: if ((buffer[offset + length - 1] & 0x0F) == 0x0D) {
358: signum = -1;
359: } else {
360: signum = 1;
361: }
362:
363: // compute the integer part only.
364: int leftOfDecimalPoint = length * 2 - 1 - scale;
365: long integer = 0;
366: if (leftOfDecimalPoint > 0) {
367: int i = 0;
368: for (; i < leftOfDecimalPoint / 2; i++) {
369: integer = integer * 10 + signum
370: * ((buffer[offset + i] & 0xF0) >>> 4); // high nybble.
371: integer = integer * 10 + signum
372: * (buffer[offset + i] & 0x0F); // low nybble.
373: }
374: if ((leftOfDecimalPoint % 2) == 1) {
375: // process high nybble of the last byte if necessary.
376: integer = integer * 10 + signum
377: * ((buffer[offset + i] & 0xF0) >>> 4);
378: }
379: }
380:
381: return integer;
382: }
383:
384: //--------------entry points for runtime representation-----------------------
385:
386: /**
387: * Write a Java <code>java.math.BigDecimal</code> to packed decimal bytes.
388: */
389: public static final int bigDecimalToPackedDecimalBytes(
390: byte[] buffer, int offset, java.math.BigDecimal b,
391: int declaredPrecision, int declaredScale)
392: throws SqlException {
393: // packed decimal may only be up to 31 digits.
394: if (declaredPrecision > 31) {
395: throw new SqlException(null, new ClientMessageId(
396: SQLState.DECIMAL_TOO_MANY_DIGITS));
397: }
398:
399: // get absolute unscaled value of the BigDecimal as a String.
400: String unscaledStr = b.unscaledValue().abs().toString();
401:
402: // get precision of the BigDecimal.
403: int bigPrecision = unscaledStr.length();
404:
405: if (bigPrecision > 31) {
406: throw new SqlException(null, new ClientMessageId(
407: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE),
408: "packed decimal", new SqlCode(-405));
409: }
410:
411: int bigScale = b.scale();
412: int bigWholeIntegerLength = bigPrecision - bigScale;
413: if ((bigWholeIntegerLength > 0) && (!unscaledStr.equals("0"))) {
414: // if whole integer part exists, check if overflow.
415: int declaredWholeIntegerLength = declaredPrecision
416: - declaredScale;
417: if (bigWholeIntegerLength > declaredWholeIntegerLength) {
418: throw new SqlException(null, new ClientMessageId(
419: SQLState.NUMERIC_OVERFLOW), b.toString(),
420: "packed decimal", new SqlCode(-413));
421: }
422: }
423:
424: // convert the unscaled value to a packed decimal bytes.
425:
426: // get unicode '0' value.
427: int zeroBase = '0';
428:
429: // start index in target packed decimal.
430: int packedIndex = declaredPrecision - 1;
431:
432: // start index in source big decimal.
433: int bigIndex;
434:
435: if (bigScale >= declaredScale) {
436: // If target scale is less than source scale,
437: // discard excessive fraction.
438:
439: // set start index in source big decimal to ignore excessive fraction.
440: bigIndex = bigPrecision - 1 - (bigScale - declaredScale);
441:
442: if (bigIndex < 0) {
443: // all digits are discarded, so only process the sign nybble.
444: buffer[offset + (packedIndex + 1) / 2] = (byte) ((b
445: .signum() >= 0) ? 12 : 13); // sign nybble
446: } else {
447: // process the last nybble together with the sign nybble.
448: buffer[offset + (packedIndex + 1) / 2] = (byte) (((unscaledStr
449: .charAt(bigIndex) - zeroBase) << 4) + // last nybble
450: ((b.signum() >= 0) ? 12 : 13)); // sign nybble
451: }
452: packedIndex -= 2;
453: bigIndex -= 2;
454: } else {
455: // If target scale is greater than source scale,
456: // pad the fraction with zero.
457:
458: // set start index in source big decimal to pad fraction with zero.
459: bigIndex = declaredScale - bigScale - 1;
460:
461: // process the sign nybble.
462: buffer[offset + (packedIndex + 1) / 2] = (byte) ((b
463: .signum() >= 0) ? 12 : 13); // sign nybble
464:
465: for (packedIndex -= 2, bigIndex -= 2; bigIndex >= 0; packedIndex -= 2, bigIndex -= 2) {
466: buffer[offset + (packedIndex + 1) / 2] = (byte) 0;
467: }
468:
469: if (bigIndex == -1) {
470: buffer[offset + (packedIndex + 1) / 2] = (byte) ((unscaledStr
471: .charAt(bigPrecision - 1) - zeroBase) << 4); // high nybble
472:
473: packedIndex -= 2;
474: bigIndex = bigPrecision - 3;
475: } else {
476: bigIndex = bigPrecision - 2;
477: }
478: }
479:
480: // process the rest.
481: for (; bigIndex >= 0; packedIndex -= 2, bigIndex -= 2) {
482: buffer[offset + (packedIndex + 1) / 2] = (byte) (((unscaledStr
483: .charAt(bigIndex) - zeroBase) << 4) + // high nybble
484: (unscaledStr.charAt(bigIndex + 1) - zeroBase)); // low nybble
485: }
486:
487: // process the first nybble when there is one left.
488: if (bigIndex == -1) {
489: buffer[offset + (packedIndex + 1) / 2] = (byte) (unscaledStr
490: .charAt(0) - zeroBase);
491:
492: packedIndex -= 2;
493: }
494:
495: // pad zero in front of the big decimal if necessary.
496: for (; packedIndex >= -1; packedIndex -= 2) {
497: buffer[offset + (packedIndex + 1) / 2] = (byte) 0;
498: }
499:
500: return declaredPrecision / 2 + 1;
501: }
502: }
|