All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.questdb.misc.Base64 Maven / Gradle / Ivy

There is a newer version: 3.3.3
Show newest version
/*******************************************************************************
 *    ___                  _   ____  ____
 *   / _ \ _   _  ___  ___| |_|  _ \| __ )
 *  | | | | | | |/ _ \/ __| __| | | |  _ \
 *  | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *   \__\_\\__,_|\___||___/\__|____/|____/
 *
 * Copyright (C) 2014-2016 Appsicle
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 *
 ******************************************************************************/

package com.questdb.misc;

public class Base64 {

    private static final byte PADDING = 127;
    private static final byte[] decodeMap = initDecodeMap();

    /**
     * @param text base64Binary data is likely to be long, and decoding requires
     *             each character to be accessed twice (once for counting length, another
     *             for decoding.)
     *             

* A benchmark showed that taking {@link String} is faster, presumably * because JIT can inline a lot of string access (with data of 1K chars, it was twice as fast) * @return decoded bytes */ public static byte[] parseBase64Binary(String text) { final int buflen = guessLength(text); final byte[] out = new byte[buflen]; int o = 0; final int len = text.length(); int i; final byte[] quadruplet = new byte[4]; int q = 0; // convert each quadruplet to three bytes. for (i = 0; i < len; i++) { char ch = text.charAt(i); byte v = decodeMap[ch]; if (v != -1) { quadruplet[q++] = v; } if (q == 4) { // quadruplet is now filled. out[o++] = (byte) ((quadruplet[0] << 2) | (quadruplet[1] >> 4)); if (quadruplet[2] != PADDING) { out[o++] = (byte) ((quadruplet[1] << 4) | (quadruplet[2] >> 2)); } if (quadruplet[3] != PADDING) { out[o++] = (byte) ((quadruplet[2] << 6) | (quadruplet[3])); } q = 0; } } if (buflen == o) // speculation worked out to be OK { return out; } // we overestimated, so need to create a new buffer byte[] nb = new byte[o]; System.arraycopy(out, 0, nb, 0, o); return nb; } /** * computes the length of binary data speculatively. *

*

* Our requirement is to create byte[] of the exact length to store the binary data. * If we do this in a straight-forward way, it takes two passes over the data. * Experiments show that this is a non-trivial overhead (35% or so is spent on * the first pass in calculating the length.) *

*

* So the approach here is that we compute the length speculatively, without looking * at the whole contents. The obtained speculative value is never less than the * actual length of the binary data, but it may be bigger. So if the speculation * goes wrong, we'll pay the cost of reallocation and buffer copying. *

*

* If the base64 text is tightly packed with no indentation nor illegal char * (like what most web services produce), then the speculation of this method * will be correct, so we get the performance benefit. */ private static int guessLength(String text) { final int len = text.length(); // compute the tail '=' chars int j = len - 1; for (; j > -1; j--) { byte code = decodeMap[text.charAt(j)]; if (code != PADDING) { if (code == -1) // most likely this base64 text is indented. go with the upper bound { return text.length() / 4 * 3; } break; } } j++; // text.charAt(j) is now at some base64 char, so +1 to make it the size int padSize = len - j; if (padSize > 2) // something is wrong with base64. be safe and go with the upper bound { return text.length() / 4 * 3; } // so far this base64 looks like it's unindented tightly packed base64. // take a chance and create an array with the expected size return text.length() / 4 * 3 - padSize; } private static byte[] initDecodeMap() { byte[] map = new byte[128]; int i; for (i = 0; i < 128; i++) { map[i] = -1; } for (i = 'A'; i <= 'Z'; i++) { map[i] = (byte) (i - 'A'); } for (i = 'a'; i <= 'z'; i++) { map[i] = (byte) (i - 'a' + 26); } for (i = '0'; i <= '9'; i++) { map[i] = (byte) (i - '0' + 52); } map['+'] = 62; map['/'] = 63; map['='] = PADDING; return map; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy