001: /**
002: * com.mckoi.database.DataCellSerialization 07 Dec 2000
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.database;
024:
025: import com.mckoi.database.global.*;
026: import com.mckoi.util.BigNumber;
027: import java.util.zip.*;
028: import java.util.Date;
029: import java.math.*;
030: import java.io.*;
031:
032: /**
033: * An object that manages the serialization and deserialization of objects
034: * to the database file system. This object maintains a buffer that stores
035: * intermediate serialization information as objects are written.
036: *
037: * @author Tobias Downer
038: */
039:
040: final class DataCellSerialization extends ByteArrayOutputStream
041: implements CellInput {
042:
043: /**
044: * A Deflater and Inflater used to compress and uncompress the size of data
045: * fields put into the store.
046: */
047: private Deflater deflater;
048: private Inflater inflater;
049: private byte[] compress_buf;
050: private int compress_length;
051:
052: /**
053: * If true, when writing out use the compressed form.
054: */
055: private boolean use_compressed;
056:
057: /**
058: * The type of object.
059: */
060: private short type;
061:
062: /**
063: * Set to true if null.
064: */
065: private boolean is_null;
066:
067: /**
068: * Constructor.
069: */
070: DataCellSerialization() {
071: super (1024);
072: }
073:
074: /**
075: * Returns the number of bytes to skip on the stream to go past the
076: * next serialization.
077: */
078: int skipSerialization(CellInput din) throws IOException {
079: int len = din.readInt();
080: return len - 4;
081: }
082:
083: /**
084: * Reads input from the given CellInput object.
085: */
086: Object readSerialization(CellInput din) throws IOException {
087:
088: count = 0;
089:
090: // Read the length first,
091: int len = din.readInt();
092: short s = din.readShort();
093: type = (short) (s & 0x0FFF);
094: is_null = (s & 0x02000) != 0;
095: use_compressed = (s & 0x04000) != 0;
096:
097: // If we are compressed...
098: if (use_compressed) {
099: // Uncompress it,
100: int uncompressed_len = din.readInt();
101: if (buf.length < uncompressed_len) {
102: buf = new byte[uncompressed_len];
103: }
104:
105: // Write data to the compressed buffer
106: compress_length = len - 4 - 2 - 4;
107: if (compress_buf == null
108: || compress_buf.length < compress_length) {
109: compress_buf = new byte[compress_length];
110: }
111: din.readFully(compress_buf, 0, compress_length);
112:
113: if (inflater == null) {
114: inflater = new Inflater();
115: }
116: inflater.reset();
117: inflater.setInput(compress_buf, 0, compress_length);
118: int inflate_count;
119: try {
120: inflate_count = inflater.inflate(buf, 0,
121: uncompressed_len);
122: } catch (DataFormatException e) {
123: throw new RuntimeException(e.getMessage());
124: }
125:
126: din = this ;
127:
128: }
129:
130: return readFromCellInput(din);
131: }
132:
133: /**
134: * Creates a BigNumber object used to store a numeric value in the database.
135: */
136: private BigNumber createBigNumber(byte[] buf, int scale, byte state) {
137: // Otherwise generate the number from the data given.
138: return BigNumber.fromData(buf, scale, state);
139: }
140:
141: /**
142: * Reads an object from the given CellInput. No type information is included
143: * with the returned object so it must be wrapped in a TObject. Returns
144: * null if the object stored was null.
145: */
146: private Object readFromCellInput(CellInput din) throws IOException {
147:
148: // If null byte is 1 then return null data cell.
149: if (is_null) {
150: return null;
151: } else {
152: // This type isn't actually serialized anymore, but we must understand
153: // how to deserialize it because of older database formats.
154: if (type == Types.DB_NUMERIC) {
155: int scale = din.readShort();
156: int num_len = din.readInt();
157: byte[] buf = new byte[num_len];
158: din.readFully(buf, 0, num_len);
159:
160: return createBigNumber(buf, scale, (byte) 0);
161: } else if (type == Types.DB_NUMERIC_EXTENDED) {
162: byte state = din.readByte();
163: int scale = din.readShort();
164: int num_len = din.readInt();
165: byte[] buf = new byte[num_len];
166: din.readFully(buf, 0, num_len);
167:
168: return createBigNumber(buf, scale, state);
169: } else if (type == Types.DB_STRING) {
170: int str_length = din.readInt();
171: // No length string is a static to save memory.
172: if (str_length == 0) {
173: return "";
174: }
175:
176: String dastr = din.readChars(str_length);
177: // NOTE: We intern the string to save memory.
178: return dastr.intern();
179: } else if (type == Types.DB_BOOLEAN) {
180: if (din.readByte() == 0) {
181: return Boolean.FALSE;
182: } else {
183: return Boolean.TRUE;
184: }
185: } else if (type == Types.DB_TIME) {
186: return new java.util.Date(din.readLong());
187: } else if (type == Types.DB_BLOB) {
188: int blob_length = din.readInt();
189: // Intern to save memory
190: if (blob_length == 0) {
191: return EMPTY_BYTE_LONG_OBJECT;
192: }
193:
194: byte[] buf = new byte[blob_length];
195: din.readFully(buf, 0, blob_length);
196:
197: return new ByteLongObject(buf);
198: } else if (type == Types.DB_OBJECT) {
199: int blob_length = din.readInt();
200:
201: byte[] buf = new byte[blob_length];
202: din.readFully(buf, 0, blob_length);
203:
204: return new ByteLongObject(buf);
205: } else {
206: throw new Error("Don't understand type: " + type);
207: }
208:
209: }
210:
211: }
212:
213: /**
214: * Writes the current serialized data buffer to the output stream.
215: */
216: void writeSerialization(DataOutputStream out) throws IOException {
217: int len = use_compressed ? (compress_length + 4) : count;
218: // size + (type | null | compressed)
219: len += 4 + 2;
220: out.writeInt(len);
221: short s = type;
222: if (is_null) {
223: s |= 0x02000;
224: }
225: if (use_compressed) {
226: s |= 0x04000;
227: }
228: out.writeShort(s);
229:
230: // Write out the data.
231: if (use_compressed) {
232: // If compressed, must write out uncompressed size first.
233: out.writeInt(count);
234: out.write(compress_buf, 0, compress_length);
235: } else {
236: out.write(buf, 0, count);
237: }
238:
239: // And that's it!
240: }
241:
242: /**
243: * Sets this up with a TObject to serialize.
244: */
245: void setToSerialize(TObject cell) throws IOException {
246:
247: is_null = false;
248: count = 0;
249: use_compressed = false;
250:
251: TType ttype = cell.getTType();
252: if (ttype instanceof TStringType) {
253: type = Types.DB_STRING;
254: } else if (ttype instanceof TNumericType) {
255: // NOTE: We set type to DB_NUMERIC_EXTENDED which includes support for
256: // NaN, negative infinity and positive infinity.
257: type = Types.DB_NUMERIC_EXTENDED;
258: } else if (ttype instanceof TBooleanType) {
259: type = Types.DB_BOOLEAN;
260: } else if (ttype instanceof TDateType) {
261: type = Types.DB_TIME;
262: } else if (ttype instanceof TBinaryType) {
263: type = Types.DB_BLOB;
264: } else if (ttype instanceof TJavaObjectType) {
265: type = Types.DB_OBJECT;
266: } else {
267: throw new Error("Couldn't handle type: " + ttype.getClass());
268: }
269:
270: if (cell.isNull()) {
271: is_null = true;
272: return;
273: }
274:
275: Object ob = cell.getObject();
276:
277: // Write the serialized form to the buffer,
278: writeToBuffer(cell);
279:
280: // Should we compress?
281:
282: // If it's a string, blob or serialized object, we may want to compress it,
283: TType type = cell.getTType();
284: if (type instanceof TStringType || type instanceof TBinaryType
285: || type instanceof TJavaObjectType) {
286: int length = count;
287: // Any strings > 150 are compressed
288: if (length > 150) {
289:
290: if (deflater == null) {
291: deflater = new Deflater();
292: }
293:
294: deflater.setInput(buf, 0, length);
295: deflater.finish();
296:
297: if (compress_buf == null
298: || compress_buf.length < length) {
299: compress_buf = new byte[length];
300: }
301: compress_length = deflater.deflate(compress_buf);
302: deflater.reset();
303:
304: if (compress_length < length) {
305: use_compressed = true;
306: }
307: }
308: }
309:
310: }
311:
312: /**
313: * Writes the TObject to the data buffer in this object.
314: */
315: private void writeToBuffer(TObject cell) throws IOException {
316:
317: Object ob = cell.getObject();
318:
319: if (ob instanceof BigNumber) {
320: BigNumber ddc = (BigNumber) ob;
321: byte[] buf = ddc.toByteArray();
322: writeByte(ddc.getState());
323: writeShort((short) ddc.getScale());
324: writeInt(buf.length);
325: write(buf);
326: } else if (ob instanceof String) {
327: String str = (String) ob;
328: writeInt(str.length());
329: writeChars(str);
330: } else if (ob instanceof Boolean) {
331: Boolean bool = (Boolean) ob;
332: writeByte((byte) (bool.booleanValue() ? 1 : 0));
333: } else if (ob instanceof java.util.Date) {
334: Date date = (Date) ob;
335: writeLong(date.getTime());
336: } else if (ob instanceof ByteLongObject) {
337: ByteLongObject blob = (ByteLongObject) ob;
338: writeInt(blob.length());
339: write(blob.getByteArray());
340: } else {
341: throw new Error("Don't know how to serialize class "
342: + ob.getClass());
343: }
344:
345: }
346:
347: public final void writeBoolean(boolean v) throws IOException {
348: write(v ? 1 : 0);
349: }
350:
351: public final void writeByte(int v) throws IOException {
352: write(v);
353: }
354:
355: public final void writeShort(int v) throws IOException {
356: write((v >>> 8) & 0xFF);
357: write((v >>> 0) & 0xFF);
358: }
359:
360: public final void writeChar(int v) throws IOException {
361: write((v >>> 8) & 0xFF);
362: write((v >>> 0) & 0xFF);
363: }
364:
365: public final void writeInt(int v) throws IOException {
366: write((v >>> 24) & 0xFF);
367: write((v >>> 16) & 0xFF);
368: write((v >>> 8) & 0xFF);
369: write((v >>> 0) & 0xFF);
370: }
371:
372: public final void writeLong(long v) throws IOException {
373: write((int) (v >>> 56) & 0xFF);
374: write((int) (v >>> 48) & 0xFF);
375: write((int) (v >>> 40) & 0xFF);
376: write((int) (v >>> 32) & 0xFF);
377: write((int) (v >>> 24) & 0xFF);
378: write((int) (v >>> 16) & 0xFF);
379: write((int) (v >>> 8) & 0xFF);
380: write((int) (v >>> 0) & 0xFF);
381: }
382:
383: public final void writeChars(String s) throws IOException {
384: int len = s.length();
385: for (int i = 0; i < len; ++i) {
386: int v = s.charAt(i);
387: write((v >>> 8) & 0xFF);
388: write((v >>> 0) & 0xFF);
389: }
390: }
391:
392: // ---------- Implemented from CellInput ----------
393:
394: public int read() throws IOException {
395: return buf[count++] & 0x0FF;
396: }
397:
398: public int read(byte b[], int off, int len) throws IOException {
399: if (len <= 0) {
400: return 0;
401: }
402: System.arraycopy(buf, count, b, off, len);
403: count += len;
404: return len;
405: }
406:
407: public long skip(long n) throws IOException {
408: if (n < 0) {
409: return 0;
410: }
411: count += n;
412: return n;
413: }
414:
415: public int available() throws IOException {
416: throw new Error("Not supported");
417: }
418:
419: public void mark(int readAheadLimit) throws IOException {
420: throw new Error("Not supported");
421: }
422:
423: // [ Function clash here but it should be okay ]
424: // public void reset() throws IOException {
425: // throw new Error("Not supported");
426: // }
427:
428: public void close() throws IOException {
429: throw new Error("Not supported");
430: }
431:
432: // ---------- Implemented from DataInput ----------
433:
434: public void readFully(byte[] b) throws IOException {
435: read(b, 0, b.length);
436: }
437:
438: public void readFully(byte b[], int off, int len)
439: throws IOException {
440: read(b, off, len);
441: }
442:
443: public int skipBytes(int n) throws IOException {
444: return (int) skip(n);
445: }
446:
447: public boolean readBoolean() throws IOException {
448: return (read() != 0);
449: }
450:
451: public byte readByte() throws IOException {
452: return (byte) read();
453: }
454:
455: public int readUnsignedByte() throws IOException {
456: return read();
457: }
458:
459: public short readShort() throws IOException {
460: int ch1 = read();
461: int ch2 = read();
462: return (short) ((ch1 << 8) + (ch2 << 0));
463: }
464:
465: public int readUnsignedShort() throws IOException {
466: int ch1 = read();
467: int ch2 = read();
468: return (ch1 << 8) + (ch2 << 0);
469: }
470:
471: public char readChar() throws IOException {
472: int ch1 = read();
473: int ch2 = read();
474: return (char) ((ch1 << 8) + (ch2 << 0));
475: }
476:
477: private char[] char_buffer;
478:
479: public String readChars(int length) throws IOException {
480: if (length <= 8192) {
481: if (char_buffer == null) {
482: char_buffer = new char[8192];
483: }
484: for (int i = 0; i < length; ++i) {
485: char_buffer[i] = readChar();
486: }
487: return new String(char_buffer, 0, length);
488: } else {
489: StringBuffer chrs = new StringBuffer(length);
490: for (int i = length; i > 0; --i) {
491: chrs.append(readChar());
492: }
493: return new String(chrs);
494: }
495: }
496:
497: public int readInt() throws IOException {
498: int ch1 = read();
499: int ch2 = read();
500: int ch3 = read();
501: int ch4 = read();
502: return (int) ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
503: }
504:
505: public long readLong() throws IOException {
506: return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
507: }
508:
509: public float readFloat() throws IOException {
510: return Float.intBitsToFloat(readInt());
511: }
512:
513: public double readDouble() throws IOException {
514: return Double.longBitsToDouble(readLong());
515: }
516:
517: public String readLine() throws IOException {
518: throw new Error("Not implemented.");
519: }
520:
521: public String readUTF() throws IOException {
522: throw new Error("Not implemented.");
523: }
524:
525: // ---------- Some statics -----------
526:
527: /**
528: * A 0 size ByteLongObject object.
529: */
530: private static final ByteLongObject EMPTY_BYTE_LONG_OBJECT = new ByteLongObject(
531: new byte[0]);
532:
533: }
|