但 OutMessage 继承了 BSONEncoder 类,而 BSONEncoder 的 writeInt 等方法实际上是委托给 OutputBuffer 类的实例执行的。
因此为了弄清楚 OutMessage 类的实例的行为,我们需要先了解 OutputBuffer 类的主要逻辑。
BasicOutputBuffer 和 PoolOutputBuffer 的 write 和 pipe 方法的实现
OutputBuffer 类是一个抽象类,有两个子类: BasicOutputBuffer 和 PoolOutputBuffer。
OutputBuffer 的作用是缓冲对将要写入到输出流中的数据进行缓冲,准备好后通过 pipe 方法将数据输出到输出流中。
子类主要实现了抽象方法 write 和 pipe:
public abstract void write(byte[] b, int off, int len); public abstract void write(int b); public abstract int pipe( OutputStream out );
BasicOutputBuffer 类的 write 方法的实现如下:
// 写入数据到缓冲区 public void write(byte[] b, int off, int len){ // 保证缓冲区空间足够 // 不够则开辟新的空间 _ensure( len ); // 复制数据到缓冲区 System.arraycopy( b , off , _buffer , _cur , len ); // 改变代表偏移量和缓冲区大小的数字 _cur += len; _size = Math.max( _cur , _size ); } // 保证缓冲区空间足够 // 不够则开辟新的空间 void _ensure( int more ){ // 计算需要的大小 final int need = _cur + more; // 目前的缓冲区大小足够。 // 不再开辟新的空间 if ( need < _buffer.length ) return; // 新的缓冲区大小是原来的两倍 int newSize = _buffer.length*2; // 如果仍然不够,开辟更大的空间 if ( newSize <= need ) newSize = need + 128; // 创建数组 byte[] n = new byte[newSize]; // 将缓冲区中数据复制到数组中 System.arraycopy( _buffer , 0 , n , 0 , _size ); // 以新的数组作为缓冲区 _buffer = n; } // 只写入一个字节 public void write(int b){ // 保证有一个字节的空间 _ensure(1); // 将 int 型数据的低 8 位保存到缓冲区 _buffer[_cur++] = (byte)(0xFF&b); // 修改表示缓冲区数据大小的数值 _size = Math.max( _cur , _size ); }
BasicOutputBuffer 类的 pipe 方法的实现如下:
public int pipe( OutputStream out ) throws IOException { // 将缓冲区中的数据写入输出流中 out.write( _buffer , 0 , _size ); // 返回缓冲区大小 return _size; }
PoolOutputBuffer 类的 write 方法实现如下:
public void write(byte[] b, int off, int len){ while ( len > 0 ){ // 获取一块当前缓冲区空间 byte[] bs = _cur(); // 计算本次写入大小 int space = Math.min( bs.length - _cur.y , len ); // 将数据复制缓冲区 System.arraycopy( b , off , bs , _cur.y , space ); // 修改偏移量等后续工作 _cur.inc( space ); len -= space; off += space; // 其他后续处理 // 如缓冲区满时,创建下一个缓冲区块等 _afterWrite(); } } // 只写入一个字节 public void write(int b){ // 获取缓冲区空间 byte[] bs = _cur(); // 将 int 型数值的低 8 为保存到缓冲区 bs[_cur.getAndInc()] = (byte)(b&0xFF); // 后续处理 _afterWrite(); }
PoolOutputBuffer 类的 pipe 方法实现如下:
public int pipe( OutputStream out ) throws IOException { if ( out == null ) throw new NullPointerException( "out is null" ); int total = 0; for ( int i=-1; i<_fromPool.size(); i++ ){ // 获取对象池中指定索引的数据 byte[] b = _get( i ); // 获取对象池中指定索引的数据的大小 int amt = _end.len( i ); // 将数据写入到输出流中 out.write( b , 0 , amt ); // 增加表示总数据大小的数值 total += amt; } return total; }
OutputBuffer 类的 write* 方法
基于子类实现的 write 方法,OutputBuffer 实现了一系列 write* 方法
// 写入 int 型 public void writeInt( int x ){ // 写入第 1 个字节(第 1 - 8 位) write( x >> 0 ); // 写入第 2 个字节(第 9 - 16 位) write( x >> 8 ); // 写入第 3 个字节(第 17 - 24 位) write( x >> 16 ); // 写入第 4 个字节(第 25 - 32 位) write( x >> 24 ); } // 按“大端”(Big End) 法 写入 int 型 public void writeIntBE( int x ){ // 写入第 4 个字节(第 25 - 32 位) write( x >> 24 ); // 写入第 3 个字节(第 17 - 24 位) write( x >> 16 ); // 写入第 2 个字节(第 9 - 16 位) write( x >> 8 ); // 写入第 1 个字节(第 1 - 8 位) write( x ); } // 指定位置写入 int 型数据 public void writeInt( int pos , int x ){ // 获取当前位置 final int save = getPosition(); // 设置当前位置 setPosition( pos ); // 写入 int 型数据 writeInt( x ); // 恢复当前位置 setPosition( save ); } // 写入 long 型数值 public void writeLong( long x ){ // 写入第 1 个字节(第 1 - 8 位) write( (byte)(0xFFL & ( x >> 0 ) ) ); // 写入第 2 个字节(第 9 - 16 位) write( (byte)(0xFFL & ( x >> 8 ) ) ); // 写入第 3 个字节(第 17 - 24 位) write( (byte)(0xFFL & ( x >> 16 ) ) ); // 写入第 4 个字节(第 25 - 32 位) write( (byte)(0xFFL & ( x >> 24 ) ) ); // 写入第 5 个字节(第 33 - 40 位) write( (byte)(0xFFL & ( x >> 32 ) ) ); // 写入第 6 个字节(第 41 - 48 位) write( (byte)(0xFFL & ( x >> 40 ) ) ); // 写入第 7 个字节(第 49 - 56 位) write( (byte)(0xFFL & ( x >> 48 ) ) ); // 写入第 8 个字节(第 57 - 64 位) write( (byte)(0xFFL & ( x >> 56 ) ) ); } // 写入 double 型数值 public void writeDouble( double x ){ // 将 double 型转为 long 型 (IEEE 754 表示法) 后写入 writeLong( Double.doubleToRawLongBits( x ) ); }