All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bson.NewBSONDecoder Maven / Gradle / Ivy

There is a newer version: 5.10
Show newest version
/**
 * Copyright (C) 2012 10gen Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bson;

import org.bson.io.Bits;
import org.bson.types.BSONDecimal;
import org.bson.types.ObjectId;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import static org.bson.BSON.*;

/**
 * A new implementation of the bson decoder.
 */
public class NewBSONDecoder implements BSONDecoder {

    //@Override
    public BSONObject readObject(final byte [] pData) {
        _length = pData.length;
        final BasicBSONCallback c = new BasicBSONCallback();
        _decode(pData, 0, c);
        return (BSONObject)c.get();
   }

    @Override
    public BSONObject readObject(byte[] b, int offset) {
        _length = Bits.readInt(b, offset);
        final BasicBSONCallback c = new BasicBSONCallback();
        _decode(b, offset, c);
        return (BSONObject)c.get();
    }

    //@Override
    public BSONObject readObject(final InputStream pIn) throws IOException {
        // Slurp in the data and convert to a byte array.
        _length = Bits.readInt(pIn);

        if (_data == null || _data.length < _length) {
            _data = new byte[_length];
        }

        (new DataInputStream(pIn)).readFully(_data, 4, (_length - 4));

        return readObject(_data);
    }

    @Override
    public int decode(byte[] b, BSONCallback callback) {
        _length = Bits.readInt(b);
        return _decode(b, 0, callback);
    }

    @Override
    public int decode(byte[] b, int offset, BSONCallback callback) {
        _length = Bits.readInt(b, offset);
        return _decode(b, offset, callback);
    }

    private int _decode(final byte[] pData, int offset, final BSONCallback pCallback) {
        _data = pData;
        _pos = offset + 4;
        _callback = pCallback;
        _decode();
        return _length;
    }

    //@Override
    public int decode(final InputStream pIn, final BSONCallback pCallback) throws IOException {
        _length = Bits.readInt(pIn);

        if (_data == null || _data.length < _length) {
            _data = new byte[_length];
        }

        (new DataInputStream(pIn)).readFully(_data, 4, (_length - 4));

        return _decode(_data, 0, pCallback);
    }

    private final void _decode() {
        _callback.objectStart();
        while (decodeElement());
        _callback.objectDone();
    }

    private final String readCstr() {
        int length = 0;
        final int offset = _pos;

        while (_data[_pos++] != 0) length++;

        try {
            return new String(_data, offset, length, DEFAULT_ENCODING);
        } catch (final UnsupportedEncodingException uee) {
            return new String(_data, offset, length);
        }
    }

    private final String readUtf8Str() {
        final int length = Bits.readInt(_data, _pos);
        _pos += 4;

        if (length <= 0 || length > MAX_STRING) throw new BSONException("String invalid - corruption");

        try {
            final String str = new String(_data, _pos, (length - 1), DEFAULT_ENCODING);
            _pos += length;
            return str;

        } catch (final UnsupportedEncodingException uee) {
            throw new BSONException("What is in the db", uee);
        }
    }

    private final Object _readBasicObject() {
        _pos += 4;

        final BSONCallback save = _callback;
        final BSONCallback _basic = _callback.createBSONCallback();
        _callback = _basic;
        _basic.reset();
        _basic.objectStart(false);

        while (decodeElement()) ;
        _callback = save;
        return _basic.get();
    }

    private final void _binary(final String pName) {

        final int totalLen = Bits.readInt(_data, _pos);
        _pos += 4;

        final byte bType = _data[_pos];
        _pos += 1;

        switch (bType) {
            case B_GENERAL: {
                final byte[] data = new byte[totalLen];

                System.arraycopy(_data, _pos, data, 0, totalLen);
                _pos += totalLen;

                _callback.gotBinary(pName, bType, data);
                return;
            }

            case B_BINARY: {
                final int len = Bits.readInt(_data, _pos);
                _pos += 4;

                if (len + 4 != totalLen)
                    throw new IllegalArgumentException("bad data size subtype 2 len: " + len + " totalLen: " + totalLen);

                final byte[] data = new byte[len];
                System.arraycopy(_data, _pos, data, 0, len);
                _pos += len;
                _callback.gotBinary(pName, bType, data);
                return;
            }

            case B_UUID: {
                if (totalLen != 16)
                    throw new IllegalArgumentException("bad data size subtype 3 len: " + totalLen + " != 16");

                final long part1 = Bits.readLong(_data, _pos);
                _pos += 8;

                final long part2 = Bits.readLong(_data, _pos);
                _pos += 8;

                _callback.gotUUID(pName, part1, part2);
                return;
            }
        }

        final byte[] data = new byte[totalLen];
        System.arraycopy(_data, _pos, data, 0, totalLen);
        _pos += totalLen;

        _callback.gotBinary(pName, bType, data);
    }

