/*
 * Copyright (c) 2003, PostgreSQL Global Development Group
 * See the LICENSE file in the project root for more information.
 */

package org.postgresql.util;

import java.sql.SQLException;

Converts to and from the postgresql bytea datatype used by the backend.
/** * Converts to and from the postgresql bytea datatype used by the backend. */
public class PGbytea { private static final int MAX_3_BUFF_SIZE = 2 * 1024 * 1024; /* * Converts a PG bytea raw value (i.e. the raw binary representation of the bytea data type) into * a java byte[] */ public static byte[] toBytes(byte[] s) throws SQLException { if (s == null) { return null; } // Starting with PG 9.0, a new hex format is supported // that starts with "\x". Figure out which format we're // dealing with here. // if (s.length < 2 || s[0] != '\\' || s[1] != 'x') { return toBytesOctalEscaped(s); } return toBytesHexEscaped(s); } private static byte[] toBytesHexEscaped(byte[] s) { byte[] output = new byte[(s.length - 2) / 2]; for (int i = 0; i < output.length; i++) { byte b1 = gethex(s[2 + i * 2]); byte b2 = gethex(s[2 + i * 2 + 1]); // squid:S3034 // Raw byte values should not be used in bitwise operations in combination with shifts output[i] = (byte) ((b1 << 4) | (b2 & 0xff)); } return output; } private static byte gethex(byte b) { // 0-9 == 48-57 if (b <= 57) { return (byte) (b - 48); } // a-f == 97-102 if (b >= 97) { return (byte) (b - 97 + 10); } // A-F == 65-70 return (byte) (b - 65 + 10); } private static byte[] toBytesOctalEscaped(byte[] s) { final int slength = s.length; byte[] buf = null; int correctSize = slength; if (slength > MAX_3_BUFF_SIZE) { // count backslash escapes, they will be either // backslashes or an octal escape \\ or \003 // for (int i = 0; i < slength; ++i) { byte current = s[i]; if (current == '\\') { byte next = s[++i]; if (next == '\\') { --correctSize; } else { correctSize -= 3; } } } buf = new byte[correctSize]; } else { buf = new byte[slength]; } int bufpos = 0; int thebyte; byte nextbyte; byte secondbyte; for (int i = 0; i < slength; i++) { nextbyte = s[i]; if (nextbyte == (byte) '\\') { secondbyte = s[++i]; if (secondbyte == (byte) '\\') { // escaped \ buf[bufpos++] = (byte) '\\'; } else { thebyte = (secondbyte - 48) * 64 + (s[++i] - 48) * 8 + (s[++i] - 48); if (thebyte > 127) { thebyte -= 256; } buf[bufpos++] = (byte) thebyte; } } else { buf[bufpos++] = nextbyte; } } if (bufpos == correctSize) { return buf; } byte[] result = new byte[bufpos]; System.arraycopy(buf, 0, result, 0, bufpos); return result; } /* * Converts a java byte[] into a PG bytea string (i.e. the text representation of the bytea data * type) */ public static String toPGString(byte[] buf) { if (buf == null) { return null; } StringBuilder stringBuilder = new StringBuilder(2 * buf.length); for (byte element : buf) { int elementAsInt = (int) element; if (elementAsInt < 0) { elementAsInt = 256 + elementAsInt; } // we escape the same non-printable characters as the backend // we must escape all 8bit characters otherwise when convering // from java unicode to the db character set we may end up with // question marks if the character set is SQL_ASCII if (elementAsInt < 040 || elementAsInt > 0176) { // escape charcter with the form \000, but need two \\ because of // the Java parser stringBuilder.append("\\"); stringBuilder.append((char) (((elementAsInt >> 6) & 0x3) + 48)); stringBuilder.append((char) (((elementAsInt >> 3) & 0x7) + 48)); stringBuilder.append((char) ((elementAsInt & 0x07) + 48)); } else if (element == (byte) '\\') { // escape the backslash character as \\, but need four \\\\ because // of the Java parser stringBuilder.append("\\\\"); } else { // other characters are left alone stringBuilder.append((char) element); } } return stringBuilder.toString(); } }