001package io.prometheus.metrics.expositionformats;
002
003import io.prometheus.metrics.model.snapshots.Labels;
004import java.io.IOException;
005import java.io.Writer;
006
007public class TextFormatUtil {
008
009  static void writeLong(Writer writer, long value) throws IOException {
010    writer.append(Long.toString(value));
011  }
012
013  static void writeDouble(Writer writer, double d) throws IOException {
014    if (d == Double.POSITIVE_INFINITY) {
015      writer.write("+Inf");
016    } else if (d == Double.NEGATIVE_INFINITY) {
017      writer.write("-Inf");
018    } else {
019      writer.write(Double.toString(d));
020      // FloatingDecimal.getBinaryToASCIIConverter(d).appendTo(writer);
021    }
022  }
023
024  static void writeTimestamp(Writer writer, long timestampMs) throws IOException {
025    writer.write(Long.toString(timestampMs / 1000L));
026    writer.write(".");
027    long ms = timestampMs % 1000;
028    if (ms < 100) {
029      writer.write("0");
030    }
031    if (ms < 10) {
032      writer.write("0");
033    }
034    writer.write(Long.toString(ms));
035  }
036
037  static void writeEscapedLabelValue(Writer writer, String s) throws IOException {
038    // optimize for the common case where no escaping is needed
039    int start = 0;
040    // #indexOf is a vectorized intrinsic
041    int backslashIndex = s.indexOf('\\', start);
042    int quoteIndex = s.indexOf('\"', start);
043    int newlineIndex = s.indexOf('\n', start);
044
045    int allEscapesIndex = backslashIndex & quoteIndex & newlineIndex;
046    while (allEscapesIndex != -1) {
047      int escapeStart = Integer.MAX_VALUE;
048      if (backslashIndex != -1) {
049        escapeStart = backslashIndex;
050      }
051      if (quoteIndex != -1) {
052        escapeStart = Math.min(escapeStart, quoteIndex);
053      }
054      if (newlineIndex != -1) {
055        escapeStart = Math.min(escapeStart, newlineIndex);
056      }
057
058      // bulk write up to the first character that needs to be escaped
059      if (escapeStart > start) {
060        writer.write(s, start, escapeStart - start);
061      }
062      char c = s.charAt(escapeStart);
063      start = escapeStart + 1;
064      switch (c) {
065        case '\\':
066          writer.write("\\\\");
067          backslashIndex = s.indexOf('\\', start);
068          break;
069        case '\"':
070          writer.write("\\\"");
071          quoteIndex = s.indexOf('\"', start);
072          break;
073        case '\n':
074          writer.write("\\n");
075          newlineIndex = s.indexOf('\n', start);
076          break;
077      }
078
079      allEscapesIndex = backslashIndex & quoteIndex & newlineIndex;
080    }
081    // up until the end nothing needs to be escaped anymore
082    int remaining = s.length() - start;
083    if (remaining > 0) {
084      writer.write(s, start, remaining);
085    }
086  }
087
088  static void writeLabels(
089      Writer writer, Labels labels, String additionalLabelName, double additionalLabelValue)
090      throws IOException {
091    writer.write('{');
092    for (int i = 0; i < labels.size(); i++) {
093      if (i > 0) {
094        writer.write(",");
095      }
096      writer.write(labels.getPrometheusName(i));
097      writer.write("=\"");
098      writeEscapedLabelValue(writer, labels.getValue(i));
099      writer.write("\"");
100    }
101    if (additionalLabelName != null) {
102      if (!labels.isEmpty()) {
103        writer.write(",");
104      }
105      writer.write(additionalLabelName);
106      writer.write("=\"");
107      writeDouble(writer, additionalLabelValue);
108      writer.write("\"");
109    }
110    writer.write('}');
111  }
112}