1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 // This file is available under and governed by the GNU General Public
  27 // License version 2 only, as published by the Free Software Foundation.
  28 // However, the following notice accompanied the original version of this
  29 // file:
  30 //
  31 // Copyright 2010 the V8 project authors. All rights reserved.
  32 // Redistribution and use in source and binary forms, with or without
  33 // modification, are permitted provided that the following conditions are
  34 // met:
  35 //
  36 //     * Redistributions of source code must retain the above copyright
  37 //       notice, this list of conditions and the following disclaimer.
  38 //     * Redistributions in binary form must reproduce the above
  39 //       copyright notice, this list of conditions and the following
  40 //       disclaimer in the documentation and/or other materials provided
  41 //       with the distribution.
  42 //     * Neither the name of Google Inc. nor the names of its
  43 //       contributors may be used to endorse or promote products derived
  44 //       from this software without specific prior written permission.
  45 //
  46 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  47 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  48 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  49 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  50 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  51 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  52 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  53 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  54 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  55 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  56 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  57 
  58 package jdk.nashorn.internal.runtime.doubleconv;
  59 
  60 /**
  61  * A buffer for generating string representations of doubles.
  62  */
  63 public class DtoaBuffer {
  64 
  65     // The character buffer
  66     final char[] chars;
  67 
  68     // The number of characters in the buffer
  69     int length = 0;
  70 
  71     // The position of the decimal point
  72     int decimalPoint = 0;
  73 
  74     // Is this a negative number?
  75     boolean isNegative = false;
  76 
  77     /**
  78      * Maximal length of numbers converted by FastDtoa
  79      */
  80     public static final int kFastDtoaMaximalLength = FastDtoa.kFastDtoaMaximalLength;
  81 
  82     /**
  83      * Create a buffer with the given capacity.
  84      * @param capacity the capacity of the buffer.
  85      */
  86     public DtoaBuffer(final int capacity) {
  87         chars = new char[capacity];
  88     }
  89 
  90     /**
  91      * Append a character to the buffer, increasing its length.
  92      * @param c character
  93      */
  94     void append(final char c) {
  95         chars[length++] = c;
  96     }
  97 
  98     /**
  99      * Clear the buffer contents and set its length to {@code 0}.
 100      */
 101     public void reset() {
 102         length = 0;
 103         decimalPoint = 0;
 104     }
 105 
 106     /**
 107      * Get the raw digits of this buffer as string.
 108      * @return the raw buffer contents
 109      */
 110     public String getRawDigits() {
 111         return new String(chars, 0, length);
 112     }
 113 
 114     /**
 115      * Get the position of the decimal point.
 116      * @return the decimal point position
 117      */
 118     public int getDecimalPoint() {
 119         return decimalPoint;
 120     }
 121 
 122     /**
 123      * Returns the number of characters in the buffer.
 124      * @return buffer length
 125      */
 126     public int getLength() {
 127         return length;
 128     }
 129 
 130     /**
 131      * Returns the formatted buffer content as string, using the specified conversion mode
 132      * and padding.
 133      *
 134      * @param mode conversion mode
 135      * @param digitsAfterPoint number of digits after point
 136      * @return formatted string
 137      */
 138     public String format(final DtoaMode mode, final int digitsAfterPoint) {
 139         final StringBuilder buffer = new StringBuilder();
 140         if (isNegative) {
 141             buffer.append('-');
 142         }
 143 
 144         // check for minus sign
 145         switch (mode) {
 146             case SHORTEST:
 147                 if (decimalPoint < -5 || decimalPoint > 21) {
 148                     toExponentialFormat(buffer);
 149                 } else {
 150                     toFixedFormat(buffer, digitsAfterPoint);
 151                 }
 152                 break;
 153             case FIXED:
 154                 toFixedFormat(buffer, digitsAfterPoint);
 155                 break;
 156             case PRECISION:
 157                 if (decimalPoint < -5 || decimalPoint > length) {
 158                     toExponentialFormat(buffer);
 159                 } else {
 160                     toFixedFormat(buffer, digitsAfterPoint);
 161                 }
 162                 break;
 163         }
 164 
 165         return buffer.toString();
 166     }
 167 
 168     private void toFixedFormat(final StringBuilder buffer, final int digitsAfterPoint) {
 169         if (decimalPoint <= 0) {
 170             // < 1,
 171             buffer.append('0');
 172             if (length > 0) {
 173                 buffer.append('.');
 174                 final int padding = -decimalPoint;
 175                 for (int i = 0; i < padding; i++) {
 176                     buffer.append('0');
 177                 }
 178                 buffer.append(chars, 0, length);
 179             } else {
 180                 decimalPoint = 1;
 181             }
 182         } else if (decimalPoint >= length) {
 183             // large integer, add trailing zeroes
 184             buffer.append(chars, 0, length);
 185             for (int i = length; i < decimalPoint; i++) {
 186                 buffer.append('0');
 187             }
 188         } else if (decimalPoint < length) {
 189             // >= 1, split decimals and insert decimalPoint
 190             buffer.append(chars, 0, decimalPoint);
 191             buffer.append('.');
 192             buffer.append(chars, decimalPoint, length - decimalPoint);
 193         }
 194 
 195         // Create trailing zeros if requested
 196         if (digitsAfterPoint > 0) {
 197             if (decimalPoint >= length) {
 198                 buffer.append('.');
 199             }
 200             for (int i = Math.max(0, length - decimalPoint); i < digitsAfterPoint; i++) {
 201                 buffer.append('0');
 202             }
 203         }
 204     }
 205 
 206     private void toExponentialFormat(final StringBuilder buffer) {
 207         buffer.append(chars[0]);
 208         if (length > 1) {
 209             // insert decimal decimalPoint if more than one digit was produced
 210             buffer.append('.');
 211             buffer.append(chars, 1, length - 1);
 212         }
 213         buffer.append('e');
 214         final int exponent = decimalPoint - 1;
 215         if (exponent > 0) {
 216             buffer.append('+');
 217         }
 218         buffer.append(exponent);
 219     }
 220 
 221     @Override
 222     public String toString() {
 223         return "[chars:" + new String(chars, 0, length) + ", decimalPoint:" + decimalPoint + "]";
 224     }
 225 }