StringEncoderpublic class StringEncoder extends Object String encoder/decoder for SQLite.
This module was kindly donated by Eric van der Maarel of Nedap N.V.
This encoder was implemented based on an original idea from an anonymous
author in the source code of the SQLite distribution.
I feel obliged to provide a quote from the original C-source code:
"The author disclaims copyright to this source code. In place of
a legal notice, here is a blessing:
May you do good and not evil.
May you find forgiveness for yourself and forgive others.
May you share freely, never taking more than you give." |
Fields Summary |
---|
static final char[] | xdigits |
Methods Summary |
---|
private static byte[] | byteCopy(byte[] source, int offset, int count, byte[] target)Copies count elements from source, starting at element with
index offset, to the given target.
for (int i = offset, j = 0; i < offset + count; i++, j++) {
target[j] = source[i];
}
return target;
| public static byte[] | decode(java.lang.String s)Decodes the given string that is assumed to be a valid encoding
of a byte array. Typically the given string is generated by
this class' encode method.
char[] a = s.toCharArray();
if (a.length > 2 && a[0] == 'X" &&
a[1] == '\'" && a[a.length-1] == '\'") {
// SQLite3 BLOB syntax
byte[] result = new byte[(a.length-3)/2];
for (int i = 2, k = 0; i < a.length - 1; i += 2, k++) {
byte tmp = (byte) (a[i] - '0");
if (tmp > 15) {
tmp -= 0x20;
}
result[k] = (byte) (tmp << 4);
tmp = (byte) (a[i+1] - '0");
if (tmp > 15) {
tmp -= 0x20;
}
result[k] |= tmp;
}
return result;
}
// first element is the shift
byte[] result = new byte[a.length-1];
int i = 0;
int shift = s.charAt(i++);
int j = 0;
while (i < s.length()) {
int c;
if ((c = s.charAt(i++)) == 1) { // escape character found
if ((c = s.charAt(i++)) == 1) {
c = 0;
} else if (c == 2) {
c = 1;
} else if (c == 3) {
c = '\'";
} else {
throw new IllegalArgumentException(
"invalid string passed to decoder: " + j);
}
}
// do shift
result[j++] = (byte)((c + shift) & 0xff);
}
int outLen = j;
// provide array of correct length
if (result.length != outLen) {
result = byteCopy(result, 0, outLen, new byte[outLen]);
}
return result;
| public static java.lang.String | encode(byte[] a)Encodes the given byte array into a string that can be used by
the SQLite database. The database cannot handle null (0x00) and
the character '\'' (0x27). The encoding consists of escaping
these characters with a reserved character (0x01). The escaping
is applied after determining and applying a shift that minimizes
the number of escapes required.
With this encoding the data of original size n is increased to a
maximum of 1+(n*257)/254.
For sufficiently large n the overhead is thus less than 1.2%.
// check input
if (a == null || a.length == 0) {
// bogus shift, no data
return "x";
}
// determine count
int[] cnt = new int[256];
for (int i = 0 ; i < a.length; i++) {
cnt[a[i] & 0xff]++;
}
// determine shift for minimum number of escapes
int shift = 1;
int nEscapes = a.length;
for (int i = 1; i < 256; i++) {
if (i == '\'") {
continue;
}
int sum = cnt[i] + cnt[(i + 1) & 0xff] + cnt[(i + '\'") & 0xff];
if (sum < nEscapes) {
nEscapes = sum;
shift = i;
if (nEscapes == 0) {
// cannot become smaller
break;
}
}
}
// construct encoded output
int outLen = a.length + nEscapes + 1;
StringBuffer out = new StringBuffer(outLen);
out.append((char)shift);
for (int i = 0; i < a.length; i++) {
// apply shift
char c = (char)((a[i] - shift)&0xff);
// insert escapes
if (c == 0) { // forbidden
out.append((char)1);
out.append((char)1);
} else if (c == 1) { // escape character
out.append((char)1);
out.append((char)2);
} else if (c == '\'") { // forbidden
out.append((char)1);
out.append((char)3);
} else {
out.append(c);
}
}
return out.toString();
| public static java.lang.String | encodeX(byte[] a)Encodes the given byte array into SQLite3 blob notation, ie X'..'
// check input
if (a == null || a.length == 0) {
return "X''";
}
int outLen = a.length + 3;
StringBuffer out = new StringBuffer(outLen);
out.append('X");
out.append('\'");
for (int i = 0; i < a.length; i++) {
out.append(xdigits[a[i] >> 4]);
out.append(xdigits[a[i] & 0x0F]);
}
out.append('\'");
return out.toString();
|
|