// BSONDecoder.java package org.bson; import java.io.*; import static org.bson.BSON.*; import org.bson.io.*; import org.bson.types.*; public class BSONDecoder { public BSONObject readObject( byte[] b ){ try { return readObject( new ByteArrayInputStream( b ) ); } catch ( IOException ioe ){ throw new RuntimeException( "should be impossible" , ioe ); } } public BSONObject readObject( InputStream in ) throws IOException { BasicBSONCallback c = new BasicBSONCallback(); decode( in , c ); return (BSONObject)c.get(); } public int decode( byte[] b , BSONCallback callback ){ try { return decode( new Input( new ByteArrayInputStream(b) ) , callback ); } catch ( IOException ioe ){ throw new RuntimeException( "should be impossible" , ioe ); } } public int decode( InputStream in , BSONCallback callback ) throws IOException { return decode( new Input( in ) , callback ); } public int decode( Input in , BSONCallback callback ) throws IOException { if ( _in != null || _callback != null ) throw new IllegalStateException( "not ready" ); _in = in; _callback = callback; try { return decode(); } finally { _in = null; _callback = null; } } int decode() throws IOException { final int start = _in._read; final int len = _in.readInt(); _callback.objectStart(); while ( decodeElement() ); _callback.objectDone(); final int read = _in._read - start; if ( read != len ){ //throw new IllegalArgumentException( "bad data. lengths don't match " + read + " != " + len ); } return len; } boolean decodeElement() throws IOException { final byte type = _in.read(); if ( type == EOO ) return false; String name = _in.readCStr(); switch ( type ){ case NULL: _callback.gotNull( name ); break; case UNDEFINED: _callback.gotUndefined( name ); break; case BOOLEAN: _callback.gotBoolean( name , _in.read() > 0 ); break; case NUMBER: _callback.gotDouble( name , _in.readDouble() ); break; case NUMBER_INT: _callback.gotInt( name , _in.readInt() ); break; case NUMBER_LONG: _callback.gotLong( name , _in.readLong() ); break; case SYMBOL: // intentional fallthrough case CODE: case STRING: int size = _in.readInt(); if ( size < 0 || size > ( 3 * 1024 * 1024 ) ) throw new RuntimeException( "bad string size: " + size ); byte[] b = size < _random.length ? _random : new byte[size]; _in.fill( b , size ); try { String s = new String( b , 0 , size - 1 , "UTF-8" ); if ( type == SYMBOL ) _callback.gotSymbol( name , s ); else _callback.gotString( name , s ); } catch ( java.io.UnsupportedEncodingException uee ){ throw new RuntimeException( "impossible" , uee ); } break; case OID: _callback.gotObjectId( name , new ObjectId( _in.readInt() , _in.readInt() , _in.readInt() ) ); break; case REF: _in.readInt(); // length of ctring that follows String ns = _in.readCStr(); ObjectId theOID = new ObjectId( _in.readInt() , _in.readInt() , _in.readInt() ); _callback.gotDBRef( name , ns , theOID ); break; case DATE: _callback.gotDate( name , _in.readLong() ); break; case REGEX: _callback.gotRegex( name , _in.readCStr() , _in.readCStr() ); break; case BINARY: _binary( name ); break; /* case CODE: throw new UnsupportedOperationException( "can't handle CODE yet" );*/ case ARRAY: _in.readInt(); // total size - we don't care.... _callback.arrayStart( name ); while ( decodeElement() ); _callback.arrayDone(); break; case OBJECT: _in.readInt(); // total size - we don't care.... _callback.objectStart( name ); while ( decodeElement() ); _callback.objectDone(); break; case TIMESTAMP: int i = _in.readInt(); int time = _in.readInt(); _callback.gotTimestamp( name , time , i ); break; case MINKEY: _callback.gotMinKey( name ); break; case MAXKEY: _callback.gotMaxKey( name ); break; case CODE_W_SCOPE: _callback.objectStart( name ); _in.readInt(); // total size - we don't care.... String codeName = _in.readString(); _in.readInt(); // total size - we don't care.... _callback.objectStart( codeName ); while ( decodeElement() ); _callback.objectDone(); _callback.objectDone(); /* CodeWScope scope = new CodeWScope(codeName,scopeObj); BSONObject obj = (BSONObject)_callback.objectDone();*/ //_callback.got(name,obj ); break; default: throw new UnsupportedOperationException( "BSONDecoder doesn't understand type : " + type + " name: " + name ); } return true; } void _binary( String name ) throws IOException { final int totalLen = _in.readInt(); final byte bType = _in.read(); switch ( bType ){ case B_BINARY: final int len = _in.readInt(); if ( len + 4 != totalLen ) throw new IllegalArgumentException( "bad data size subtype 2 len: " + len + " totalLen: " + totalLen ); final byte[] data = new byte[len]; _in.fill( data ); _callback.gotBinaryArray( name , data ); return; } byte[] data = new byte[totalLen]; _in.fill( data ); _callback.gotBinary( name , bType , data ); } class Input { Input( InputStream in ){ _in = in; _read = 0; } int readInt() throws IOException { _read += 4; return Bits.readInt( _in ); } long readLong() throws IOException { _read += 8; return Bits.readLong( _in ); } double readDouble() throws IOException { return Double.longBitsToDouble( readLong() ); } byte read() throws IOException { _read++; return (byte)(_in.read() & 0xFF); } void fill( byte b[] ) throws IOException { fill( b , b.length ); } void fill( byte b[] , int len ) throws IOException { int off = 0; while ( len > 0 ){ int x = _in.read( b , off , len ); _read += x; off += x; len -= x; } } String readCStr() throws IOException { _stringBuffer.reset(); while ( true ){ byte b = read(); if ( b == 0 ) break; _stringBuffer.write( b ); } String out = null; try { out = _stringBuffer.asString( "UTF-8" ); } catch ( UnsupportedOperationException e ){ throw new RuntimeException( "impossible" , e ); } _stringBuffer.reset(); return out; } String readString() throws IOException { int lenght = readInt(); _stringBuffer.reset(); byte[] content = new byte[lenght -1]; this.fill(content); String out = null; try { out = new String(content, "UTF-8" ); } catch ( UnsupportedOperationException e ){ throw new RuntimeException( "impossible" , e ); } this.read();//skip 0x00 return out; } int _read; final InputStream _in; } private Input _in; private BSONCallback _callback; private byte[] _random = new byte[1024]; private PoolOutputBuffer _stringBuffer = new PoolOutputBuffer(); public static void main(String[] args){ long start = System.currentTimeMillis(); for(int i=0;i<100000;i++){ BasicOutputBuffer outBuffer = new BasicOutputBuffer(); BSONDecoder decoder = new BSONDecoder(); BSONEncoder encoder = new BSONEncoder(); encoder.set(outBuffer); BasicBSONObject object = new BasicBSONObject(); object.put("aa", "asdfa��ɣ�ķ����ķ�"); object.put("1", "asdfa��ɣ�ķ����ķ�"); object.put("2", "asdfa��ɣ�ķ����ķ�"); object.put("3", "asdfa��ɣ�ķ����ķ�"); object.put("4", "asdfa��ɣ�ķ����ķ�"); object.put("5", "asdfa��ɣ�ķ����ķ�"); object.put("6", "asdfa��ɣ�ķ����ķ�"); encoder.putObject(object); BasicBSONCallback callback = new BasicBSONCallback(); decoder.decode(encoder._buf.toByteArray(), callback); if(i==0){ System.out.println(callback.get()); } } System.out.println("time="+(System.currentTimeMillis()-start)); } }