    private final boolean decodeElement() {

        final byte type = _data[_pos];
        _pos += 1;

        if (type == EOO) return false;

        final String name = readCstr();

        switch (type) {
            case NULL: {
                _callback.gotNull(name);
                return true;
            }

            case UNDEFINED: {
                _callback.gotUndefined(name);
                return true;
            }

            case BOOLEAN: {
                _callback.gotBoolean(name, (_data[_pos] > 0));
                _pos += 1;
                return true;
            }

            case NUMBER: {
                _callback.gotDouble(name, Double.longBitsToDouble(Bits.readLong(_data, _pos)));
                _pos += 8;
                return true;
            }

            case NUMBER_INT: {
                _callback.gotInt(name, Bits.readInt(_data, _pos));
                _pos += 4;
                return true;
            }

            case NUMBER_LONG: {
                _callback.gotLong(name, Bits.readLong(_data, _pos));
                _pos += 8;
                return true;
            }

            case SYMBOL: {
                _callback.gotSymbol(name, readUtf8Str());
                return true;
            }
            case STRING: {
                _callback.gotString(name, readUtf8Str());
                return true;
            }

            case OID: {
                // OID is stored as big endian

                final int p1 = Bits.readIntBE(_data, _pos);
                _pos += 4;

                final int p2 = Bits.readIntBE(_data, _pos);
                _pos += 4;

                final int p3 = Bits.readIntBE(_data, _pos);
                _pos += 4;

                _callback.gotObjectId(name, new ObjectId(p1, p2, p3));
                return true;
            }

            case REF: {
                _pos += 4;

                final String ns = readCstr();

                final int p1 = Bits.readInt(_data, _pos);
                _pos += 4;

                final int p2 = Bits.readInt(_data, _pos);
                _pos += 4;

                final int p3 = Bits.readInt(_data, _pos);
                _pos += 4;

                _callback.gotDBRef(name, ns, new ObjectId(p1, p2, p3));

                return true;
            }

            case DATE: {
                _callback.gotDate(name, Bits.readLong(_data, _pos));
                _pos += 8;
                return true;
            }


            case REGEX: {
                _callback.gotRegex(name, readCstr(), readCstr());
                return true;
            }

            case BINARY: {
                _binary(name);
                return true;
            }

            case CODE: {
                _callback.gotCode(name, readUtf8Str());
                return true;
            }

            case CODE_W_SCOPE: {
                _pos += 4;
                _callback.gotCodeWScope(name, readUtf8Str(), _readBasicObject());
                return true;
            }

            case ARRAY:
                _pos += 4;
                _callback.arrayStart(name);
                while (decodeElement()) ;
                _callback.arrayDone();
                return true;

            case OBJECT:
                _pos += 4;
                _callback.objectStart(name);
                while (decodeElement()) ;
                _callback.objectDone();
                return true;

            case TIMESTAMP:
                int i = Bits.readInt(_data, _pos);
                _pos += 4;

                int time = Bits.readInt(_data, _pos);
                _pos += 4;

                _callback.gotTimestamp(name, time, i);
                return true;

            case NUMBER_DECIMAL:
                int size = Bits.readInt(_data, _pos);
                _pos += 4;

                int typeMod = Bits.readInt(_data, _pos);
                _pos += 4;

                short signScale = Bits.readShort(_data, _pos);
                _pos += 2;

                short weight = Bits.readShort(_data, _pos);
                _pos += 2;

                int nDigits = (size - BSONDecimal.DECIMAL_HEADER_SIZE) / (Short.SIZE / Byte.SIZE);
                short[] digits = new short[nDigits];
                for (int index = 0; index < nDigits; index++) {
                    digits[index] = Bits.readShort(_data, _pos);
                    _pos += 2;
                }

                BSONDecimal decimal = new BSONDecimal(size, typeMod, signScale, weight, digits);
                _callback.gotDecimal(name, decimal);
                return true;

            case MINKEY:
                _callback.gotMinKey(name);
                return true;
            case MAXKEY:
                _callback.gotMaxKey(name);
                return true;

            default:
                throw new UnsupportedOperationException("BSONDecoder doesn't understand type : " + type + " name: " + name);
        }
    }

    private static final int MAX_STRING = (32 * 1024 * 1024);
    private static final String DEFAULT_ENCODING = "UTF-8";

    private byte[] _data;
    private int _length;
    private int _pos = 0;
    private BSONCallback _callback;
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy